eddie32的KFOL表情插件的魔改分支

Github页面

稳定版下载(基于喵拉分支):

Github源下载

度盘下载 (已过时,待更新)

测试版下载(基于eddie32最新5.2.1):

Github源下载

稳定版代码复制

// ==UserScript==
// @name        绯月表情增强插件*改
// @namespace   https://github.com/HazukiKaguya/KFOL_Stickers
// @version     0.0.6
// @author      HazukiKaguya
// @description KF论坛专用的回复表情,插图扩展插件,在发帖时快速输入自定义表情和论坛BBCODE
// @icon        https://sticker.inari.site/favicon.ico
// @homepage    https://mistakey.top/KFStickers
// @include     http*://*2dkf.com/*
// @include     http*://*9moe.com/*
// @include     https://*kforz.com/*
// @include     https://*kfmax.com/*
// @include     http*://*kfgal.com/*
// @include     https://*miaola.info/*
// @copyright   2020-2021, Hazukikaguya
// @grant       none
// @license     MIT
// @run-at      document-end
// @updateURL   https://github.com/HazukiKaguya/KFOL_Stickers/raw/master/es6_KfStickers.user.js
// ==/UserScript==
//eddie32大佬的KFOL助手的表情插件的分支,目前基于5.1.3版本的喵拉分支 @copyright   2014-2019, eddie32 https://greasyfork.org/users/5415
//0.0.6 表情贴纸旧域名替换为新域名的功能现在仅对<img>进行操作,避免因为修改innerHTML造成事件绑定失效
//0.0.5 更改表情贴纸域名,增加表情贴纸旧域名替换为新域名的功能
//历史更新:0.0.4 url添加kfmax,优化注释;0.0.3 贴纸更新贴吧,微博等;0.0.2 贴纸更新eddie32佬的伪中国语和流行(直接使用eddie32源);0.0.1 替换失效贴纸,常用替换为小日向雪花,bilibili替换为林大B
'use strict';
// 版本号
const version = '0.0.6';
// 网站是否为KfMobile
const isKfMobile = typeof Info !== 'undefined' && typeof Info.imgPath !== 'undefined';
// 表情贴纸旧域名替换为新域名
var x = document.getElementsByTagName("img");
var i;
for (i = 0; i < x.length; i++) {
   x[i].src=x[i].src.replace(/mistake.tech\/emote/g, "sticker.inari.site");
   x[i].src=x[i].src.replace(/http:\/\/o6smnd6uw.bkt.clouddn.com\/xds3\/akari/g, "https://sticker.inari.site/akarin/akarin");//实验性功能,此储存桶地址的表情贴纸很可能和修复后的表情贴纸并不能一一对应。
}

// 灰企鹅
const KfSmileList = [];
const KfSmileCodeList = [];
let kfImgPath = typeof imgpath !== 'undefined' ? imgpath : '';
if (isKfMobile) kfImgPath = Info.imgPath;
for (let i = 1; i < 49; i++) {
    KfSmileList.push(`/${kfImgPath}/post/smile/em/em${(i) > 9 ? i : ('0' + i)}.gif`);
    KfSmileCodeList.push(`[s:${i + 9}]`);
}

// 小日向雪花
const YukikaSmileList = [];
for (let i = 1; i < 7; i++) {
    YukikaSmileList.push(`https://sticker.inari.site/yukika/${i}.jpg`);
}
for (let i = 21; i < 24; i++) {
    YukikaSmileList.push(`https://sticker.inari.site/yukika/${i}.jpg`);
}

// AC娘表情
const AcSmileList = [];
for (let i = 1; i < 55; i++) {
    AcSmileList.push(`https://sticker.inari.site/acfun/1/${i}.png`);
}
for (let i = 1001; i < 1041; i++) {
    AcSmileList.push(`https://sticker.inari.site/acfun/2/${i}.png`);
}
for (let i = 2001; i < 2056; i++) {
    AcSmileList.push(`https://sticker.inari.site/acfun/3/${i}.png`);
}

// 百度贴吧
const BaiduSmileList = [];
for(let i = 1; i < 10; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/face/i_f0${i}.png`);
}
for(let i = 10; i < 56; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/face/i_f${i}.png`);
}
for(let i = 1; i < 10; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/ali/ali_00${i}.gif`);
}
for(let i = 10; i < 71; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/ali/ali_0${i}.gif`);
}

// 微博
const WeiboSmileList = [];
for (let i = 0; i < 101; i++) {
    WeiboSmileList.push(`https://sticker.inari.site/weibo/${i}.png`);
}

// 东方
const TouhouSmileList = [];
for (let i = 1; i < 46; i++) {
    TouhouSmileList.push(`https://sticker.inari.site/touhou/reimu/${i}.jpg`);
}


// 阿卡林 from 摇曳百合
const AkarinSmileList = [];
for (let i = 1; i < 21; i++) {
    AkarinSmileList.push(`https://sticker.inari.site/akarin/2/akarin (${i}).gif`);
}
for (let i = 1; i < 72; i++) {
    AkarinSmileList.push(`https://sticker.inari.site/akarin/1/akarin (${i}).png`);
}

// 林大B
const lindaBSmileList = [];
for (let i = 1; i < 52; i++) {
    lindaBSmileList.push(`https://sticker.inari.site/lindaB/lindaB (${i}).jpg`);
}

// lovelive表情
const LoveliveSmallSmileList = [];
for (let i = 1; i < 42; i++) {
    LoveliveSmallSmileList.push(`https://sticker.inari.site/lovelive/2/ll (${i}).png`);
}
for (let i = 0; i < 38; i++) {
    LoveliveSmallSmileList.push(`https://sticker.inari.site/lovelive/4/ll (${i}).jpg`);
}

// 少女歌剧
const RevstarSmileList = [];
for (let i = 1; i < 41; i++) {
    RevstarSmileList.push(`https://sticker.inari.site/revstar/revstar (${i}).png`);
}

// BanG Dream
const BandoriSmileList = [];
for (let i = 1; i < 41; i++) {
    BandoriSmileList.push(`https://sticker.inari.site/bangdream/bangdream (${i}).png`);
}

// 伪中国语(eddie32)
const FakeCHSSmileList = [];
for (let i = 49; i < 83; i++) {
    FakeCHSSmileList.push(`https://ecs32.top/emotions/selected/2-20190811/sticker (${i}).png`);
}

// 流行(eddie32)
const PopularSmileList = [];
for (let i = 1; i < 48; i++) {
    PopularSmileList.push(`https://ecs32.top/emotions/selected/1-20190811/sticker (${i}).png`);
}


/**
 * 表情菜单
 */
const MenuList = {
    KfSmile: {datatype: 'imageLink', title: 'KF自带', addr: KfSmileList, ref: KfSmileCodeList},
    Shortcut: {
        datatype: 'plain',
        title: '快捷',
        addr: [
            '[sell=100][/sell]', '[quote][/quote]', '[hide=100][/hide]', '[code][/code]', '[strike][/strike]', '[fly][/fly]',
            '[color=#00FF00][/color]', '[b][/b]', '[u][/u]', '[i][/i]', '[hr]', '[backcolor=][/backcolor]', '[url=][/url]', '[img][/img]'
        ],
        ref: [
            '出售贴sell=售价', '引用', '隐藏hide=神秘等级', '插入代码', '删除线', '跑马灯', '文字颜色', '粗体', '下划线', '斜体', '水平线', '背景色', '插入链接', '插入图片'
        ]
    },
    Emoji: {
        datatype: 'plain',
        title: '颜文字',
        addr: [
            '(●・ 8 ・●)', '╰(๑◕ ▽ ◕๑)╯', '(ゝω・)', '〜♪♪', '(゚Д゚≡゚Д゚)', '(^o^)ノ', '(|||゚Д゚)', '(`ε´ )', '(╬゚д゚)', '(|||゚д゚)', '( ̄∇ ̄)',
            '( ̄3 ̄)', '( ̄ー ̄)', '( ̄ .  ̄)', '( ̄︿ ̄)', '( ̄︶ ̄)', '(*´ω`*)', '(・ω・)', '(⌒▽⌒)', '( ̄▽ ̄)', '(=・ω・=)', '(`・ω・´)',
            '(〜 ̄△ ̄)〜', '(・∀・)', '(°∀°)ノ', '( ̄3 ̄)', '╮( ̄▽ ̄)╭', '( ´_ゝ`)', 'のヮの', '(ノ؂< ๑)诶嘿☆~', '(<_<)', '(>_>)',
            '(;¬_¬)', '(▔□▔)/', '(゚Д゚≡゚д゚)!?', 'Σ(゚д゚;)', 'Σ(  ̄□ ̄||)', '(´;ω;`)', '(/TДT)/', '(^・ω・^ )', '(。・ω・。)', '(● ̄(エ) ̄●)',
            'ε=ε=(ノ≧∇≦)ノ', '(´・_・`)', '(-_-#)', '( ̄へ ̄)', '( ̄ε(# ̄) Σ', 'ヽ(`Д´)ノ', '(╯°口°)╯(┴—┴', '(#-_-)┯━┯', '_(:3」∠)_', '(笑)',
            '(汗)', '(泣)', '(苦笑)', '(´・ω・`)', '(╯°□°)╯︵ ┻━┻', '(╯‵□′)╯︵┻━┻', '( ´ρ`)', '( ゚ω゚)', '(o゚ω゚o)', '( ^ω^)', '(。◕∀◕。)',
            '/( ◕‿‿◕ )\\', 'ε٩( º∀º )۶з', '( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)', '(●´3`)~♪', '_(:з」∠)_', 'хорошо!', '\(^o^)/', '(•̅灬•̅ )', '(゚Д゚)',
            'まったく、小学生は最高だぜ!!', 'ε=ε=ε=┏(゜ロ゜;)┛', '(;°ほ°)', '⎝≧⏝⏝≦⎠', 'ヽ(✿゚▽゚)ノ', '焔に舞い上がるスパークよ、邪悪な異性交際に、天罰を与え!',
            '|•ω•`)'
        ]
    },
    Yukika:   {datatype: 'image', title: '小日向雪花', addr: YukikaSmileList},
    Acfun:    {datatype: 'image', title: 'ACFUN', addr: AcSmileList},
    Akari:    {datatype: 'image', title: 'Akari', addr: AkarinSmileList},
    lindaB:   {datatype: 'image', title: '林大B', addr: lindaBSmileList},
    LoveLive: {datatype: 'image', title: 'LoveLive', addr: LoveliveSmallSmileList},
    Revstar:  {datatype: 'image', title: '少女歌剧', addr: RevstarSmileList},
    Bandori:  {datatype: 'image', title: '邦邦', addr: BandoriSmileList},
    Touhou:   {datatype: 'image', title: '东方', addr: TouhouSmileList},
    Baidu:    {datatype: 'image', title: '贴吧', addr: BaiduSmileList},
    Weibo:    {datatype: 'image', title: '微博', addr: WeiboSmileList},
    FakeCHS:  {datatype: 'image', title: '伪中国语', addr: FakeCHSSmileList},
    Popular:  {datatype: 'image', title: '流行', addr: PopularSmileList},
};

/**
 * 添加BBCode
 * @param textArea 文本框
 * @param {string} code BBCode
 * @param {string} selText 选择文本
 */
const addCode = function (textArea, code, selText = '') {
    let startPos = !selText ? (code.indexOf('[img]') > -1 || code.indexOf(']') < 0 ? code.length : code.indexOf(']') + 1) : code.indexOf(selText);
    if (typeof textArea.selectionStart !== 'undefined') {
        let prePos = textArea.selectionStart;
        textArea.value = textArea.value.substring(0, prePos) + code + textArea.value.substring(textArea.selectionEnd);
        textArea.selectionStart = prePos + startPos;
        textArea.selectionEnd = prePos + startPos + selText.length;
    }
    else {
        textArea.value += code;
    }
};

/**
 * 显示放大的表情图片
 * @param {jQuery} $img 表情图片对象
 */
const showZoomInImage = function ($img) {
    if ($img.get(0).naturalWidth <= $img.height()) return;
    let offset = $img.offset();
    let $zoomIn = $(`<img class="kfe-zoom-in" src="${$img.attr('src')}" alt="[预览图片]">`).appendTo('body');
    let windowWidth = $(window).width();
    let zoomInWidth = $zoomIn.outerWidth();
    let top = offset.top - $zoomIn.outerHeight() - 5;
    let left = offset.left + $img.width() / 2 - zoomInWidth / 2;
    if (left < 0) left = 0;
    else if (left + zoomInWidth > windowWidth) left = windowWidth - zoomInWidth;
    $zoomIn.css({top, left});
};

/**
 * 获取表情面板的HTML代码
 * @param {string} key 菜单关键字
 * @returns {string} 表情面板内容
 */
const getSmilePanelHtml = function (key) {
    let data = MenuList[key];
    if (!data) return '';
    let html = '';
    for (let i = 0; i < data.addr.length; i++) {
        if (data.datatype === 'image') {
            html += `<img class="kfe-smile" src="${data.addr[i]}" alt="[表情]">`;
        }
        else if (data.datatype === 'imageLink') {
            let ref = typeof data.ref !== 'undefined' && typeof data.ref[i] !== 'undefined' ? data.ref[i] : '';
            html += `<img class="kfe-smile" data-code="${ref}" src="${data.addr[i]}" alt="[表情]">`;
        }
        else if (data.datatype === 'plain') {
            let ref = typeof data.ref !== 'undefined' && typeof data.ref[i] !== 'undefined' ? data.ref[i] : data.addr[i];
            html += `<a class="kfe-smile-text" data-code="${data.addr[i]}" href="#">${ref}</a>`;
        }
    }
    return `<div class="kfe-smile-panel" data-key="${key}">${html}</div>`;
};

/**
 * 获取子菜单的HTML代码
 * @returns {string} 子菜单内容
 */
const getSubMenuHtml = function () {
    let html = '';
    $.each(MenuList, function (key, data) {
        html += `<a class="kfe-sub-menu" data-key="${key}" href="#" title="${data.title}">${data.title}</a>`;
    });
    return html;
};

/**
 * 创建容器
 * @param textArea 文本框
 */
const createContainer = function (textArea) {
    let $container = $(`
<div class="kfe-container">
  <div class="kfe-menu">
    <span title="本分支由mistakey维护,目前为eddie32佬插件的喵拉布丁分支的分支" style="cursor: pointer;"><b>:)</b></span>
    ${getSubMenuHtml()}
    <span class="kfe-close-panel">[-]</span>
  </div>
</div>
`).insertBefore($(textArea));
    $container.on('click', '.kfe-sub-menu', function (e) {
        e.preventDefault();
        let $this = $(this);
        let key = $this.data('key');
        if (!key) return;
        $container.find('.kfe-sub-menu').removeClass('kfe-sub-menu-active');
        $this.addClass('kfe-sub-menu-active');
        $container.find('.kfe-smile-panel').hide();
        let $panel = $container.find(`.kfe-smile-panel[data-key="${key}"]`);
        if ($panel.length > 0) $panel.show();
        else $(getSmilePanelHtml(key)).appendTo($container).show();
    }).on('click', '.kfe-smile, .kfe-smile-text', function (e) {
        e.preventDefault();
        let $this = $(this);
        let code = $this.data('code');
        if (!code) code = `[img]${$this.attr('src')}[/img]`;
        addCode(textArea, code);
        if (/(Mobile|MIDP)/i.test(navigator.userAgent)) textArea.blur();
        else textArea.focus();
    }).on('mouseenter', '.kfe-smile', function () {
        $('.kfe-zoom-in').remove();
        showZoomInImage($(this));
    }).on('mouseleave', '.kfe-smile', function () {
        $('.kfe-zoom-in').remove();
    }).find('.kfe-close-panel').click(function () {
        $container.find('.kfe-smile-panel').hide();
    });
};

/**
 * 添加CSS
 */
const appendCss = function () {
    $('head').append(`
<style>
  .kfe-container { padding: 5px; vertical-align: middle; font: 12px/1.7em "sans-serif"; }
  .kfe-menu { margin-bottom: 5px; }
  .kfe-sub-menu { margin: 0 7px; text-decoration: none; border-bottom: 2px solid transparent; }
  .kfe-sub-menu:hover, .kfe-sub-menu:focus { text-decoration: none; border-color: deeppink; }
  a.kfe-sub-menu-active { color: black }
  .kfe-smile-panel { display: none; height: 120px; padding: 5px 3px; overflow-y: auto; border-top: 1px solid #ddd; }
  .kfe-smile-panel[data-key="Shortcut"] { height: auto; }
  .kfe-smile { display: inline-block; max-width: 60px; max-height: 60px; cursor: pointer; }
  .kfe-smile-text { display: inline-block; padding: 3px 5px; }
  .kfe-smile-text:hover { color: #fff !important; background-color: #2b2b2b; text-decoration: none; }
  .kfe-close-panel { cursor: pointer; }
  .kfe-zoom-in {
    position: absolute; max-width: 150px; max-height: 150px; background-color: #fcfcfc; border: 3px solid rgba(242, 242, 242, 0.6);
    border-radius: 2px; box-shadow: 0 0 3px rgb(102, 102, 102);
  }
</style>
`);
    if (isKfMobile) {
        $('head').append(`
<style>
  #readPage .kfe-container, #writeMessagePage .kfe-container { margin-top: -10px; }
  .kfe-menu { white-space: nowrap; overflow-x: auto; }
</style>
`);
    }
};

/**
 * 初始化
 */
const init = function () {
    let $textAreas = $('textarea');
    if (!$textAreas.length) return;
    appendCss();
    $textAreas.each(function () {
        createContainer(this);
    });
};

init();
// ==UserScript==
// @name        绯月表情增强插件*改
// @namespace   https://github.com/HazukiKaguya/KFOL_Stickers
// @version     0.0.4
// @author      HazukiKaguya
// @description KF论坛专用的回复表情,插图扩展插件,在发帖时快速输入自定义表情和论坛BBCODE
// @icon        https://mistake.tech/emote/favicon.ico
// @homepage    https://mistakey.top/KFStickers
// @include     http*://*2dkf.com/*
// @include     http*://*9moe.com/*
// @include     https://*kforz.com/*
// @include     https://*kfmax.com/*
// @include     http*://*kfgal.com/*
// @include     https://*miaola.info/*
// @copyright   2020-2021, Hazukikaguya
// @grant       none
// @license     MIT
// @run-at      document-end
// @updateURL   https://github.com/HazukiKaguya/KFOL_Stickers/raw/master/es6_KfStickers.user.js
// ==/UserScript==
'use strict';
//eddie32大佬的KFOL助手的表情插件的分支,目前基于5.1.3.x版本 @copyright   2014-2019, eddie32 https://greasyfork.org/users/5415
//0.0.4 添加了kfmax站,优化了脚本相关信息
//0.0.3 表情更新了百度贴吧,新浪微博等
//0.0.2 表情更新了eddie32最新版的伪中国语和流行(直接使用了eddie32大佬的图片网址)
//0.0.1 替换了失效表情,常用替换为自截小日向雪花表情包,bilibili替换为林大B
// 版本号
const version = '0.0.4';
// 网站是否为KfMobile
const isKfMobile = typeof Info !== 'undefined' && typeof Info.imgPath !== 'undefined';

// 灰企鹅
const KfSmileList = [];
const KfSmileCodeList = [];
let kfImgPath = typeof imgpath !== 'undefined' ? imgpath : '';
if (isKfMobile) kfImgPath = Info.imgPath;
for (let i = 1; i < 49; i++) {
    KfSmileList.push(`/${kfImgPath}/post/smile/em/em${(i) > 9 ? i : ('0' + i)}.gif`);
    KfSmileCodeList.push(`[s:${i + 9}]`);
}

// 小日向雪花
const YukikaSmileList = [];
for (let i = 1; i < 7; i++) {
    YukikaSmileList.push(`https://mistake.tech/emote/yukika/${i}.jpg`);
}
for (let i = 21; i < 24; i++) {
    YukikaSmileList.push(`https://mistake.tech/emote/yukika/${i}.jpg`);
}

// AC娘表情
const AcSmileList = [];
for (let i = 1; i < 55; i++) {
    AcSmileList.push(`https://mistake.tech/emote/acfun/1/${i}.png`);
}
for (let i = 1001; i < 1041; i++) {
    AcSmileList.push(`https://mistake.tech/emote/acfun/2/${i}.png`);
}
for (let i = 2001; i < 2056; i++) {
    AcSmileList.push(`https://mistake.tech/emote/acfun/3/${i}.png`);
}

// 百度贴吧
const BaiduSmileList = [];
for(let i = 1; i < 10; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/face/i_f0${i}.png`);
}
for(let i = 10; i < 56; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/face/i_f${i}.png`);
}
for(let i = 1; i < 10; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/ali/ali_00${i}.gif`);
}
for(let i = 10; i < 71; i++) {
    BaiduSmileList.push(`http://tb2.bdstatic.com/tb/editor/images/ali/ali_0${i}.gif`);
}

// 微博
const WeiboSmileList = [];
for (let i = 0; i < 101; i++) {
    WeiboSmileList.push(`https://mistake.tech/emote/weibo/${i}.png`);
}

// 东方
const TouhouSmileList = [];
for (let i = 1; i < 46; i++) {
    TouhouSmileList.push(`https://mistake.tech/emote/touhou/reimu/${i}.jpg`);
}


// 阿卡林 from 摇曳百合
const AkarinSmileList = [];
for (let i = 1; i < 21; i++) {
    AkarinSmileList.push(`https://mistake.tech/emote/akarin/2/akarin (${i}).gif`);
}
for (let i = 1; i < 72; i++) {
    AkarinSmileList.push(`https://mistake.tech/emote/akarin/1/akarin (${i}).png`);
}

// 林大B
const lindaBSmileList = [];
for (let i = 1; i < 52; i++) {
    lindaBSmileList.push(`https://mistake.tech/emote/lindaB/lindaB (${i}).jpg`);
}

// lovelive表情
const LoveliveSmallSmileList = [];
for (let i = 1; i < 42; i++) {
    LoveliveSmallSmileList.push(`https://mistake.tech/emote/lovelive/2/ll (${i}).png`);
}
for (let i = 0; i < 38; i++) {
    LoveliveSmallSmileList.push(`https://mistake.tech/emote/lovelive/4/ll (${i}).jpg`);
}

// 少女歌剧
const RevstarSmileList = [];
for (let i = 1; i < 41; i++) {
    RevstarSmileList.push(`https://mistake.tech/emote/revstar/revstar (${i}).png`);
}

// BanG Dream
const BandoriSmileList = [];
for (let i = 1; i < 41; i++) {
    BandoriSmileList.push(`https://mistake.tech/emote/bangdream/bangdream (${i}).png`);
}

// 伪中国语(eddie32)
const FakeCHSSmileList = [];
for (let i = 1; i < 83; i++) {
    FakeCHSSmileList.push(`https://ecs32.top/emotions/selected/2-20190811/sticker (${i}).png`);
}

// 流行(eddie32)
const PopularSmileList = [];
for (let i = 1; i < 48; i++) {
    PopularSmileList.push(`https://ecs32.top/emotions/selected/1-20190811/sticker (${i}).png`);
}


/**
 * 表情菜单
 */
const MenuList = {
    KfSmile: {datatype: 'imageLink', title: 'KF自带', addr: KfSmileList, ref: KfSmileCodeList},
    Shortcut: {
        datatype: 'plain',
        title: '快捷',
        addr: [
            '[sell=100][/sell]', '[quote][/quote]', '[hide=100][/hide]', '[code][/code]', '[strike][/strike]', '[fly][/fly]',
            '[color=#00FF00][/color]', '[b][/b]', '[u][/u]', '[i][/i]', '[hr]', '[backcolor=][/backcolor]', '[url=][/url]', '[img][/img]'
        ],
        ref: [
            '出售贴sell=售价', '引用', '隐藏hide=神秘等级', '插入代码', '删除线', '跑马灯', '文字颜色', '粗体', '下划线', '斜体', '水平线', '背景色', '插入链接', '插入图片'
        ]
    },
    Emoji: {
        datatype: 'plain',
        title: '颜文字',
        addr: [
            '(●・ 8 ・●)', '╰(๑◕ ▽ ◕๑)╯', '(ゝω・)', '〜♪♪', '(゚Д゚≡゚Д゚)', '(^o^)ノ', '(|||゚Д゚)', '(`ε´ )', '(╬゚д゚)', '(|||゚д゚)', '( ̄∇ ̄)',
            '( ̄3 ̄)', '( ̄ー ̄)', '( ̄ .  ̄)', '( ̄︿ ̄)', '( ̄︶ ̄)', '(*´ω`*)', '(・ω・)', '(⌒▽⌒)', '( ̄▽ ̄)', '(=・ω・=)', '(`・ω・´)',
            '(〜 ̄△ ̄)〜', '(・∀・)', '(°∀°)ノ', '( ̄3 ̄)', '╮( ̄▽ ̄)╭', '( ´_ゝ`)', 'のヮの', '(ノ؂< ๑)诶嘿☆~', '(<_<)', '(>_>)',
            '(;¬_¬)', '(▔□▔)/', '(゚Д゚≡゚д゚)!?', 'Σ(゚д゚;)', 'Σ(  ̄□ ̄||)', '(´;ω;`)', '(/TДT)/', '(^・ω・^ )', '(。・ω・。)', '(● ̄(エ) ̄●)',
            'ε=ε=(ノ≧∇≦)ノ', '(´・_・`)', '(-_-#)', '( ̄へ ̄)', '( ̄ε(# ̄) Σ', 'ヽ(`Д´)ノ', '(╯°口°)╯(┴—┴', '(#-_-)┯━┯', '_(:3」∠)_', '(笑)',
            '(汗)', '(泣)', '(苦笑)', '(´・ω・`)', '(╯°□°)╯︵ ┻━┻', '(╯‵□′)╯︵┻━┻', '( ´ρ`)', '( ゚ω゚)', '(o゚ω゚o)', '( ^ω^)', '(。◕∀◕。)',
            '/( ◕‿‿◕ )\\', 'ε٩( º∀º )۶з', '( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)', '(●´3`)~♪', '_(:з」∠)_', 'хорошо!', '\(^o^)/', '(•̅灬•̅ )', '(゚Д゚)',
            'まったく、小学生は最高だぜ!!', 'ε=ε=ε=┏(゜ロ゜;)┛', '(;°ほ°)', '⎝≧⏝⏝≦⎠', 'ヽ(✿゚▽゚)ノ', '焔に舞い上がるスパークよ、邪悪な異性交際に、天罰を与え!',
            '|•ω•`)'
        ]
    },
	Yukika:   {datatype: 'image', title: '小日向雪花', addr: YukikaSmileList},
    Acfun:    {datatype: 'image', title: 'ACFUN', addr: AcSmileList},
    Akari:    {datatype: 'image', title: 'Akari', addr: AkarinSmileList},
    lindaB:   {datatype: 'image', title: '林大B', addr: lindaBSmileList},
    LoveLive: {datatype: 'image', title: 'LoveLive', addr: LoveliveSmallSmileList},
    Revstar:  {datatype: 'image', title: '少女歌剧', addr: RevstarSmileList},
    Bandori:  {datatype: 'image', title: '邦邦', addr: BandoriSmileList},
	Touhou:   {datatype: 'image', title: '东方', addr: TouhouSmileList},
    Baidu:    {datatype: 'image', title: '贴吧', addr: BaiduSmileList},
	Weibo:    {datatype: 'image', title: '微博', addr: WeiboSmileList},
	FakeCHS:  {datatype: 'image', title: '伪中国语', addr: FakeCHSSmileList},
	Popular:  {datatype: 'image', title: '流行', addr: PopularSmileList},
};

/**
 * 添加BBCode
 * @param textArea 文本框
 * @param {string} code BBCode
 * @param {string} selText 选择文本
 */
const addCode = function (textArea, code, selText = '') {
    let startPos = !selText ? (code.indexOf('[img]') > -1 || code.indexOf(']') < 0 ? code.length : code.indexOf(']') + 1) : code.indexOf(selText);
    if (typeof textArea.selectionStart !== 'undefined') {
        let prePos = textArea.selectionStart;
        textArea.value = textArea.value.substring(0, prePos) + code + textArea.value.substring(textArea.selectionEnd);
        textArea.selectionStart = prePos + startPos;
        textArea.selectionEnd = prePos + startPos + selText.length;
    }
    else {
        textArea.value += code;
    }
};

/**
 * 显示放大的表情图片
 * @param {jQuery} $img 表情图片对象
 */
const showZoomInImage = function ($img) {
    if ($img.get(0).naturalWidth <= $img.height()) return;
    let offset = $img.offset();
    let $zoomIn = $(`<img class="kfe-zoom-in" src="${$img.attr('src')}" alt="[预览图片]">`).appendTo('body');
    let windowWidth = $(window).width();
    let zoomInWidth = $zoomIn.outerWidth();
    let top = offset.top - $zoomIn.outerHeight() - 5;
    let left = offset.left + $img.width() / 2 - zoomInWidth / 2;
    if (left < 0) left = 0;
    else if (left + zoomInWidth > windowWidth) left = windowWidth - zoomInWidth;
    $zoomIn.css({top, left});
};

/**
 * 获取表情面板的HTML代码
 * @param {string} key 菜单关键字
 * @returns {string} 表情面板内容
 */
const getSmilePanelHtml = function (key) {
    let data = MenuList[key];
    if (!data) return '';
    let html = '';
    for (let i = 0; i < data.addr.length; i++) {
        if (data.datatype === 'image') {
            html += `<img class="kfe-smile" src="${data.addr[i]}" alt="[表情]">`;
        }
        else if (data.datatype === 'imageLink') {
            let ref = typeof data.ref !== 'undefined' && typeof data.ref[i] !== 'undefined' ? data.ref[i] : '';
            html += `<img class="kfe-smile" data-code="${ref}" src="${data.addr[i]}" alt="[表情]">`;
        }
        else if (data.datatype === 'plain') {
            let ref = typeof data.ref !== 'undefined' && typeof data.ref[i] !== 'undefined' ? data.ref[i] : data.addr[i];
            html += `<a class="kfe-smile-text" data-code="${data.addr[i]}" href="#">${ref}</a>`;
        }
    }
    return `<div class="kfe-smile-panel" data-key="${key}">${html}</div>`;
};

/**
 * 获取子菜单的HTML代码
 * @returns {string} 子菜单内容
 */
const getSubMenuHtml = function () {
    let html = '';
    $.each(MenuList, function (key, data) {
        html += `<a class="kfe-sub-menu" data-key="${key}" href="#" title="${data.title}">${data.title}</a>`;
    });
    return html;
};

/**
 * 创建容器
 * @param textArea 文本框
 */
const createContainer = function (textArea) {
    let $container = $(`
<div class="kfe-container">
  <div class="kfe-menu">
    <span title="made by eddie32 version ${version}; modified by 喵拉布丁" style="cursor: pointer;"><b>囧⑨</b></span>
    ${getSubMenuHtml()}
    <span class="kfe-close-panel">[-]</span>
  </div>
</div>
`).insertBefore($(textArea));
    $container.on('click', '.kfe-sub-menu', function (e) {
        e.preventDefault();
        let $this = $(this);
        let key = $this.data('key');
        if (!key) return;
        $container.find('.kfe-sub-menu').removeClass('kfe-sub-menu-active');
        $this.addClass('kfe-sub-menu-active');
        $container.find('.kfe-smile-panel').hide();
        let $panel = $container.find(`.kfe-smile-panel[data-key="${key}"]`);
        if ($panel.length > 0) $panel.show();
        else $(getSmilePanelHtml(key)).appendTo($container).show();
    }).on('click', '.kfe-smile, .kfe-smile-text', function (e) {
        e.preventDefault();
        let $this = $(this);
        let code = $this.data('code');
        if (!code) code = `[img]${$this.attr('src')}[/img]`;
        addCode(textArea, code);
        if (/(Mobile|MIDP)/i.test(navigator.userAgent)) textArea.blur();
        else textArea.focus();
    }).on('mouseenter', '.kfe-smile', function () {
        $('.kfe-zoom-in').remove();
        showZoomInImage($(this));
    }).on('mouseleave', '.kfe-smile', function () {
        $('.kfe-zoom-in').remove();
    }).find('.kfe-close-panel').click(function () {
        $container.find('.kfe-smile-panel').hide();
    });
};

/**
 * 添加CSS
 */
const appendCss = function () {
    $('head').append(`
<style>
  .kfe-container { padding: 5px; vertical-align: middle; font: 12px/1.7em "sans-serif"; }
  .kfe-menu { margin-bottom: 5px; }
  .kfe-sub-menu { margin: 0 7px; text-decoration: none; border-bottom: 2px solid transparent; }
  .kfe-sub-menu:hover, .kfe-sub-menu:focus { text-decoration: none; border-color: deeppink; }
  a.kfe-sub-menu-active { color: black }
  .kfe-smile-panel { display: none; height: 120px; padding: 5px 3px; overflow-y: auto; border-top: 1px solid #ddd; }
  .kfe-smile-panel[data-key="Shortcut"] { height: auto; }
  .kfe-smile { display: inline-block; max-width: 60px; max-height: 60px; cursor: pointer; }
  .kfe-smile-text { display: inline-block; padding: 3px 5px; }
  .kfe-smile-text:hover { color: #fff !important; background-color: #2b2b2b; text-decoration: none; }
  .kfe-close-panel { cursor: pointer; }
  .kfe-zoom-in {
    position: absolute; max-width: 150px; max-height: 150px; background-color: #fcfcfc; border: 3px solid rgba(242, 242, 242, 0.6);
    border-radius: 2px; box-shadow: 0 0 3px rgb(102, 102, 102);
  }
</style>
`);
    if (isKfMobile) {
        $('head').append(`
<style>
  #readPage .kfe-container, #writeMessagePage .kfe-container { margin-top: -10px; }
  .kfe-menu { white-space: nowrap; overflow-x: auto; }
</style>
`);
    }
};

/**
 * 初始化
 */
const init = function () {
    let $textAreas = $('textarea');
    if (!$textAreas.length) return;
    appendCss();
    $textAreas.each(function () {
        createContainer(this);
    });
};

init();