511 lines
20 KiB
JavaScript
511 lines
20 KiB
JavaScript
(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();
|
|
}
|
|
})();
|