(function(){ // 后台媒体选择交互脚本 用于在设置页选择媒体项目 function initAdminMedia(){ // 将后台资源映射注入到前端设置对象 以便 shapes 脚本复用 if (typeof window !== 'undefined'){ // 条件判断 如果前端设置对象不存在则创建 window.YooneSnowSettings = window.YooneSnowSettings || {}; // 条件判断 若未设置 assetsMap 且后台提供则注入 if (!window.YooneSnowSettings.assetsMap && window.YooneSnowAdmin && window.YooneSnowAdmin.assetsMap){ window.YooneSnowSettings.assetsMap = window.YooneSnowAdmin.assetsMap; } } // 条件判断 如果没有媒体按钮则不执行 var addMediaUnifiedButton = document.getElementById('yooneSnowAddMediaUnified'); var listContainer = null; var emojiListContainer = null; var emojiInput = document.getElementById('yooneSnowAddEmojiInput'); var emojiAddButton = document.getElementById('yooneSnowAddEmoji'); var emojiSuggestBox = document.getElementById('yooneSnowEmojiSuggest'); var shapeListContainer = document.getElementById('yooneSnowShapeList'); var shapeAddSelect = document.getElementById('yooneSnowAddShapeSelect'); var shapeAddButton = document.getElementById('yooneSnowAddShapeBtn'); var emojiSelect = document.getElementById('yooneSnowEmojiSelect'); var typeSelect = document.getElementById('yooneSnowAddTypeSelect'); var paneDefault = document.getElementById('yooneSnowAddDefaultPane'); var paneEmoji = document.getElementById('yooneSnowAddEmojiPane'); var paneMedia = document.getElementById('yooneSnowAddMediaPane'); // 统一列表容器为形状列表容器 旧容器不再使用 listContainer = shapeListContainer; emojiListContainer = shapeListContainer; if (!shapeListContainer) { return; } // 权重输入已整合到卡片内部 旧容器不再使用 // 形状标签映射用于显示友好名称 var shapeLabelsMap = { dot: 'Dot', flake: 'Snowflake', yuanbao: 'Yuanbao', coin: 'Coin', santa_hat: 'Santa Hat', candy_cane: 'Candy Cane', christmas_sock: 'Christmas Sock', christmas_tree: 'Christmas Tree', reindeer: 'Reindeer', christmas_berry: 'Christmas Berry' }; // 创建形状预览元素 直接复用 shapes 渲染器 实现一致预览 function createShapePreviewElement(shapeKey){ var registry = (typeof window !== 'undefined' && window.YooneSnowShapeRenderers) ? window.YooneSnowShapeRenderers : {}; var renderer = registry[shapeKey]; // 条件判断 如果找到渲染器则使用 canvas 调用渲染函数 if (typeof renderer === 'function'){ var canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = 32; canvas.style.marginRight = '6px'; // 使用中性灰色背景提升可读性 canvas.style.backgroundColor = '#e6e6e6'; canvas.style.border = '1px solid #ddd'; canvas.style.borderRadius = '4px'; var ctx = canvas.getContext('2d'); // 根据形状选择更合适的基础尺寸 保证预览清晰 var previewBaseSizeMap = { dot: 6, flake: 6, yuanbao: 3, coin: 3.5, santa_hat: 2.8, candy_cane: 2.8, christmas_sock: 2.8, christmas_tree: 2.8, reindeer: 2.8, christmas_berry: 2.8 }; var baseSize = previewBaseSizeMap[shapeKey] || 3.2; // 调用渲染器 使用画布中心作为位置 try { renderer(ctx, 16, 16, baseSize); } catch(e){} // 对基于圖像的形狀進行補繪 當圖像加載完成時重試一次 var assets = (window.YooneSnowSettings && window.YooneSnowSettings.assetsMap) ? window.YooneSnowSettings.assetsMap : {}; var url = assets[shapeKey]; if (url && typeof window.YooneSnowGetOrLoadImage === 'function'){ var record = window.YooneSnowGetOrLoadImage(url); if (!record || !record.ready){ var tries = 0; var timer = setInterval(function(){ tries++; var check = window.YooneSnowGetOrLoadImage(url); if (check && check.ready){ try { ctx.clearRect(0,0,canvas.width,canvas.height); renderer(ctx, 16, 16, 2.5); } catch(err){} clearInterval(timer); } if (tries > 50){ clearInterval(timer); } }, 100); } } return canvas; } // 条件判断 若无渲染器则回退为资源图预览 保证可见性 if (typeof window !== 'undefined' && window.YooneSnowAdmin && window.YooneSnowAdmin.assetsMap && window.YooneSnowAdmin.assetsMap[shapeKey]){ var img = document.createElement('img'); img.src = String(window.YooneSnowAdmin.assetsMap[shapeKey]); img.alt = shapeKey; img.style.width = '28px'; img.style.height = '28px'; img.style.objectFit = 'contain'; img.style.marginRight = '6px'; img.style.backgroundColor = '#e6e6e6'; img.style.border = '1px solid #ddd'; img.style.borderRadius = '4px'; return img; } // 默认降级 使用文本首字母表示 var fallback = document.createElement('canvas'); fallback.width = 32; fallback.height = 32; fallback.style.marginRight = '6px'; var fctx = fallback.getContext('2d'); // 使用中性灰色背景提升可读性 fallback.style.backgroundColor = '#e6e6e6'; fallback.style.border = '1px solid #ddd'; fallback.style.borderRadius = '4px'; fctx.fillStyle = '#555'; fctx.font = '12px system-ui'; fctx.textAlign = 'center'; fctx.textBaseline = 'middle'; var txt = (shapeKey || 'S').substring(0, 1).toUpperCase(); fctx.fillText(txt, 16, 16); return fallback; } // 初始化时为现有卡片补齐预览 // 为现有形状卡片填充预览 保证刷新页面后仍可见 function renderExistingShapeCardPreviews(){ if (!shapeListContainer) { return; } var items = shapeListContainer.querySelectorAll('.yoone-snow-shape-item'); items.forEach(function(item){ var key = item.getAttribute('data-shape-key'); var host = item.querySelector('.yoone-snow-shape-preview'); if (!host) { return; } // 条件判断 若已有内容则跳过 避免重复渲染 if (host.childNodes && host.childNodes.length > 0) { return; } var previewEl = createShapePreviewElement(key); if (previewEl) { host.appendChild(previewEl); } }); } renderExistingShapeCardPreviews(); // 添加形状卡片 并插入预览与隐藏输入 用于保存选择 function addShapeBox(shapeKey){ if (!shapeListContainer) { return; } var key = String(shapeKey); if (key.trim() === '') { return; } var exist = shapeListContainer.querySelector('.yoone-snow-shape-item[data-shape-key="' + key + '"]'); if (exist) { return; } var wrapper = document.createElement('div'); wrapper.className = 'yoone-snow-shape-item'; wrapper.setAttribute('data-shape-key', key); wrapper.style.border = '1px solid #ddd'; wrapper.style.padding = '8px'; wrapper.style.display = 'flex'; wrapper.style.flexDirection = 'column'; wrapper.style.alignItems = 'center'; wrapper.style.minWidth = '96px'; var previewHost = document.createElement('div'); previewHost.className = 'yoone-snow-shape-preview'; previewHost.style.width = '32px'; previewHost.style.height = '32px'; previewHost.style.marginBottom = '6px'; previewHost.style.backgroundColor = '#e6e6e6'; previewHost.style.border = '1px solid #ddd'; previewHost.style.borderRadius = '4px'; var nameSpan = document.createElement('span'); nameSpan.textContent = shapeLabelsMap[key] || key; var weightInput = document.createElement('input'); weightInput.type = 'number'; weightInput.min = '0'; weightInput.name = 'yoone_snow_shape_weights[' + key + ']'; weightInput.value = '1'; weightInput.style.width = '120px'; weightInput.style.marginTop = '6px'; var input = document.createElement('input'); input.type = 'hidden'; input.name = 'yoone_snow_mixed_items[]'; input.value = key; var cancelBtn = document.createElement('button'); cancelBtn.type = 'button'; cancelBtn.className = 'button yoone-snow-cancel-shape'; cancelBtn.textContent = 'Cancel'; cancelBtn.style.marginTop = '6px'; wrapper.appendChild(previewHost); wrapper.appendChild(nameSpan); wrapper.appendChild(weightInput); wrapper.appendChild(input); wrapper.appendChild(cancelBtn); shapeListContainer.appendChild(wrapper); var previewEl = createShapePreviewElement(key); if (previewEl) { previewHost.appendChild(previewEl); } } // 绑定移除按钮事件 使用事件委托处理动态元素 shapeListContainer.addEventListener('click', function(event){ // 条件判断 如果点击的是移除按钮则执行删除 var target = event.target; if (target && target.classList.contains('yoone-snow-remove-media')){ var item = target.closest('.yoone-snow-media-item'); if (item) { var aidStr = item.getAttribute('data-attachment-id'); item.remove(); // 媒体权重输入已在卡片内 无需额外删除 } } }); // 綁定移除 emoji 按鈕事件 使用事件委託處理動態元素 if (shapeListContainer){ shapeListContainer.addEventListener('click', function(event){ var target = event.target; if (target && target.classList.contains('yoone-snow-remove-emoji')){ var item = target.closest('.yoone-snow-emoji-item'); if (item){ var ch = item.getAttribute('data-emoji-char'); item.remove(); // Emoji 权重输入已在卡片内 无需额外删除 } } }); } // 打开媒体选择器 支持多选 图片和 SVG if (addMediaUnifiedButton){ addMediaUnifiedButton.addEventListener('click', function(){ // 条件判断 如果 wp.media 不可用则终止 if (typeof wp === 'undefined' || !wp.media) { return; } var frame = wp.media({ title: 'Select images or SVG', multiple: true, library: { type: [ 'image', 'image/svg+xml' ] } }); frame.on('select', function(){ var selection = frame.state().get('selection'); selection.each(function(attachment){ var data = attachment.toJSON(); var id = data.id; var url = data.sizes && data.sizes.thumbnail ? data.sizes.thumbnail.url : data.url; // 创建预览项与隐藏输入 保存附件 ID var wrapper = document.createElement('div'); wrapper.className = 'yoone-snow-media-item'; wrapper.setAttribute('data-attachment-id', String(id)); wrapper.style.border = '1px solid #ddd'; wrapper.style.padding = '8px'; wrapper.style.display = 'flex'; wrapper.style.flexDirection = 'column'; wrapper.style.alignItems = 'center'; var img = document.createElement('img'); img.src = url; img.alt = 'media'; img.style.width = '72px'; img.style.height = '72px'; img.style.objectFit = 'contain'; var weightInput = document.createElement('input'); weightInput.type = 'number'; weightInput.min = '0'; weightInput.name = 'yoone_snow_media_weights[' + String(id) + ']'; weightInput.value = '1'; weightInput.style.width = '120px'; weightInput.style.marginTop = '6px'; var input = document.createElement('input'); input.type = 'hidden'; input.name = 'yoone_snow_media_items[]'; input.value = String(id); var removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'button yoone-snow-remove-media'; removeBtn.textContent = 'Remove'; removeBtn.style.marginTop = '6px'; wrapper.appendChild(img); wrapper.appendChild(weightInput); wrapper.appendChild(input); wrapper.appendChild(removeBtn); listContainer.appendChild(wrapper); }); }); frame.open(); }); } // Emoji 别名映射 用於文本搜索到 emoji 字符 var emojiAliasMap = { smile: '🙂', happy: '😊', grin: '😁', laugh: '😂', joy: '😂', wink: '😉', blush: '☺️', heart: '❤️', love: '❤️', star: '⭐', fire: '🔥', cool: '😎', cry: '😢', sad: '🙁', angry: '😠', thumbs_up: '👍', ok: '👌', clap: '👏', pray: '🙏', tada: '🎉', gift: '🎁', snow: '❄️', tree: '🌲', bell: '🔔', candy: '🍬', sock: '🧦', deer: '🦌', berry: '🍓' }; // 填充下拉選單 以別名和字符組合顯示 function populateEmojiSelect(){ if (!emojiSelect) { return; } // 清空除第一個提示項之外的選項 while (emojiSelect.options.length > 1){ emojiSelect.remove(1); } for (var key in emojiAliasMap){ var ch = emojiAliasMap[key]; var opt = document.createElement('option'); opt.value = ch; opt.textContent = ch + ' ' + key; emojiSelect.appendChild(opt); } } // 綁定下拉選擇事件 選擇後添加對應 emoji 並重置選中 if (emojiSelect){ populateEmojiSelect(); emojiSelect.addEventListener('change', function(){ var val = emojiSelect.value; if (String(val).trim() !== ''){ addEmojiByChar(val); emojiSelect.value = ''; } }); } // 顯示建議列表 根據查詢文本匹配別名 function showEmojiSuggestions(query){ if (!emojiSuggestBox) { return; } var q = String(query || '').toLowerCase().trim(); emojiSuggestBox.innerHTML = ''; if (q === '') { return; } var max = 12; var count = 0; for (var key in emojiAliasMap){ if (key.indexOf(q) !== -1){ var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'button'; btn.textContent = emojiAliasMap[key] + ' ' + key; (function(ch){ btn.addEventListener('click', function(){ addEmojiByChar(ch); }); })(emojiAliasMap[key]); emojiSuggestBox.appendChild(btn); count++; if (count >= max){ break; } } } } // 將 emoji 字符添加到列表 並創建隱藏輸入 function addEmojiByChar(ch){ if (!shapeListContainer) { return; } var key = String(ch); if (key.trim() === '') { return; } var exist = shapeListContainer.querySelector('.yoone-snow-emoji-item[data-emoji-char="' + key + '"]'); if (exist) { return; } var wrapper = document.createElement('div'); wrapper.className = 'yoone-snow-emoji-item'; wrapper.setAttribute('data-emoji-char', key); wrapper.style.border = '1px solid #ddd'; wrapper.style.padding = '8px'; wrapper.style.display = 'flex'; wrapper.style.flexDirection = 'column'; wrapper.style.alignItems = 'center'; var preview = document.createElement('div'); preview.textContent = key; preview.style.fontSize = '28px'; preview.style.lineHeight = '32px'; preview.style.backgroundColor = '#e6e6e6'; preview.style.border = '1px solid #ddd'; preview.style.borderRadius = '4px'; preview.style.width = '32px'; preview.style.height = '32px'; preview.style.display = 'flex'; preview.style.alignItems = 'center'; preview.style.justifyContent = 'center'; var weightInput = document.createElement('input'); weightInput.type = 'number'; weightInput.min = '0'; weightInput.name = 'yoone_snow_emoji_weights[' + key + ']'; weightInput.value = '1'; weightInput.style.width = '120px'; weightInput.style.marginTop = '6px'; var input = document.createElement('input'); input.type = 'hidden'; input.name = 'yoone_snow_emoji_items[]'; input.value = key; var removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'button yoone-snow-remove-emoji'; removeBtn.textContent = 'Cancel'; removeBtn.style.marginTop = '6px'; wrapper.appendChild(preview); wrapper.appendChild(weightInput); wrapper.appendChild(input); wrapper.appendChild(removeBtn); shapeListContainer.appendChild(wrapper); } // 綁定 emoji 添加按鈕 點擊後優先按別名匹配 否則直接添加字符 if (emojiAddButton){ emojiAddButton.addEventListener('click', function(){ var q = emojiInput ? String(emojiInput.value || '') : ''; var trimmed = q.trim(); if (trimmed === '') { return; } var lower = trimmed.toLowerCase(); if (emojiAliasMap[lower]){ addEmojiByChar(emojiAliasMap[lower]); } else { addEmojiByChar(trimmed); } }); } // 監聽輸入變化 顯示建議列表 支持即時搜索 if (emojiInput){ emojiInput.addEventListener('input', function(){ showEmojiSuggestions(emojiInput.value); }); emojiInput.addEventListener('keydown', function(e){ if (e && e.key === 'Enter'){ e.preventDefault(); var q = String(emojiInput.value || '').trim(); if (q !== ''){ var lower = q.toLowerCase(); if (emojiAliasMap[lower]){ addEmojiByChar(emojiAliasMap[lower]); } else { addEmojiByChar(q); } } } }); } if (shapeListContainer){ shapeListContainer.addEventListener('click', function(event){ var target = event.target; if (target && target.classList.contains('yoone-snow-cancel-shape')){ var item = target.closest('.yoone-snow-shape-item'); if (item){ var key = item.getAttribute('data-shape-key'); item.remove(); } } }); } if (shapeAddSelect){ shapeAddSelect.addEventListener('change', function(){ var val = shapeAddSelect.value; if (String(val).trim() !== ''){ addShapeBox(val); shapeAddSelect.value = ''; } }); } if (shapeAddButton){ shapeAddButton.addEventListener('click', function(){ var val = shapeAddSelect ? String(shapeAddSelect.value || '') : ''; if (val.trim() !== ''){ addShapeBox(val); shapeAddSelect.value = ''; } }); } // 切換類型面板 顯示對應控件 其他隱藏 function updateTypePane(){ var t = typeSelect ? String(typeSelect.value || '') : ''; if (!paneDefault || !paneEmoji || !paneMedia) { return; } paneDefault.style.display = (t === 'default') ? 'flex' : 'none'; paneEmoji.style.display = (t === 'emoji') ? 'flex' : 'none'; paneMedia.style.display = (t === 'media') ? 'flex' : 'none'; } if (typeSelect){ typeSelect.addEventListener('change', updateTypePane); updateTypePane(); } } // 条件判断 如果文档尚未加载则等待 DOMContentLoaded 事件 if (document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', initAdminMedia); } else { initAdminMedia(); } })();