create-fesd-app 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fesd-app",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -77,10 +77,6 @@ $typeFull-padding_rwd: 30px 20px
77
77
  $typeFull-content-padding: 85px 75px 100px
78
78
  $typeFull-content-padding_rwd: 20px
79
79
 
80
- // swiper pagination
81
- $pagination-TB-gap: 20px
82
- $pagination-TB-gap_rwd: 10px
83
-
84
80
  // 表格與其他物件上下間的距離
85
81
  $table-TB-gap: 20px
86
82
  $table-TB-gap_rwd: 20px
@@ -341,28 +337,31 @@ $quote-TB-gap: 20px
341
337
  // @extend %table_border_rows
342
338
  // @extend %table_border_full
343
339
  @extend %table_solidSingle_borderColumn
340
+
341
+ &[data-table-markdown="on"]
342
+ table p
343
+ white-space: pre-wrap
344
+
344
345
  table
345
346
  th, td
346
347
  text-align: left
347
348
  background: #fff
349
+ span
350
+ display: block
351
+ ol,ul
352
+ &:not(:first-child)
353
+ margin-top: 10px
348
354
  thead
349
355
  th
350
356
  // 以下可修改
351
357
  font-size: 16px
352
358
  line-height: 1.2
353
359
  font-weight: 600
354
- span
355
- display: block
356
360
  tbody
357
361
  td
358
362
  // 以下可修改
359
363
  font-size: 16px
360
364
  line-height: 1.2
361
- span
362
- display: block
363
- ol,ul
364
- &:not(:first-child)
365
- margin-top: 10px
366
365
 
367
366
  %table_tipText
368
367
  // 以下可修改
@@ -581,8 +580,6 @@ $quote-TB-gap: 20px
581
580
  line-height: 1.2
582
581
  &:not(:first-child)
583
582
  margin-top: 10px
584
- p
585
- white-space: pre-wrap
586
583
  em
587
584
  font-style: italic
588
585
  a
@@ -1,13 +1,16 @@
1
- // list 內細項若無帶 key 值, 就不會開啟 switch 開關; state 可以控制預設值
2
- // 基本上進階版細項開關不開, 有需求才開
1
+ // 此處為 demo 資料, 後端會用相同資料結構洗資料在 #_wcookie
2
+ // 給後端 :
3
+ // 基本上進階版細項開關不開, 有需求才開; 若開啟進階版細項, 大分類的 close 開關預設會無效, 會以判斷細項是否有開啟 or 全關為主
4
+ // close: 0 -> 開啟; close: 1 -> 關閉
3
5
  export const cookieData = [
4
6
  {
7
+ multiType: true, // 是否開啟個別選項設定
5
8
  title: "您的 Cookie 偏好設定",
6
9
  text: "我們使用不同類型的 Cookies 來優化您在我們的網站上的體驗,點擊下面類別以了解其目的與更多訊息。<br>您可以選擇允許的 Cookies 類型,也可以隨時更改您的偏好設定。<br>提醒您,停用 Cookies 可能會影響您在網站上的體驗,您可以瀏覽我們的隱私權政策,進一步了解我們如何使用 Cookie。",
7
10
  options: [
8
11
  {
9
12
  key: "required",
10
- state: true,
13
+ close: 0,
11
14
  disabled: true,
12
15
  title: "必要",
13
16
  text: "這些 Cookies 對於支持核心網站功能(例如提供安全登錄)至關重要。",
@@ -18,26 +21,26 @@ export const cookieData = [
18
21
  text: "第一方 cookies 由用戶訪問的網站(即主機域名)設置。網站通常使用第一方 cookies 用於跟踪訪客在網站上的行為並個性化他們在網站上的瀏覽體驗以及個性化他們的瀏覽體驗。",
19
22
  list: [
20
23
  {
21
- key: "",
22
- state: "",
24
+ key: "PHPSESSID",
25
+ close: 0,
23
26
  title: "PHPSESSID",
24
27
  text: "php用來辨識用戶的session id",
25
28
  },
26
29
  {
27
- key: "",
28
- state: "",
30
+ key: "XSRF",
31
+ close: 0,
29
32
  title: "XSRF-TOKEN",
30
33
  text: "網站防止 CSRF(Cross-Site Request Forgery,跨站請求偽造)攻擊 的 token",
31
34
  },
32
35
  {
33
- key: "",
34
- state: "",
36
+ key: "web_session",
37
+ close: 0,
35
38
  title: "web_session",
36
39
  text: "網站用來辨識用戶的session id",
37
40
  },
38
41
  {
39
- key: "",
40
- state: "",
42
+ key: "hubspot",
43
+ close: 0,
41
44
  title: "hubspot",
42
45
  text: "hubspot 使用了Cloudflare服務,區分真人訪客與惡意機器人",
43
46
  },
@@ -48,77 +51,57 @@ export const cookieData = [
48
51
  },
49
52
  {
50
53
  key: "analytics",
51
- state: false,
54
+ close: 1,
52
55
  disabled: false,
53
56
  title: "分析與自訂",
54
57
  text: "這些 Cookies 分析網站優化的使用情況。",
55
58
  notice: "我們將無法分析您的網站使用情況,以提供自定義內容",
56
59
  data: {
57
60
  items: [
58
- // {
59
- // main: "第一方 Cookie 提供商",
60
- // text: "第一方 cookies 由用戶訪問的網站(即主機域名)設置。網站通常使用第一方 cookies 用於跟踪訪客在網站上的行為並個性化他們在網站上的瀏覽體驗以及個性化他們的瀏覽體驗。",
61
- // list: [
62
- // {
63
- // title: "gtm",
64
- // key: "gtm",
65
- // state: "",
66
- // },
67
- // {
68
- // title: "ga",
69
- // key: "ga",
70
- // state: "",
71
- // detail: [
72
- // {
73
- // title: "_ga",
74
- // text: "用來區分不同使用者。每個使用者會被指派唯一的 ID",
75
- // typeTitle: "時間 / 類型",
76
- // typeDesc: "13 個月",
77
- // },
78
- // {
79
- // title: "_ga_<container-id>",
80
- // text: "追蹤單一屬性 (Property) 的資料。",
81
- // typeTitle: "時間 / 類型",
82
- // typeDesc: "13 個月",
83
- // },
84
- // ],
85
- // },
86
- // ],
87
- // },
88
61
  {
89
- main: "第三方 Cookie 提供商",
90
- text: "第三方 cookies 是由其他第三方實體放置在網站上,而不是網站本身,即使用者在地址欄中看到的網域以外的域名。第三方 cookies 通常跟踪訪客在網站之間的行為,通常用於針對性廣告目的。",
62
+ main: "第一方 Cookie 提供商",
63
+ text: "第一方 cookies 由用戶訪問的網站(即主機域名)設置。網站通常使用第一方 cookies 用於跟踪訪客在網站上的行為並個性化他們在網站上的瀏覽體驗以及個性化他們的瀏覽體驗。",
91
64
  list: [
92
- // {
93
- // title: "googlefont",
94
- // key: "",
95
- // state: "",
96
- // },
97
65
  {
98
- title: "Vimeo",
99
- text: "https://vimeo.com/",
100
- key: "",
101
- state: "",
66
+ title: "gtm",
67
+ key: "gtm",
68
+ close: 1,
69
+ },
70
+ {
71
+ title: "ga",
72
+ key: "ga",
73
+ close: 1,
102
74
  detail: [
103
75
  {
104
- title: "_cf_bm",
105
- text: "Cloudflare bot manager manages incoming traffic that matches the criteria associated with bots.",
76
+ title: "_ga",
77
+ text: "用來區分不同使用者。每個使用者會被指派唯一的 ID",
106
78
  typeTitle: "時間 / 類型",
107
- typeDesc: "30 ",
79
+ typeDesc: "13 個月",
108
80
  },
109
81
  {
110
- title: "_cfuvid",
111
- text: "Cloudflare cookie used to enforce rate-limiting rules.",
82
+ title: "_ga_<container-id>",
83
+ text: "追蹤單一屬性 (Property) 的資料。",
112
84
  typeTitle: "時間 / 類型",
113
- typeDesc: "session",
85
+ typeDesc: "13 個月",
114
86
  },
115
87
  ],
116
88
  },
89
+ ],
90
+ },
91
+ {
92
+ main: "第三方 Cookie 提供商",
93
+ text: "第三方 cookies 是由其他第三方實體放置在網站上,而不是網站本身,即使用者在地址欄中看到的網域以外的域名。第三方 cookies 通常跟踪訪客在網站之間的行為,通常用於針對性廣告目的。",
94
+ list: [
95
+ {
96
+ title: "googlefont",
97
+ key: "googlefont",
98
+ close: 1,
99
+ },
117
100
  {
118
101
  title: "Youtube",
119
102
  text: "https://www.youtube.com/",
120
- key: "",
121
- state: "",
103
+ key: "Youtube",
104
+ close: 1,
122
105
  detail: [
123
106
  {
124
107
  title: "YSC",
@@ -157,67 +140,86 @@ export const cookieData = [
157
140
  },
158
141
  ],
159
142
  },
160
-
161
- // {
162
- // title: "Tiktok",
163
- // text: "https://www.tiktok.com/zh-Hant-TW/",
164
- // key: "",
165
- // state: "",
166
- // detail: [
167
- // {
168
- // title: "_ttp",
169
- // text: "To measure and improve the performance of advertising campaigns and to personalize the customer experience (including ads) on TikTok.",
170
- // typeTitle: "時間 / 類型",
171
- // typeDesc: "13 個月",
172
- // },
173
- // {
174
- // title: "msToken",
175
- // text: "用於存儲用戶的會話令牌,確保用戶在瀏覽器會話期間保持登錄狀態。",
176
- // typeTitle: "時間 / 類型",
177
- // typeDesc: "10 天",
178
- // },
179
- // {
180
- // title: "passport_csrf_token",
181
- // text: "用於防止跨站請求偽造(CSRF)攻擊,確保請求的合法性。",
182
- // typeTitle: "時間 / 類型",
183
- // typeDesc: "60 天",
184
- // },
185
- // {
186
- // title: "ttwid",
187
- // text: "用於識別用戶的唯一 ID,協助 TikTok 進行用戶識別與防止機器人行為。",
188
- // typeTitle: "時間 / 類型",
189
- // typeDesc: "10 ",
190
- // },
191
- // ],
192
- // },
193
- // {
194
- // title: "instagram",
195
- // text: "https://www.instagram.com/",
196
- // key: "",
197
- // state: "",
198
- // detail: [
199
- // {
200
- // title: "mid",
201
- // text: "用於區分不同的用戶,協助 Instagram 提供個性化的內容推薦、廣告展示以及其他社交媒體功能。",
202
- // typeTitle: "時間 / 類型",
203
- // typeDesc: "13 個月",
204
- // },
205
- // ],
206
- // },
207
- // {
208
- // title: "meta",
209
- // text: "https://www.facebook.com/privacy/policy",
210
- // key: "",
211
- // state: "",
212
- // detail: [
213
- // {
214
- // title: "_fbp",
215
- // text: "用於辨識網站訪客,讓 Meta 可以顯示相關廣告(再行銷用途)。",
216
- // typeTitle: "時間 / 類型",
217
- // typeDesc: "90 天",
218
- // },
219
- // ],
220
- // },
143
+ {
144
+ title: "Vimeo",
145
+ text: "https://vimeo.com/",
146
+ key: "Vimeo",
147
+ close: 1,
148
+ detail: [
149
+ {
150
+ title: "_cf_bm",
151
+ text: "Cloudflare bot manager manages incoming traffic that matches the criteria associated with bots.",
152
+ typeTitle: "時間 / 類型",
153
+ typeDesc: "30 ",
154
+ },
155
+ {
156
+ title: "_cfuvid",
157
+ text: "Cloudflare cookie used to enforce rate-limiting rules.",
158
+ typeTitle: "時間 / 類型",
159
+ typeDesc: "session",
160
+ },
161
+ ],
162
+ },
163
+ {
164
+ title: "Tiktok",
165
+ text: "https://www.tiktok.com/zh-Hant-TW/",
166
+ key: "Tiktok",
167
+ close: 1,
168
+ detail: [
169
+ {
170
+ title: "_ttp",
171
+ text: "To measure and improve the performance of advertising campaigns and to personalize the customer experience (including ads) on TikTok.",
172
+ typeTitle: "時間 / 類型",
173
+ typeDesc: "13 個月",
174
+ },
175
+ {
176
+ title: "msToken",
177
+ text: "用於存儲用戶的會話令牌,確保用戶在瀏覽器會話期間保持登錄狀態。",
178
+ typeTitle: "時間 / 類型",
179
+ typeDesc: "10 天",
180
+ },
181
+ {
182
+ title: "passport_csrf_token",
183
+ text: "用於防止跨站請求偽造(CSRF)攻擊,確保請求的合法性。",
184
+ typeTitle: "時間 / 類型",
185
+ typeDesc: "60 ",
186
+ },
187
+ {
188
+ title: "ttwid",
189
+ text: "用於識別用戶的唯一 ID,協助 TikTok 進行用戶識別與防止機器人行為。",
190
+ typeTitle: "時間 / 類型",
191
+ typeDesc: "10 天",
192
+ },
193
+ ],
194
+ },
195
+ {
196
+ title: "instagram",
197
+ text: "https://www.instagram.com/",
198
+ key: "instagram",
199
+ close: 1,
200
+ detail: [
201
+ {
202
+ title: "mid",
203
+ text: "用於區分不同的用戶,協助 Instagram 提供個性化的內容推薦、廣告展示以及其他社交媒體功能。",
204
+ typeTitle: "時間 / 類型",
205
+ typeDesc: "13 個月",
206
+ },
207
+ ],
208
+ },
209
+ {
210
+ title: "meta",
211
+ text: "https://www.facebook.com/privacy/policy",
212
+ key: "meta",
213
+ close: 1,
214
+ detail: [
215
+ {
216
+ title: "_fbp",
217
+ text: "用於辨識網站訪客,讓 Meta 可以顯示相關廣告(再行銷用途)。",
218
+ typeTitle: "時間 / 類型",
219
+ typeDesc: "90 天",
220
+ },
221
+ ],
222
+ },
221
223
  ],
222
224
  },
223
225
  ]
@@ -225,14 +227,14 @@ export const cookieData = [
225
227
  },
226
228
  {
227
229
  key: "functionality",
228
- state: false,
230
+ close: 1,
229
231
  disabled: false,
230
232
  title: "性能與功能",
231
233
  text: "這些 Cookies 有助於測量和分析,以改善瀏覽體驗",
232
234
  },
233
235
  {
234
236
  key: "advertising",
235
- state: false,
237
+ close: 1,
236
238
  disabled: false,
237
239
  title: "廣告",
238
240
  text: "這些 Cookies 有助於提供與您相關的廣告內容",
@@ -0,0 +1,157 @@
1
+ // js 產 cookie 結構方法
2
+ /**
3
+ * @param {Array} target
4
+ * @param {(opt: cookieData[0], index: number) => string} fn
5
+ */
6
+ const generateHtmlString = (target, fn) => {
7
+ return target.map((data, index) => fn(data, index)).join("")
8
+ }
9
+
10
+ export const cookieStep1El = (opt, index, isDisabled, isChecked) => {
11
+ return `
12
+ <div class="item" data-index="${index}">
13
+ <div class="left">
14
+ <div class="checkbox ${isDisabled}">
15
+ <div class="input-wrap">
16
+ <input type="checkbox"
17
+ id="s1-check-${index}"
18
+ ${isChecked()}
19
+ data-name="${opt.key}"
20
+ >
21
+ <div class="fake-checkbox">
22
+ <i class="icon-check"></i>
23
+ </div>
24
+ </div>
25
+ <label for="s1-check-${index}">
26
+ <span>${opt.title}</span>
27
+ </label>
28
+ </div>
29
+ <div class="desc">
30
+ <div class="text">${opt.text}</div>
31
+ ${opt.notice ? `
32
+ <div class="notice">
33
+ <div class="icon">
34
+ <i class="icon-notice"></i>
35
+ </div>
36
+ <span>${opt.notice}</span>
37
+ </div>` : ""}
38
+ </div>
39
+ </div>
40
+ <div class="right">
41
+ <div class="switch-box ${isDisabled}">
42
+ <input type="checkbox" hidden
43
+ id="s1-switch-${index}"
44
+ ${isChecked()}
45
+ data-name="${opt.key}"
46
+ >
47
+ <label class="switch" for="s1-switch-${index}"></label>
48
+ </div>
49
+ <div class="icon" data-open>
50
+ <i class="icon-arrow"></i>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ `
55
+ }
56
+
57
+ export const cookieStep2TittleEl = (opt, index, isDisabled, isChecked) => {
58
+ return `
59
+ <div class="box">
60
+ <div class="left">
61
+ <div class="checkbox ${isDisabled}">
62
+ <div class="input-wrap">
63
+ <input type="checkbox"
64
+ id="s2-check-${index}"
65
+ ${isChecked()}
66
+ data-name="${opt.key}"
67
+ >
68
+ <div class="fake-checkbox">
69
+ <i class="icon-check"></i>
70
+ </div>
71
+ </div>
72
+ <label for="s2-check-${index}">
73
+ <span>${opt.title}</span>
74
+ </label>
75
+ </div>
76
+ </div>
77
+ <div class="right">
78
+ <div class="switch-box ${isDisabled}">
79
+ <input type="checkbox" hidden
80
+ id="s2-switch-${index}"
81
+ ${isChecked()}
82
+ data-name="${opt.key}"
83
+ >
84
+ <label class="switch" for="s2-switch-${index}"></label>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ <div class="desc">
89
+ <div class="text">${opt.text || ""}</div>
90
+ ${opt.notice ?
91
+ `<div class="notice">
92
+ <div class="icon">
93
+ <i class="icon-notice"></i>
94
+ </div>
95
+ <span>${opt.notice}</span>
96
+ </div> ` : ""}
97
+ </div>
98
+ `
99
+ }
100
+
101
+ export const cookieStep2DetailEl = (data, li, index, parentKey, num, isDisabled, isListChecked) => {
102
+ return `
103
+ <div class="list collapseItem" data-collapse>
104
+ <div class="title collapseTitle" data-collapse-click>
105
+ <div class="icon">
106
+ <i class="icon-arrow"></i>
107
+ </div>
108
+ <span>${li.title || ""}</span>
109
+ ${data.multiType ?
110
+ `<div class="switch-box ${isDisabled}">
111
+ <input
112
+ type="checkbox"
113
+ hidden
114
+ id="s2-content-${num}-${index}"
115
+ ${isListChecked}
116
+ data-name="${li.key}"
117
+ data-parent="${parentKey || ""}"
118
+ >
119
+ <label
120
+ class="switch"
121
+ for="s2-content-${num}-${index}">
122
+ </label>
123
+ </div>`
124
+ : ""
125
+ }
126
+ </div>
127
+ ${li.text ?
128
+ `<div class="text">${li.text}</div>`
129
+ : ""
130
+ }
131
+ <div class="list-detail collapseBox" data-collapse-content>
132
+ <div class="innerBox">
133
+ <div class="content">
134
+ ${li.detail?.length > 0 ?
135
+ generateHtmlString(li.detail, (i) => {
136
+ return `
137
+ <div class="li">
138
+ <div class="title">${i.title || ""}</div>
139
+ ${i.text ?
140
+ `<div class="text">${i.text}</div>`
141
+ : ""
142
+ }
143
+ <div class="flex">
144
+ <div class="text bold">${i.typeTitle || ""}</div>
145
+ <div class="text">${i.typeDesc || ""}</div>
146
+ </div>
147
+ </div>
148
+ `
149
+ })
150
+ : ""
151
+ }
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ `
157
+ }
@@ -5,15 +5,24 @@ import {
5
5
  scrollLock,
6
6
  scrollUnlock
7
7
  } from '@xwadex/fesd/tools';
8
- import Cookies from "js-cookie"
9
- import { OverlayScrollbars } from 'overlayscrollbars'
10
- import { cookieData } from "./cookieData"
11
-
8
+ import Cookies from "js-cookie";
9
+ import { OverlayScrollbars } from 'overlayscrollbars';
10
+ import { cookieData } from "./cookieData";
11
+ import { cookieStep1El, cookieStep2TittleEl, cookieStep2DetailEl } from "./cookieElement"
12
+
13
+ // 基礎版 cookie
14
+ const cookieBasic = {}
15
+ // 進階版 cookie
16
+ const cookieAdvanced = {}
17
+ // 存方法工具
12
18
  const cookieSetting = {}
19
+ // 存結構
13
20
  const cookieElements = {}
21
+ // 存一個空物件
22
+ let cookieNewObj = {};
14
23
 
15
24
  // 簡易版 cookie 設定
16
- cookieSetting.basicCookiePolicy = () => {
25
+ cookieBasic.basicCookiePolicy = () => {
17
26
  const $cookiePolicy = $(".cookie-check");
18
27
  if (!$cookiePolicy.length) return;
19
28
 
@@ -47,7 +56,21 @@ cookieSetting.basicCookiePolicy = () => {
47
56
  $closeBtn.on("click", () => {
48
57
  $cookiePolicy.addClass("hidden");
49
58
  sessionStorage.setItem("frameworkCookie-agree", "false");
59
+ if (typeof window._wdSetting === 'function') {
60
+ window._wdSetting()
61
+ }
50
62
  });
63
+
64
+ // ⚠️ 以下可斟酌移除
65
+ // 開啟 cookie 視窗按鈕
66
+ $("[data-cookie-open]").on("click", function() {
67
+ $cookiePolicy.addClass("first-enter");
68
+ $cookiePolicy.removeClass("agree");
69
+ $cookiePolicy.removeClass("hidden");
70
+ setTimeout(() => {
71
+ $cookiePolicy.addClass("show");
72
+ }, 800);
73
+ })
51
74
  };
52
75
 
53
76
  // 執行事件
@@ -55,33 +78,90 @@ cookieSetting.basicCookiePolicy = () => {
55
78
  clickEvent();
56
79
  };
57
80
 
58
- // cookie 開啟方法
59
- cookieSetting.openEvent = (el) => {
60
- $(el).addClass("active")
61
- setTimeout(() => {
62
- $(el).addClass("show")
63
- scrollLock();
64
- const scrollers = $(".cookie-settingBlock").find('.scroller')[0]
65
- OverlayScrollbars(scrollers, {})
66
- }, 500);
81
+ // ⚠️ ** --- 結構這邊修改 --- ** ⚠️
82
+ // step 1 結構
83
+ cookieElements.getStep1Element = (opt, index) => {
84
+ const isDisabled = opt.disabled ? "disabled" : ""
85
+ const isChecked = () => {
86
+ if (opt.key == "required") {
87
+ return "checked"
88
+ } else {
89
+ if (cookieSetting.hasAnyListItemsOpen(opt.key)) {
90
+ return "checked"
91
+ } else {
92
+ return ""
93
+ }
94
+ }
95
+ }
96
+ return cookieStep1El(opt, index, isDisabled, isChecked)
67
97
  }
68
98
 
69
- // cookie 關閉方法
70
- cookieSetting.closeEvent = (el) => {
71
- $(el).removeClass("show")
72
- setTimeout(() => {
73
- $(el).removeClass("active")
74
- scrollUnlock();
75
- }, 500);
99
+ // step 2 上面結構
100
+ cookieElements.getStep2TopElement = (opt, index) => {
101
+ const isDisabled = opt.disabled ? "disabled" : ""
102
+ const isChecked = () => {
103
+ if (opt.key == "required") {
104
+ return "checked"
105
+ } else {
106
+ if (cookieSetting.hasAnyListItemsOpen(opt.key)) {
107
+ return "checked"
108
+ } else {
109
+ return ""
110
+ }
111
+ }
112
+ }
113
+ return cookieStep2TittleEl(opt, index, isDisabled, isChecked)
76
114
  }
77
115
 
78
- // 打開進階版 cookie 介面
79
- cookieSetting.clickEvent = () => {
80
- const $settingBtn = $("[data-cookie-open]")
81
- const $settingBlock = $(".cookie-settingBlock")
82
- $settingBtn.on("click", () => {
83
- cookieSetting.openEvent($settingBlock)
84
- });
116
+ // step 2 下面細項結構
117
+ cookieElements.getStep2BottomElement = (data, li, index, parentKey, num) => {
118
+ const isDisabled = li.disabled ? " disabled" : ""
119
+ const isListChecked = cookieNewObj[parentKey].list[li.key].close == 0 ? "checked" : ""
120
+
121
+ return cookieStep2DetailEl(data, li, index, parentKey, num, isDisabled, isListChecked)
122
+ }
123
+
124
+ // 產生新物件
125
+ cookieAdvanced.newObj = () => {
126
+ if (Cookies.get('_wd') !== undefined) {
127
+ const cloudDdata = JSON.parse(Cookies.get('_wd'))
128
+ cookieNewObj = { ...cloudDdata }
129
+ } else {
130
+ const data = $("#_wcookie").val() ? JSON.parse($("#_wcookie").val())[0] : cookieData[0]
131
+ const options = data?.options;
132
+ if (options) {
133
+ // 判斷是否全開 or 全關 (排除 required)
134
+ const nonRequiredOptions = options.filter(opt => opt.key !== 'required');
135
+ const allNonRequiredAreFalse = nonRequiredOptions.every(opt => opt.close === 1);
136
+ const globalCloseValue = allNonRequiredAreFalse == 1 ? 1 : 0;
137
+
138
+ cookieNewObj.close = globalCloseValue;
139
+
140
+ const categoriesObject = options.reduce((acc, option) => {
141
+ const topLevelKey = option.key;
142
+ const dataItems = option.data?.items;
143
+
144
+ acc[topLevelKey] = { close: option.close };
145
+
146
+ if (data.multiType && dataItems !== undefined && dataItems.length > 0) {
147
+ acc[topLevelKey].list = {};
148
+
149
+ dataItems.forEach(item => {
150
+ item.list?.forEach(listItem => {
151
+ if (listItem.key) {
152
+ acc[topLevelKey].list[listItem.key] = {
153
+ close: listItem.close
154
+ };
155
+ }
156
+ });
157
+ });
158
+ }
159
+ return acc;
160
+ }, {});
161
+ // 若無 cookie 紀錄可抓, 直接新建一個新的物件預存
162
+ Object.assign(cookieNewObj, categoriesObject);
163
+ }
164
+ }
85
165
  }
86
166
 
87
167
  // js 產 cookie 結構方法
@@ -94,7 +174,7 @@ cookieElements.generateHtmlString = (target, fn) => {
94
174
  }
95
175
 
96
176
  // 產資料結構
97
- cookieSetting.dataAppend = () => {
177
+ cookieAdvanced.dataAppend = () => {
98
178
  const $block = $(".cookie-settingBlock")
99
179
  if (!$block.length) return
100
180
 
@@ -111,26 +191,35 @@ cookieSetting.dataAppend = () => {
111
191
  const opt = data.options[index]
112
192
  const items = opt.data?.items || []
113
193
 
194
+ // ⚠️ ** --- 結構這邊修改 --- ** ⚠️
114
195
  // 產 step2 下方結構
115
196
  const htmlStep2bottom = items.length > 0
116
- ? items.map(i => `
197
+ ? items.map((item,num) => `
117
198
  <div class="item">
118
199
  <div class="list">
119
- <div class="main">${i.main}</div>
120
- <div class="text">${i.text}</div>
200
+ <div class="main">${item.main}</div>
201
+ <div class="text">${item.text}</div>
121
202
  </div>
122
203
  ${
123
- (i.list || []).map((li, index) =>
124
- (li.detail && li.detail.length > 0)
125
- ? cookieElements.getStep2BottomElement(li, index, opt.key)
204
+ (item.list || []).map((li, index) =>
205
+ (li.detail && li.detail.length > 0) ?
206
+ cookieElements.getStep2BottomElement(data, li, index, opt.key, num)
126
207
  : `
127
208
  <div class="list">
128
209
  <div class="title">
129
210
  <span>${li.title || ""}</span>
130
- ${li.key.length > 0 ? `<div class="switch-box ${opt.disabled ? " disabled" : ""}">
131
- <input type="checkbox" hidden id="s2detail-switch-${index}" ${opt.disabled? "checked" : ""} data-name="${li.key}" data-parent="${opt.key || ""}">
132
- <label class="switch" for="s2detail-switch-${index}" ${opt.disabled ? "checked" : ""}></label>
133
- </div>` : ""}
211
+ ${data.multiType && li.key.length > 0 ?
212
+ `<div class="switch-box ${opt.disabled ? "disabled" : ""}">
213
+ <input type="checkbox" hidden
214
+ id="s2-content-${num}-${index}"
215
+ ${cookieNewObj[opt.key].list[li.key].close == 0 ? "checked" : ""}
216
+ data-name="${li.key}"
217
+ data-parent="${opt.key || ""}"
218
+ >
219
+ <label class="switch" for="s2-content-${num}-${index}"></label>
220
+ </div>`
221
+ : ""
222
+ }
134
223
  </div>
135
224
  <div class="text">${li.text || ""}</div>
136
225
  </div>
@@ -140,291 +229,241 @@ cookieSetting.dataAppend = () => {
140
229
  </div>
141
230
  `).join("")
142
231
  : `<div class="item"><div class="text">此分類目前沒有詳細資料。</div></div>`
143
-
232
+
144
233
  // 產 step2 上方結構
145
234
  $step2.find(".top-block").html(cookieElements.getStep2TopElement(opt, index))
146
235
  // 產 step2 下方結構
147
236
  $step2.find(".content-block").html(htmlStep2bottom)
148
- // 開關狀態判斷
149
- switchState(opt.key)
237
+
150
238
  // 換頁
151
239
  methods.pageChange(true)
240
+ // 重綁收合
152
241
  methods.collapseEvent('[data-collapse-click]', '[data-collapse]', '[data-collapse-content]', false, true)
153
242
  })
243
+ }
244
+
245
+ // switch 的一些狀態
246
+ cookieAdvanced.switchState = () => {
247
+ const $step2contentSwitch = `[data-step="2"] .content-block input[type="checkbox"][data-parent][data-name]:not(.disabled)`
248
+ const $step2topSwitch = `[data-step="2"] .top-block input[type="checkbox"][data-name]:not(.disabled)`
249
+
250
+ let isSyncing = false
154
251
 
155
252
  // 開關同步連動控制
156
- // 若只有一顆控制按鈕, 可關閉
253
+ // ⚠️ 若只有一顆控制按鈕, 可關閉
157
254
  $(document).on('change', 'input[type="checkbox"][data-name]:not(:disabled)', function () {
158
255
  const key = $(this).data('name')
159
256
  const val = $(this).prop('checked')
160
-
257
+ if (isSyncing) return
258
+ isSyncing = true
259
+
161
260
  $(`input[type="checkbox"][data-name="${key}"]`).not(this).prop('checked', val)
261
+
262
+ isSyncing = false
162
263
  })
163
264
 
164
- const switchState = (el) => {
165
- const currentVal = $(`input[type="checkbox"][data-name="${el}"]`).first().prop('checked') ?? false
166
- $step2.find(`input[type="checkbox"][data-name="${el}"]`).prop('checked', currentVal)
167
-
168
- const $step2TopCheckbox = $(
169
- `[data-step="2"] .top-block input[type="checkbox"][data-name="${el}"]`
170
- )
171
-
172
- const stepCurrentVal = $step2TopCheckbox.prop('checked') ?? false
173
- $(`[data-step="2"] .content-block input[type="checkbox"][data-name]:not(:disabled)`)
174
- .prop('checked', stepCurrentVal)
175
- .trigger('change')
176
-
177
- $(document).on(
178
- 'change',
179
- `[data-step="2"] .top-block input[type="checkbox"][data-name="${el}"]`,
180
- function () {
181
- const val = $(this).prop('checked') ?? false
182
-
183
- $(
184
- `[data-step="2"] .content-block input[type="checkbox"][data-name]:not(:disabled)`
185
- )
186
- .prop('checked', val)
187
- .trigger('change')
188
- }
189
- )
190
-
191
- $(document).on(
192
- 'change',
193
- `[data-step="2"] .content-block input[type="checkbox"][data-name]:not(:disabled)`,
194
- function () {
195
- const $all = $(
196
- `[data-step="2"] .content-block input[type="checkbox"][data-name]:not(:disabled)`
197
- )
198
- const total = $all.length
199
- const checked = $all.filter(':checked').length
200
-
201
- if (!total) return
202
-
203
- if (checked === total) {
204
- // 全開
205
- $step2TopCheckbox.prop('checked', true)
206
- } else if (checked === 0) {
207
- // 全關
208
- $step2TopCheckbox.prop('checked', false)
209
- }
265
+ // step1 變動 更新 cookieNewObj
266
+ $(document)
267
+ .off('change.cookieStep1')
268
+ .on('change.cookieStep1', `[data-step="1"] input[type="checkbox"][data-name]`, function () {
269
+ if (isSyncing) return
270
+ isSyncing = true
271
+
272
+ const key = $(this).data("name")
273
+ const val = $(this).prop('checked') ? 0 : 1
274
+ if (cookieNewObj[key]) {
275
+ cookieNewObj[key].close = val
276
+ const targetList = cookieNewObj[key].list;
277
+ if (targetList && typeof targetList === 'object') {
278
+ Object.keys(targetList).forEach(childKey => {
279
+ if (targetList[childKey].close !== undefined) {
280
+ targetList[childKey].close = val;
281
+ }
282
+ });
283
+ }
210
284
  }
211
- )
212
285
 
213
- }
214
- }
286
+ isSyncing = false
287
+ })
215
288
 
216
- // 存 cookie
217
- cookieSetting.cookieSaveFn = (el) => {
218
- const $container = $(".cookie-settingBlock")
219
-
220
- // 紀錄每一個值存入
221
- cookieSetting.save = () => {
222
- let allOpen = true;
223
- const mainCookies = ['required', 'analytics', 'functionality', 'advertising'];
224
- const obj = {};
225
-
226
- mainCookies.forEach((key) => {
227
- // 先存大分類的 cookie
228
- const isOpen = $(`input[type="checkbox"][data-name="${key}"]`)
229
- .first()
230
- .prop('checked');
231
- const category = {
232
- close: isOpen ? 0 : 1,
233
- };
234
-
235
- // 再存大分類底下所有細項的 cookie
236
- const children = {};
237
- $(`input[type="checkbox"][data-parent="${key}"][data-name]`)
238
- .each(function () {
239
- const childKey = $(this).data('name');
240
- const childOpen = $(this).prop('checked');
241
- children[childKey] = {
242
- close: childOpen ? 0 : 1,
243
- };
244
- });
245
-
246
- if (Object.keys(children).length > 0) {
247
- category.list = children;
248
- }
289
+ $(document)
290
+ .off("change.step2content")
291
+ .on("change.step2content", $step2contentSwitch, function () {
249
292
 
250
- obj[key] = category;
251
- });
293
+ if (isSyncing) return
294
+ isSyncing = true
295
+
296
+ const key = $(this).data("name")
297
+ const parent = $(this).data("parent")
298
+ const val = $(this).prop('checked') ? 0 : 1
299
+ const $step1Switch = $(`[data-step="1"] input[type="checkbox"][data-name='${parent}']:not(.disabled)`)
300
+ const total = $($step2contentSwitch).length
301
+ const checked = $($step2contentSwitch).filter(':checked').length
252
302
 
253
- // 判斷是否除了 required 以外全部關閉
254
- for (let i = 0; i < mainCookies.length; i++) {
255
- const key = mainCookies[i];
256
- if (key === 'required') continue;
257
- if (obj[key].close !== 1) {
258
- allOpen = false;
259
- break;
303
+ // 改變 cookieNewObj 的值
304
+ cookieNewObj[parent].list[key].close = val
305
+
306
+ // Step2 子項全部打開
307
+ if (checked === total || checked > 0) {
308
+ cookieNewObj[parent].close = 0
309
+ $($step2topSwitch).prop("checked", true)
310
+ $step1Switch.prop("checked", true)
311
+ }
312
+
313
+ // Step2 子項全部關閉
314
+ else if (checked === 0) {
315
+ cookieNewObj[parent].close = 1
316
+ $($step2topSwitch).prop("checked", false)
317
+ $step1Switch.prop("checked", false)
260
318
  }
261
- }
262
319
 
263
- obj.close = allOpen ? 1 : 0;
320
+ isSyncing = false
321
+ })
264
322
 
265
- Cookies.set('_wd', JSON.stringify(obj), {
266
- expires: 180,
267
- path: '/',
268
- sameSite: true,
269
- });
270
- }
323
+ $(document)
324
+ .off("change.step2top")
325
+ .on("change.step2top", $step2topSwitch, function () {
271
326
 
272
- // 重整後重抓 cookie 設定
273
- cookieSetting.restore = () => {
274
- const raw = Cookies.get('_wd');
275
- if (!raw) return;
327
+ if (isSyncing) return
328
+ isSyncing = true
276
329
 
277
- let mystore;
278
- try {
279
- mystore = JSON.parse(raw);
280
- } catch {
281
- return;
282
- }
330
+ const key = $(this).data("name")
331
+ const isChecked = $(this).prop("checked")
332
+ const val = isChecked ? 0 : 1
283
333
 
284
- const mainCookies = ['required', 'analytics', 'functionality', 'advertising'];
334
+ // 改所有子項
335
+ $($step2contentSwitch)
336
+ .prop('checked', isChecked)
285
337
 
286
- mainCookies.forEach((key) => {
287
- const category = mystore[key];
288
- if (!category) return;
338
+ // cookie main
339
+ if (cookieNewObj[key]) {
340
+ const list = cookieNewObj[key].list
341
+ cookieNewObj[key].close = val
342
+ if (list) {
343
+ Object.keys(list).forEach(childKey => {
344
+ list[childKey].close = val
345
+ })
346
+ }
347
+ }
348
+ isSyncing = false
349
+ })
350
+ }
289
351
 
290
- const checked = category.close === 0;
291
- $(`input[type="checkbox"][data-name="${key}"]`)
292
- .prop('checked', checked)
293
- .trigger('change');
352
+ // cookie
353
+ cookieAdvanced.cookieSaveFn = () => {
354
+ const nonRequiredValues = Object.entries(cookieNewObj)
355
+ .filter(([key]) => key !== 'required' && key !== 'close')
356
+ .map(([, value]) => value);
357
+
358
+ const allNonRequiredAreClosed = nonRequiredValues.every(opt => opt.close === 1);
359
+ const globalCloseValue = allNonRequiredAreClosed ? 1 : 0;
360
+
361
+ // 判斷設定第一層的 close 值
362
+ cookieNewObj.close = globalCloseValue;
363
+
364
+ // 存入 cookie
365
+ Cookies.set('_wd', JSON.stringify(cookieNewObj), {
366
+ expires: 180,
367
+ path: '/',
368
+ sameSite: true,
369
+ });
370
+ }
371
+
372
+ // 點擊方法
373
+ cookieAdvanced.clickEvent = () => {
374
+ const $settingBtn = $(".cookie-settingBtn")
375
+ const $settingBlock = $(".cookie-settingBlock")
376
+
377
+ // 打開進階版 cookie 介面
378
+ $settingBtn.on("click", () => {
379
+ cookieSetting.openEvent($settingBlock)
380
+ });
294
381
 
295
- const children = category.list || {};
296
- Object.keys(children).forEach((childKey) => {
297
- const childChecked = children[childKey].close === 0;
298
- $(`input[type="checkbox"][data-parent="${key}"][data-name="${childKey}"]`)
299
- .prop('checked', childChecked)
300
- .trigger('change');
301
- });
302
- });
303
- };
304
382
  // 一鍵全開
305
383
  cookieSetting.enableAll = () => {
306
- $(`input[type="checkbox"][data-name]:not(:disabled)`).prop('checked', true).trigger('change')
384
+ $(`input[type="checkbox"][data-name]:not(.disabled)`).prop('checked', true).trigger('change')
385
+ cookieNewObj.close = 0
386
+ Object.keys(cookieNewObj).forEach(data => {
387
+ cookieNewObj[data].close == 0
388
+ })
389
+ }
390
+ // 一鍵全關
391
+ cookieSetting.rejectAll = () => {
392
+ $(`input[type="checkbox"][data-name]:not([data-name="required"])`).prop('checked', false).trigger('change')
393
+ cookieNewObj.close = 1
394
+ Object.keys(cookieNewObj).forEach(data => {
395
+ cookieNewObj[data].close == 1
396
+ })
307
397
  }
308
-
309
398
  // 綁定按鈕
310
- $("[data-cookie-all]").on('click', cookieSetting.enableAll)
399
+ $("[data-cookie-all]").on('click', function(){
400
+ cookieSetting.enableAll()
401
+ })
311
402
  // 儲存
312
403
  $("[data-cookie-save]").on('click', function(){
313
- cookieSetting.save()
404
+ cookieAdvanced.cookieSaveFn()
314
405
  })
315
406
  // 關閉視窗
316
407
  $("[data-cookie-close]").on('click', function(){
317
- cookieSetting.closeEvent($container)
408
+ cookieSetting.closeEvent($settingBlock)
409
+ })
410
+ $("[data-cookie-reject]").on('click', function() {
411
+ cookieSetting.rejectAll()
412
+ cookieAdvanced.cookieSaveFn()
318
413
  })
319
-
320
- // 頁面載入時先還原一次
321
- $(cookieSetting.restore)
322
414
  }
323
415
 
324
- // ⚠️ ** --- 結構這邊修改 --- ** ⚠️
325
- // step 1 結構
326
- cookieElements.getStep1Element = (opt, index) => {
327
- return `
328
- <div class="item" data-index="${index}">
329
- <div class="left">
330
- <div class="checkbox ${opt.disabled ? " disabled" : ""}">
331
- <div class="input-wrap">
332
- <input type="checkbox" id="s1-check-${index}" ${opt.disabled ? "checked" : ""} data-name="${opt.key}">
333
- <div class="fake-checkbox"><i class="icon-check"></i></div>
334
- </div>
335
- <label for="s1-check-${index}"><span>${opt.title}</span></label>
336
- </div>
337
- <div class="desc">
338
- <div class="text">${opt.text}</div>
339
- ${opt.notice ? `
340
- <div class="notice">
341
- <div class="icon"><i class="icon-notice"></i></div>
342
- <span>${opt.notice}</span>
343
- </div>` : ""}
344
- </div>
345
- </div>
346
- <div class="right">
347
- <div class="switch-box ${opt.disabled ? " disabled" : ""}">
348
- <input type="checkbox" hidden id="s1-switch-${index}" ${opt.disabled ? "checked" : ""} data-name="${opt.key}">
349
- <label class="switch" for="s1-switch-${index}"></label>
350
- </div>
351
- <div class="icon" data-open><i class="icon-arrow"></i></div>
352
- </div>
353
- </div>
354
- `
416
+ // ** --- 工具們 --- **
417
+ // cookie 開啟方法
418
+ cookieSetting.openEvent = (el) => {
419
+ $(el).addClass("active")
420
+ setTimeout(() => {
421
+ $(el).addClass("show")
422
+ scrollLock();
423
+ const scrollers = $(".cookie-settingBlock").find('.scroller')[0]
424
+ OverlayScrollbars(scrollers, {})
425
+ }, 500);
355
426
  }
356
-
357
- // step 2 上面結構
358
- cookieElements.getStep2TopElement = (opt, index) => {
359
- return `
360
- <div class="box">
361
- <div class="left">
362
- <div class="checkbox ${opt.disabled ? " disabled" : ""}">
363
- <div class="input-wrap">
364
- <input type="checkbox" id="s2-check-${index}" ${opt.disabled ? "checked" : ""} data-name="${opt.key}">
365
- <div class="fake-checkbox"><i class="icon-check"></i></div>
366
- </div>
367
- <label for="s2-check-${index}"><span>${opt.title}</span></label>
368
- </div>
369
- </div>
370
- <div class="right">
371
- <div class="switch-box ${opt.disabled ? " disabled" : ""}">
372
- <input type="checkbox" hidden id="s2-switch-${index}" ${opt.disabled ? "checked" : ""} data-name="${opt.key}">
373
- <label class="switch" for="s2-switch-${index}"></label>
374
- </div>
375
- </div>
376
- </div>
377
- <div class="desc">
378
- <div class="text">${opt.text || ""}</div>
379
- ${opt.notice ? `<div class="notice"><div class="icon"><i class="icon-notice"></i></div><span>${opt.notice}</span></div> ` : ""}
380
- </div>
381
- `
427
+ // cookie 關閉方法
428
+ cookieSetting.closeEvent = (el) => {
429
+ $(el).removeClass("show")
430
+ setTimeout(() => {
431
+ $(el).removeClass("active")
432
+ scrollUnlock();
433
+ }, 500);
382
434
  }
435
+ // 判斷該分類底下細項按鈕狀態
436
+ cookieSetting.hasAnyListItemsOpen = (key) => {
437
+ const targetList = cookieNewObj[key]?.list;
438
+ if (!targetList) {
439
+ if (cookieNewObj[key].close == 0) {
440
+ return true;
441
+ } else {
442
+ return false;
443
+ }
444
+ }
445
+ const childKeys = Object.keys(targetList);
446
+ const isOpen = childKeys.some(childKey => {
447
+ return targetList[childKey]?.close === 0;
448
+ });
449
+ return isOpen;
450
+ };
383
451
 
384
- // step 2 下面細項結構
385
- cookieElements.getStep2BottomElement = (opt,index,parentKey) => {
386
- return `
387
- <div class="list collapseItem" data-collapse>
388
- <div class="title collapseTitle" data-collapse-click>
389
- <div class="icon"><i class="icon-arrow"></i></div>
390
- <span>${opt.title || ""}</span>
391
- ${opt.key ? `<div class="switch-box ${opt.disabled ? " disabled" : ""}">
392
- <input type="checkbox" hidden id="s2detail-switch-${index}" ${opt.disabled ? "checked" : ""} data-name="${opt.key}" data-parent="${parentKey || ""}">
393
- <label class="switch" for="s2detail-switch-${index}"></label>
394
- </div>` : ""}
395
- </div>
396
- ${opt.text ? `<div class="text">${opt.text}</div>` : ""}
397
- <div class="list-detail collapseBox" data-collapse-content>
398
- <div class="innerBox">
399
- <div class="content">
400
- ${
401
- opt.detail?.length > 0 ? cookieElements.generateHtmlString(opt.detail, (i) => {
402
- return `
403
- <div class="li">
404
- <div class="title">${i.title || ""}</div>
405
- ${i.text ? `<div class="text">${i.text}</div>` : ""}
406
- <div class="flex">
407
- <div class="text bold">${i.typeTitle || ""}</div>
408
- <div class="text">${i.typeDesc || ""}</div>
409
- </div>
410
- </div>
411
- `
412
- }) : ""
413
- }
414
- </div>
415
- </div>
416
- </div>
417
- </div>
418
- `
452
+ cookieAdvanced.init = () => {
453
+ cookieAdvanced.newObj();
454
+ cookieAdvanced.dataAppend();
455
+ // cookieAdvanced.initialState();
456
+ cookieAdvanced.clickEvent();
457
+ cookieAdvanced.cookieSaveFn();
458
+ cookieAdvanced.switchState();
419
459
  }
420
460
 
421
461
  methods.toBackend({ getCookieValue: (el) => Cookies.get(el) });
422
462
 
423
- export const init = () => {
463
+ export const init = (isOpenAdvanced = false) => {
424
464
  // cookie 基本版
425
- cookieSetting.basicCookiePolicy()
465
+ cookieBasic.basicCookiePolicy();
466
+ if (!isOpenAdvanced) return
426
467
  // cookie 進階版
427
- cookieSetting.dataAppend();
428
- cookieSetting.clickEvent();
429
- cookieSetting.cookieSaveFn();
430
- }
468
+ cookieAdvanced.init();
469
+ }
@@ -52,7 +52,7 @@ export const inits = () => {
52
52
  new ImageValidate();
53
53
  // uiInit
54
54
  navbar.init();
55
- cookie.init();
55
+ cookie.init(true);
56
56
  // coding...
57
57
  firstEntryHandler.init();
58
58
  }
@@ -6,9 +6,9 @@
6
6
  p.title Cookie
7
7
  p.text 關於本網站使用瀏覽器紀錄 Cookie 來提供您最好的使用體驗,我們使用的 Cookie 也包括了第三方 Cookie 。<br>相關資訊請訪問我們的隱私權與 Cookie 政策。如果您選擇繼續瀏覽或關閉這個提示,便表示您已接受我們的網站使用條款。
8
8
  .button-group
9
- .cookie-btn.close Reject 我拒絕
9
+ .cookie-btn.close(data-cookie-reject) Reject 我拒絕
10
10
  .cookie-btn.agree(data-cookie-save) OK 我接受
11
- .cookie-settingBtn(data-cookie-open)
11
+ .cookie-settingBtn
12
12
  span Cookie 偏好設定
13
13
  .icon
14
14
  i.icon-arrow
@@ -37,4 +37,4 @@
37
37
  // JS append 結構
38
38
 
39
39
  // 後端 cookie 資料放這兒
40
- <input id="_wcookie" type="hidden" value="[{&quot;title&quot;:&quot;\u60a8\u7684 Cookie \u504f\u597d\u8a2d\u5b9a&quot;,&quot;text&quot;:&quot;\u6211\u5011\u4f7f\u7528\u4e0d\u540c\u985e\u578b\u7684 Cookies \u4f86\u512a\u5316\u60a8\u5728\u6211\u5011\u7684\u7db2\u7ad9\u4e0a\u7684\u9ad4\u9a57\uff0c\u9ede\u64ca\u4e0b\u9762\u985e\u5225\u4ee5\u4e86\u89e3\u5176\u76ee\u7684\u8207\u66f4\u591a\u8a0a\u606f\u3002\u003Cbr\u003E\u60a8\u53ef\u4ee5\u9078\u64c7\u5141\u8a31\u7684 Cookies \u985e\u578b\uff0c\u4e5f\u53ef\u4ee5\u96a8\u6642\u66f4\u6539\u60a8\u7684\u504f\u597d\u8a2d\u5b9a\u3002\u003Cbr\u003E\u63d0\u9192\u60a8\uff0c\u505c\u7528 Cookies \u53ef\u80fd\u6703\u5f71\u97ff\u60a8\u5728\u7db2\u7ad9\u4e0a\u7684\u9ad4\u9a57\uff0c\u60a8\u53ef\u4ee5\u700f\u89bd\u6211\u5011\u7684\u96b1\u79c1\u6b0a\u653f\u7b56\uff0c\u9032\u4e00\u6b65\u4e86\u89e3\u6211\u5011\u5982\u4f55\u4f7f\u7528 Cookie\u3002&quot;,&quot;options&quot;:[{&quot;key&quot;:&quot;required&quot;,&quot;state&quot;:true,&quot;disabled&quot;:true,&quot;title&quot;:&quot;\u56b4\u683c\u5fc5\u8981&quot;,&quot;text&quot;:&quot;\u9019\u4e9b Cookies \u5c0d\u65bc\u652f\u6301\u6838\u5fc3\u7db2\u7ad9\u529f\u80fd\uff08\u4f8b\u5982\u63d0\u4f9b\u5b89\u5168\u767b\u9304\uff09\u81f3\u95dc\u91cd\u8981\u3002&quot;,&quot;data&quot;:{&quot;items&quot;:[{&quot;main&quot;:&quot;\u7b2c\u4e00\u65b9 Cookie \u63d0\u4f9b\u5546&quot;,&quot;text&quot;:&quot;\u7b2c\u4e00\u65b9 cookies \u7531\u7528\u6236\u8a2a\u554f\u7684\u7db2\u7ad9\uff08\u5373\u4e3b\u6a5f\u57df\u540d\uff09\u8a2d\u7f6e\u3002\u7db2\u7ad9\u901a\u5e38\u4f7f\u7528\u7b2c\u4e00\u65b9 cookies \u7528\u65bc\u8ddf\u8e2a\u8a2a\u5ba2\u5728\u7db2\u7ad9\u4e0a\u7684\u884c\u70ba\u4e26\u500b\u6027\u5316\u4ed6\u5011\u5728\u7db2\u7ad9\u4e0a\u7684\u700f\u89bd\u9ad4\u9a57\u4ee5\u53ca\u500b\u6027\u5316\u4ed6\u5011\u7684\u700f\u89bd\u9ad4\u9a57\u3002&quot;,&quot;list&quot;:[{&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;title&quot;:&quot;PHPSESSID&quot;,&quot;text&quot;:&quot;\u7db2\u7ad9\u7528\u65bc\u8fa8\u8b58\u7528\u6236\u7684session id&quot;},{&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;title&quot;:&quot;XSRF-TOKEN&quot;,&quot;text&quot;:&quot;\u7db2\u7ad9\u7528\u65bc\u9632\u6b62 CSRF\uff08Cross-Site Request Forgery\uff0c\u8de8\u7ad9\u8acb\u6c42\u507d\u9020\uff09\u653b\u64caTOKEN&quot;},{&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;title&quot;:&quot;web_session&quot;,&quot;text&quot;:&quot;\u7db2\u7ad9\u7528\u65bc\u8fa8\u8b58\u7528\u6236\u7684session id&quot;},{&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;title&quot;:&quot;_cfuvid&quot;,&quot;text&quot;:&quot;hubspot\u8868\u55ae\u670d\u52d9\u4f7f\u7528\u4e86Cloudflare\u670d\u52d9\uff0c\u7528\u65bc\u5340\u5206\u771f\u4eba\u8a2a\u5ba2\u6216\u6a5f\u5668\u4eba&quot;}]}]}},{&quot;key&quot;:&quot;analytics&quot;,&quot;state&quot;:false,&quot;disabled&quot;:false,&quot;title&quot;:&quot;\u7d71\u8a08\u5206\u6790&quot;,&quot;text&quot;:&quot;\u9019\u4e9b Cookies \u7d71\u8a08\/\u5206\u6790\u7db2\u7ad9\u4f7f\u7528\u60c5\u6cc1\uff0c\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u7db2\u7ad9\u9ad4\u9a57\u3002&quot;,&quot;data&quot;:{&quot;items&quot;:[{&quot;main&quot;:&quot;\u7b2c\u4e00\u65b9 Cookie \u63d0\u4f9b\u5546&quot;,&quot;text&quot;:&quot;\u7b2c\u4e00\u65b9 cookies \u7531\u7528\u6236\u8a2a\u554f\u7684\u7db2\u7ad9\uff08\u5373\u4e3b\u6a5f\u57df\u540d\uff09\u8a2d\u7f6e\u3002\u7db2\u7ad9\u901a\u5e38\u4f7f\u7528\u7b2c\u4e00\u65b9 cookies \u7528\u65bc\u8ddf\u8e2a\u8a2a\u5ba2\u5728\u7db2\u7ad9\u4e0a\u7684\u884c\u70ba\u4e26\u500b\u6027\u5316\u4ed6\u5011\u5728\u7db2\u7ad9\u4e0a\u7684\u700f\u89bd\u9ad4\u9a57\u4ee5\u53ca\u500b\u6027\u5316\u4ed6\u5011\u7684\u700f\u89bd\u9ad4\u9a57\u3002&quot;,&quot;list&quot;:[{&quot;title&quot;:&quot;gtm&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;},{&quot;title&quot;:&quot;ga&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;_ga&quot;,&quot;text&quot;:&quot;\u7528\u4f86\u5340\u5206\u4e0d\u540c\u4f7f\u7528\u8005\u3002\u6bcf\u500b\u4f7f\u7528\u8005\u6703\u88ab\u6307\u6d3e\u552f\u4e00\u7684 ID&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;13 \u500b\u6708&quot;},{&quot;title&quot;:&quot;_ga_\u003Ccontainer-id\u003E&quot;,&quot;text&quot;:&quot;\u8ffd\u8e64\u55ae\u4e00\u5c6c\u6027 (Property) \u7684\u8cc7\u6599\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;13 \u500b\u6708&quot;}]}]}]}},{&quot;key&quot;:&quot;functionality&quot;,&quot;state&quot;:false,&quot;disabled&quot;:false,&quot;title&quot;:&quot;\u529f\u80fd\/\u504f\u597d&quot;,&quot;text&quot;:&quot;\u9019\u4e9b Cookies \u7d00\u9304\u60a8\u7684\u504f\u597d\u6216\u63d0\u4f9b\u66f4\u597d\u7684\u9ad4\u9a57&quot;,&quot;data&quot;:{&quot;items&quot;:[{&quot;main&quot;:&quot;\u7b2c\u4e09\u65b9 Cookie \u63d0\u4f9b\u5546&quot;,&quot;text&quot;:&quot;\u7b2c\u4e09\u65b9 cookies \u662f\u7531\u5176\u4ed6\u7b2c\u4e09\u65b9\u5be6\u9ad4\u653e\u7f6e\u5728\u7db2\u7ad9\u4e0a\uff0c\u800c\u4e0d\u662f\u7db2\u7ad9\u672c\u8eab\uff0c\u5373\u4f7f\u7528\u8005\u5728\u5730\u5740\u6b04\u4e2d\u770b\u5230\u7684\u7db2\u57df\u4ee5\u5916\u7684\u57df\u540d\u3002\u7b2c\u4e09\u65b9 cookies \u901a\u5e38\u8ddf\u8e2a\u8a2a\u5ba2\u5728\u7db2\u7ad9\u4e4b\u9593\u7684\u884c\u70ba\uff0c\u901a\u5e38\u7528\u65bc\u91dd\u5c0d\u6027\u5ee3\u544a\u76ee\u7684\u3002&quot;,&quot;list&quot;:[{&quot;title&quot;:&quot;googlefont&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;},{&quot;title&quot;:&quot;Vimeo&quot;,&quot;text&quot;:&quot;https:\/\/vimeo.com\/&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;_cf_bm&quot;,&quot;text&quot;:&quot;Cloudflare bot manager manages incoming traffic that matches the criteria associated with bots.&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;30 \u5206&quot;},{&quot;title&quot;:&quot;_cfuvid&quot;,&quot;text&quot;:&quot;Cloudflare cookie used to enforce rate-limiting rules.&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;session&quot;}]},{&quot;title&quot;:&quot;Tiktok&quot;,&quot;text&quot;:&quot;https:\/\/www.tiktok.com\/zh-Hant-TW\/&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;_ttp&quot;,&quot;text&quot;:&quot;To measure and improve the performance of advertising campaigns and to personalize the customer experience (including ads) on TikTok.&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;13 \u500b\u6708&quot;},{&quot;title&quot;:&quot;msToken&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u5b58\u5132\u7528\u6236\u7684\u6703\u8a71\u4ee4\u724c\uff0c\u78ba\u4fdd\u7528\u6236\u5728\u700f\u89bd\u5668\u6703\u8a71\u671f\u9593\u4fdd\u6301\u767b\u9304\u72c0\u614b\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;10 \u5929&quot;},{&quot;title&quot;:&quot;passport_csrf_token&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u9632\u6b62\u8de8\u7ad9\u8acb\u6c42\u507d\u9020\uff08CSRF\uff09\u653b\u64ca\uff0c\u78ba\u4fdd\u8acb\u6c42\u7684\u5408\u6cd5\u6027\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;60 \u5929&quot;},{&quot;title&quot;:&quot;ttwid&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u8b58\u5225\u7528\u6236\u7684\u552f\u4e00 ID\uff0c\u5354\u52a9 TikTok \u9032\u884c\u7528\u6236\u8b58\u5225\u8207\u9632\u6b62\u6a5f\u5668\u4eba\u884c\u70ba\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;10 \u5929&quot;}]}]}]}},{&quot;key&quot;:&quot;advertising&quot;,&quot;state&quot;:false,&quot;disabled&quot;:false,&quot;title&quot;:&quot;\u5ee3\u544a\u8207\u884c\u92b7&quot;,&quot;text&quot;:&quot;\u9019\u4e9b Cookies \u70ba\u8de8\u7db2\u7ad9\u8ffd\u8e64\u3001\u5ee3\u544a\u518d\u884c\u92b7&quot;,&quot;data&quot;:{&quot;items&quot;:[{&quot;main&quot;:&quot;\u7b2c\u4e09\u65b9 Cookie \u63d0\u4f9b\u5546&quot;,&quot;text&quot;:&quot;\u7b2c\u4e09\u65b9 cookies \u662f\u7531\u5176\u4ed6\u7b2c\u4e09\u65b9\u5be6\u9ad4\u653e\u7f6e\u5728\u7db2\u7ad9\u4e0a\uff0c\u800c\u4e0d\u662f\u7db2\u7ad9\u672c\u8eab\uff0c\u5373\u4f7f\u7528\u8005\u5728\u5730\u5740\u6b04\u4e2d\u770b\u5230\u7684\u7db2\u57df\u4ee5\u5916\u7684\u57df\u540d\u3002\u7b2c\u4e09\u65b9 cookies \u901a\u5e38\u8ddf\u8e2a\u8a2a\u5ba2\u5728\u7db2\u7ad9\u4e4b\u9593\u7684\u884c\u70ba\uff0c\u901a\u5e38\u7528\u65bc\u91dd\u5c0d\u6027\u5ee3\u544a\u76ee\u7684\u3002&quot;,&quot;list&quot;:[{&quot;title&quot;:&quot;Youtube&quot;,&quot;text&quot;:&quot;https:\/\/www.youtube.com\/&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;YSC&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u8ffd\u8e64\u5f71\u7247\u64ad\u653e\u6703\u8a71\uff08Session cookie\uff09\uff0c\u4e0d\u542b\u500b\u8cc7\u4f46\u53ef\u8b58\u5225\u55ae\u6b21\u89c0\u770b\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;session&quot;},{&quot;title&quot;:&quot;__Secure-YNID&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;6 \u500b\u6708&quot;},{&quot;title&quot;:&quot;__Secure-ROLLOUT_TOKEN&quot;,&quot;text&quot;:&quot;\u7528\u65bc YouTube \/ Google \u529f\u80fd\u7684\u300c\u7248\u672c\u6efe\u52d5 \/ \u6e2c\u8a66\u529f\u80fd\u63a7\u5236\u300d\uff08feature rollout \/ A\/B \u6e2c\u8a66\u63a7\u5236\uff09&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;6 \u500b\u6708&quot;},{&quot;title&quot;:&quot;VISITOR_INFO1_LIVE&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u8861\u91cf\u4f7f\u7528\u8005\u7684\u7db2\u8def\u983b\u5bec \/ \u5224\u65b7\u662f\u5426\u4f7f\u7528\u65b0\u7248\u6216\u820a\u7248 YouTube \u64ad\u653e\u5668\u4ecb\u9762\uff0c\u4ee5\u53ca\u67d0\u4e9b YouTube \u670d\u52d9\u4e2d\u7528\u65bc\u89c0\u5bdf\u4f7f\u7528\u8005\u8a2d\u5099 \/ \u6d41\u91cf\u80fd\u529b&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;6 \u500b\u6708&quot;},{&quot;title&quot;:&quot;VISITOR_PRIVACY_METADATA&quot;,&quot;text&quot;:&quot;\u7528\u4f86\u5132\u5b58\u4f7f\u7528\u8005\u5728\u7576\u524d\u7db2\u57df\uff08domain\uff09\u4e0a\u5c0d cookie \u540c\u610f \/ \u96b1\u79c1\u8a2d\u5b9a\u72c0\u614b\u7684\u5143\u8cc7\u6599\uff08metadata\uff09&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;6 \u500b\u6708&quot;},{&quot;title&quot;:&quot;__Secure-YEC&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u6aa2\u6e2c\u6feb\u7528\u3001\u5ee3\u544a\u6b3a\u8a50\u3001Bot \u5075\u6e2c\uff0c\u4ee5\u53ca\u6539\u5584 YouTube \u670d\u52d9\uff08\u5982\u8207\u5f71\u7247\u64ad\u653e\uff0f\u5d4c\u5165\u6709\u95dc\u7684\u5206\u6790\u6216\u554f\u984c\u5075\u6e2c\uff09&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;13 \u500b\u6708&quot;}]},{&quot;title&quot;:&quot;meta&quot;,&quot;text&quot;:&quot;https:\/\/www.facebook.com\/privacy\/policy&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;_fbp&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u8fa8\u8b58\u7db2\u7ad9\u8a2a\u5ba2\uff0c\u8b93 Meta \u53ef\u4ee5\u986f\u793a\u76f8\u95dc\u5ee3\u544a\uff08\u518d\u884c\u92b7\u7528\u9014\uff09\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;90 \u5929&quot;}]},{&quot;title&quot;:&quot;instagram&quot;,&quot;text&quot;:&quot;https:\/\/www.instagram.com\/&quot;,&quot;key&quot;:&quot;&quot;,&quot;state&quot;:&quot;&quot;,&quot;detail&quot;:[{&quot;title&quot;:&quot;mid&quot;,&quot;text&quot;:&quot;\u7528\u65bc\u5340\u5206\u4e0d\u540c\u7684\u7528\u6236\uff0c\u5354\u52a9 Instagram \u63d0\u4f9b\u500b\u6027\u5316\u7684\u5167\u5bb9\u63a8\u85a6\u3001\u5ee3\u544a\u5c55\u793a\u4ee5\u53ca\u5176\u4ed6\u793e\u4ea4\u5a92\u9ad4\u529f\u80fd\u3002&quot;,&quot;typeTitle&quot;:&quot;\u6642\u9593 \/ \u985e\u578b&quot;,&quot;typeDesc&quot;:&quot;13 \u500b\u6708&quot;}]}]}]}}]}]">
40
+ input(input type="hidden" id="_wcookie" value='')
@@ -79,7 +79,7 @@ block content
79
79
  h3 Video4 👇
80
80
  .row
81
81
  .grid
82
- .photo-box(video-target video-id="5bMdjkfvONE" video-type="youtube")
82
+ .photo-box(video-target video-id="" video-type="youtube")
83
83
  picture
84
84
  source(srcset="https://cdn.wdd.idv.tw/image/video-cover01.webp" type="image/webp")
85
85
  source(srcset="https://cdn.wdd.idv.tw/image/video-cover01.jpg" type="image/jpeg")