PluginProbe
Channel.io / trunk
Channel.io vtrunk
channel-io / channel_plugin_script.js

channel_plugin_script.js in Channel.io trunk, at channel_plugin_script.js

164 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ;
2 function ch_parseInt(data) {
3 if (!data) { return undefined; }
4 try {
5 return parseInt(data);
6 } catch (e) {
7 return undefined;
8 }
9 }
10
11 (function() {
12 var w = window;
13 if (w.ChannelIO) {
14 return (window.console.error || window.console.log || function(){})('ChannelIO script included twice.');
15 }
16 var ch = function() {
17 ch.c(arguments);
18 };
19 ch.q = [];
20 ch.c = function(args) {
21 ch.q.push(args);
22 };
23 w.ChannelIO = ch;
24
25 function loadSdk() {
26 if (w.ChannelIOInitialized) {
27 return;
28 }
29 w.ChannelIOInitialized = true;
30 var s = document.createElement('script');
31 s.type = 'text/javascript';
32 s.async = true;
33 s.src = 'https://cdn.channel.io/plugin/ch-plugin-web.js';
34 s.charset = 'UTF-8';
35 var x = document.getElementsByTagName('script')[0];
36 x.parentNode.insertBefore(s, x);
37 }
38
39 function buildBaseSettings() {
40 return {
41 "pluginKey": channel_io_options.channel_io_plugin_key,
42 "hideChannelButtonOnBoot": channel_io_options.channel_io_hide_default_launcher === 'on',
43 "customLauncherSelector": channel_io_options.channel_io_custom_launcher_selector,
44 "mobileMessengerMode": channel_io_options.channel_io_mobile_messenger_mode === 'on' ? 'iframe' : undefined,
45 "zIndex": ch_parseInt(channel_io_options.channel_io_z_index),
46 "scriptProvider": "channel",
47 "scriptPlatform": "wordpress",
48 "scriptVersion": "2.0.0"
49 };
50 }
51
52 function bootAnonymous() {
53 ChannelIO('boot', buildBaseSettings());
54 }
55
56 function bootWithProfile(me) {
57 var settings = buildBaseSettings();
58 if (me && me.login && me.memberId) {
59 settings.memberId = me.memberId;
60 if (me.memberHash) {
61 settings.memberHash = me.memberHash;
62 }
63 settings.profile = me.profile || {};
64 }
65 ChannelIO('boot', settings);
66 }
67
68 // 페이지 HTML 에 회원 정보를 박지 않고, AJAX profile endpoint 에서 받아 boot 한다.
69 // 이렇게 하면 어떤 페이지 캐시 (LiteSpeed Cache, WP Rocket 등) 환경에서도
70 // 한 회원의 정보가 다른 방문자에게 �
71 �출되는 누출이 발생하지 않는다.
72 //
73 // fetch / XHR 의 정상 응답 / 에러 외에 응답이 stalled 되어 promise 가 영영
74 // settle 안 되는 케이스(slow 3G, 끊긴 네트워크, 서버 hang 등)도 막아야 한다.
75 // settle 안 되면 start() 의 started 가드 때문에 재시도도 없이 ChannelIO('boot')
76 // 자체가 호출되지 않아 위젯이 영원히 안 뜬다. settled 플래그 + 5초 타임아웃으로
77 // 어떤 경로로든 정확히 한 번 익�
78 부트 fallback 보장되도록 한다.
79 function fetchProfileAndBoot() {
80 if (!channel_io_options || !channel_io_options.profile_url) {
81 bootAnonymous();
82 return;
83 }
84
85 var settled = false;
86 var timeoutId = window.setTimeout(function() {
87 if (settled) {
88 return;
89 }
90 settled = true;
91 bootAnonymous();
92 }, 5000);
93
94 function finish(callback) {
95 if (settled) {
96 return;
97 }
98 settled = true;
99 window.clearTimeout(timeoutId);
100 callback();
101 }
102
103 if (typeof window.fetch === 'function') {
104 window.fetch(channel_io_options.profile_url, {
105 credentials: 'same-origin'
106 })
107 .then(function(res) { return res.ok ? res.json() : null; })
108 .then(function(me) {
109 finish(function() { bootWithProfile(me); });
110 })
111 .catch(function() { finish(bootAnonymous); });
112 return;
113 }
114
115 // fetch 미지원 브라우저용 fallback
116 try {
117 var xhr = new XMLHttpRequest();
118 xhr.open('GET', channel_io_options.profile_url, true);
119 xhr.withCredentials = true;
120 xhr.onreadystatechange = function() {
121 if (xhr.readyState !== 4) return;
122 if (xhr.status >= 200 && xhr.status < 300) {
123 try {
124 var me = JSON.parse(xhr.responseText);
125 finish(function() { bootWithProfile(me); });
126 } catch (e) {
127 finish(bootAnonymous);
128 }
129 } else {
130 finish(bootAnonymous);
131 }
132 };
133 xhr.onerror = function() { finish(bootAnonymous); };
134 xhr.send();
135 } catch (e) {
136 finish(bootAnonymous);
137 }
138 }
139
140 // start() 는 한 번만 실행되어야 한다. document.readyState 검사 + DOMContentLoaded
141 // + load 세 경로 중 둘 이상이 동작할 수 있어 (예: load 가 DOMContentLoaded 뒤에
142 // 한 번 더 발생) 가드 없이 두면 fetchProfileAndBoot() 가 두 번 호출되어 /profile
143 // 호출과 ChannelIO('boot', ...) 가 중복 실행된다. loadSdk() 는 자체 가드(
144 // ChannelIOInitialized) 가 있지만 boot 호출 자체는 막지 못하므로 여기서 차단.
145 var started = false;
146 function start() {
147 if (started) {
148 return;
149 }
150 started = true;
151 loadSdk();
152 fetchProfileAndBoot();
153 }
154
155 if (document.readyState === 'complete') {
156 start();
157 } else if (window.attachEvent) {
158 window.attachEvent('onload', start);
159 } else {
160 window.addEventListener('DOMContentLoaded', start, false);
161 window.addEventListener('load', start, false);
162 }
163 })();
164