1/** 2 * Fortune Cookie ã°ãã¼ãã«åå空é 3 * 4 * ã°ãã¼ãã«å¤æ°ã®æ±æãé²ãããããã¹ã¦ã®ã¢ããªã±ã¼ã·ã§ã³åºæã® 5 * ãã¼ã¿ã¨ã¡ã½ããããã®åå空éå ã«é ç½®ãã¾ãã 6 */ 7 8(function() { 9 'use strict'; 10 11 // åå空éã®åæåï¼æ¢ã«åå¨ããå ´åã¯ä¸æ¸ãããªãï¼ 12 window.FortuneCookie = window.FortuneCookie || {}; 13 14 // GTMé¢é£ 15 window.FortuneCookie.gtm = { 16 debug: false, 17 eventHistory: [], 18 19 /** 20 * GTMã¤ãã³ããçºç« 21 * @param {string} eventName - ã¤ãã³ãå 22 * @param {Object} eventData - ã¤ãã³ããã¼ã¿ 23 */ 24 fireEvent: function(eventName, eventData) { 25 // iframe å (ä¾: /payment_method ã purchase-popup-iframe çµç±ã§èªã¿è¾¼ã¾ããå ´å) 㯠26 // GTM ã親ãã¬ã¼ã ã«ããå± ãªãã®ã§è¦ªã¸ postMessage ã§ä¸ç¶ããã 27 // 親å´ã®åä¿¡ handler 㯠resources/views/components/purchase-popup.blade.php:191-194 ã 28 // 'purchase-popup:ga4-events' ãåãåã window.FortuneCookie.gtm.fireEvents(...) ãå¼ã¶ã 29 if (window.parent !== window) { 30 try { 31 // event_callback çã®é¢æ°åç §ã¯ postMessage ã® structured clone ãè¶ããããªãã®ã§ 32 // äºåã«è½ã¨ãã¦ãã (å¼ã³åºãå´ã® fireEventThenRun ã iframe åå²ã§å¯¾å¿) 33 var dataForRelay = Object.assign({}, eventData || {}); 34 delete dataForRelay.event_callback; 35 delete dataForRelay.event_timeout; 36 // page_location / page_path / page_title ã iframe èªèº«ã® URL ã§æç¤ºã 37 // 親ãã¬ã¼ã ã¸ä¸ç¶ãã GA4 ã¤ãã³ãã¯ãæç¤ºããªãã¨è¦ªã® URL (TOP ç) ã 38 // èªååéå¤ã¨ãã¦éä¿¡ããã¦ãã¾ããGA4 ä¸ã§ã/payment_method ã® button click ã 39 // TOP ã§çºç«ãããããã«è¦ãã (Notion ⢠ã®è¦ãæ¹ã®åå )ãevent params ã®æç¤ºå¤ã¯ 40 // GA4 ã® auto-collect ããåªå ããããããiframe ã® URL ã§ä¸æ¸ãããã 41 // å¼ã³åºãå´ã§æ¢ã« page_location çãæå®ããã¦ããã°ãã¡ããåªå (äºé䏿¸ãããªã)ã 42 if (dataForRelay.page_location === undefined) dataForRelay.page_location = window.location.href; 43 if (dataForRelay.page_path === undefined) dataForRelay.page_path = window.location.pathname; 44 if (dataForRelay.page_title === undefined) dataForRelay.page_title = document.title; 45 window.parent.postMessage( 46 { type: 'purchase-popup:ga4-events', events: [{ event: eventName, data: dataForRelay }] }, 47 window.location.origin 48 ); 49 } catch (e) { 50 if (window.console) console.warn('[FortuneCookie.gtm.fireEvent] parent postMessage failed:', e); 51 } 52 return; 53 } 54 55 window.dataLayer = window.dataLayer || []; 56 57 // GA4 äºç´èªè¡çªãã¼ï¼source çï¼ã click_ ãã¬ãã£ãã¯ã¹ä»ãã¸ä»æ¿ã 58 // å¼ã³åºãå´ã¯å¾æ¥ã©ãã { source: ... } ãæ¸¡ãã¦ãããããã§å¸åããã 59 // spec: docs/features/ga4-source-rename/spec.md 60 var normalizedEventData = this._renameConflictingKeys(eventData || {}); 61 62 // dataLayer ãã ab_experiments ãèªã¿åºãï¼GA4-3: å ¨ã¤ãã³ãã« exp_* ãä»ä¸ï¼ 63 var abExperiments = {}; 64 for (var i = window.dataLayer.length - 1; i >= 0; i--) { 65 if (window.dataLayer[i] && window.dataLayer[i].ab_experiments) { 66 abExperiments = window.dataLayer[i].ab_experiments; 67 break; 68 } 69 } 70 71 // ab_experiments ã®åãã¼ï¼exp_xxx å½¢å¼ï¼ã eventData ã«ãã¼ã¸ 72 // GA4-2: ab_experiments ã空ãªãã¸ã§ã¯ã {}
72 ã®å ´åãexp_* ãã¼ã¯è¿½å ãããªã 73 const timestamp = new Date().toISOString(); 74 const fullEventData = Object.assign( 75 { event: eventName }, 76 abExperiments, 77 normalizedEventData 78 ); 79 80 // dataLayerã«push 81 window.dataLayer.push(fullEventData); 82 83 // çºç«å±¥æ´ã«è¨é²ï¼ä»æ¿å¾ã®å¤ãä¿åããå®éã« GA4 ã¸éã£ãå 容ã¨ä¸è´ãããï¼ 84 this.eventHistory.push({ 85 timestamp: timestamp, 86 event: eventName, 87 data: normalizedEventData 88 }); 89 90 // ãããã°ã¢ã¼ãæã¯ã³ã³ã½ã¼ã«åºå 91 if (this.debug) { 92 console.group('GA4 Event: ' + eventName); 93 console.log('Timestamp:', timestamp); 94 console.log('Original Event Data:', eventData); 95 console.log('Normalized Event Data:', normalizedEventData); 96 console.log('User ID:', window.userId || 'not set'); 97 console.log('Full dataLayer Object:', fullEventData); 98 console.groupEnd(); 99 } 100 }, 101 102 /** 103 * GTMã¤ãã³ããã¾ã¨ãã¦çºç«ï¼ãµã¼ãã¼å´ã»ãã·ã§ã³ãã¥ã¼ã»postMessage ä¸ç¶ç¨ï¼ 104 * @param {Array<{event: string, data: Object}>} events 105 */ 106 fireEvents: function(events) { 107 // iframe å ãªã 1 åã® postMessage ã§ã¾ã¨ãã¦è¦ªã¸ä¸ç¶ãã (颿°åç §ã¯é¤å») 108 if (window.parent !== window) { 109 try { 110 // fireEvent å´ã¨åãçç±ã§ page_location / page_path / page_title ã iframe URL ã§æç¤ºããã 111 var pageLocation = window.location.href; 112 var pagePath = window.location.pathname; 113 var pageTitle = document.title; 114 var relayEvents = (events || []).map(function(e) { 115 var data = Object.assign({}, e.data || {}); 116 delete data.event_callback; 117 delete data.event_timeout; 118 if (data.page_location === undefined) data.page_location = pageLocation; 119 if (data.page_path === undefined) data.page_path = pagePath; 120 if (data.page_title === undefined) data.page_title = pageTitle; 121 return { event: e.event, data: data }; 122 }); 123 window.parent.postMessage( 124 { type: 'purchase-popup:ga4-events', events: relayEvents }, 125 window.location.origin 126 ); 127 } catch (e) { 128 if (window.console) console.warn('[FortuneCookie.gtm.fireEvents] parent postMessage failed:', e); 129 } 130 return; 131 } 132 var self = this; 133 (events || []).forEach(function(e) { 134 self.fireEvent(e.event, e.data); 135 }); 136 }, 137 138 /** 139 * GTM ã¤ãã³ããçºç«ããéä¿¡å®äºï¼GA4 ã® event_callbackï¼ãå¾ ã£ã¦ callbackFn ãå®è¡ããã 140 * ã¿ã¤ã ã¢ã¦ãï¼1000msï¼ã§ãé·ç§»ããããã¯ããªããcallback ã¯å ç 1 çºã¬ã¼ãã§ 141 * äºéå®è¡ããªãï¼event_callback ã¨ã¿ã¤ã ã¢ã¦ãã® race 対çï¼ã 142 * 143 * spec: docs/features/add-payment-info-event/spec.md 144 * 145 * @param {string} eventName - ã¤ãã³ãå 146 * @param {Object} eventData - ã¤ãã³ããã¼ã¿ 147 * @param {Function} callbackFn - éä¿¡å®äº / ã¿ã¤ã ã¢ã¦ãã®ããããå çã§ 1 åã ãå®è¡ããã 148 */ 149 fireEventThenRun: function(eventName, eventData, callbackFn) { 150 // iframe å ã§ã¯ fireEvent ã親㸠postMessage ä¸ç¶ãããã event_callback (颿°åç §) 151 // ã使ããªã (structured clone ãè¶ããããªã)ãpostMessage éä¿¡ç´å¾ã« callback ã 152 // å®è¡ãã fire-and-forget æ¹å¼ã«ãã (親 â GTM å°éã¾ã§ã®ã©ã°ã¯é£²ãã» 153 // 決æ¸å°ç·ã 1.1s å¾ ãããªã)ã 154 if (window.parent !== window) { 155 this.fireEvent(eventName, eventData); 156 try { 157 callbackFn && callbackFn(); 158 } catch (e) { 159 if (window.console) console.warn('[FortuneCookie.gtm.fireEventThenRun] callback threw (iframe path):', e); 160 } 161 return; 162 } 163 var dispatched = false;
164 var run = function() { 165 if (dispatched) return; 166 dispatched = true; 167 try { 168 callbackFn && callbackFn(); 169 } catch (e) { 170 if (window.console) console.warn('[FortuneCookie.gtm.fireEventThenRun] callback threw:', e); 171 } 172 }; 173 // event_callback / event_timeout ã eventData ã«è¼ããã 174 // GTM ã® GA4 ã¿ã°ã§ã¯ gtag.js ã® event_callback / event_timeout ããã®ã¾ã¾ééããã 175 var params = Object.assign({}, eventData || {}, { 176 event_callback: run, 177 event_timeout: 1000, 178 }); 179 this.fireEvent(eventName, params); 180 // event_callback ãéå¯¾å¿ / GTM å´ã§çºç«ããªãç°å¢åãã®ä¿éºã 181 // 1000ms + å°ãã®ä½è£ãæã£ã¦ã¿ã¤ã ã¢ã¦ããdispatched ãã©ã°ã§äºéå®è¡ãé²ãã 182 setTimeout(run, 1100); 183 }, 184 185 enableDebug: function() { 186 this.debug = true; 187 console.log('GA4 Debug mode enabled'); 188 }, 189 190 disableDebug: function() { 191 this.debug = false; 192 console.log('GA4 Debug mode disabled'); 193 }, 194 195 showHistory: function(limit) { 196 var history = limit ? this.eventHistory.slice(-limit) : this.eventHistory; 197 198 if (history.length === 0) { 199 console.log('No events fired yet'); 200 return; 201 } 202 203 console.table(history.map(function(h) { 204 return { 205 Timestamp: h.timestamp, 206 Event: h.event, 207 Data: JSON.stringify(h.data) 208 }; 209 })); 210 }, 211 212 clearHistory: function() { 213 this.eventHistory = []; 214 console.log('Event history cleared'); 215 }, 216 217 /** 218 * GA4 äºç´èªè¡çªãã¼ï¼source / medium / campaign / term / contentï¼ã 219 * click_ ãã¬ãã£ãã¯ã¹ä»ãã¸ä»æ¿ããã 220 * 221 * - ãããã¬ãã«ã®ãã¼ã®ã¿å¯¾è±¡ï¼items[] ãªã©ãã¹ãæ§é ã¯è§¦ããªãï¼ 222 * - æ°ãã¼ãæ¢ã«ããå ´åã¯æ°ãã¼åªå ã§æ§ãã¼ãæ¨ã¦ã 223 * - åªçï¼æ°ãã¼ã ãã®å ¥åã«å¯¾ãã¦ã¯ no-opï¼ 224 * 225 * spec: docs/features/ga4-source-rename/spec.md 226 * 227 * @param {Object} data 228 * @returns {Object} 仿¿å¾ã®æ°ãããªãã¸ã§ã¯ãï¼å ¥åã¯å¤æ´ããªãï¼ 229 */ 230 _renameConflictingKeys: function(data) { 231 var renameMap = { 232 source: 'click_source', 233 medium: 'click_medium', 234 campaign: 'click_campaign', 235 term: 'click_term', 236 content: 'click_content' 237 }; 238 var out = Object.assign({}, data || {}); 239 for (var oldKey in renameMap) { 240 if (!Object.prototype.hasOwnProperty.call(renameMap, oldKey)) continue; 241 if (!Object.prototype.hasOwnProperty.call(out, oldKey)) continue; 242 var newKey = renameMap[oldKey]; 243 if (!Object.prototype.hasOwnProperty.call(out, newKey)) { 244 out[newKey] = out[oldKey]; 245 } 246 delete out[oldKey]; 247 } 248 return out; 249 } 250 }; 251 252})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.