MediaWiki:Gadget-charinsert-core.js

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
/** 
 * <https://en.wikipedia.org/wiki/MediaWiki:Gadget-charinsert-core.js> 
 * 
 * 最初基于 [[mw:User:Alex Smotrov/edittools.js]] 开发,为英文维基百科使用进行了修改。 
 * 配置(需在 [[Special:MyPage/common.js]] 中设置): 
 * window.charinsertCustom – 对象类型。会合并到默认的字符插入列表中。例如,将其设置为 
 * { Symbols: '‽' },会在“符号”(Symbols)分类的末尾添加疑问惊叹号。 
 * window.editToolsRecall – 布尔类型。设为 true 可创建“恢复”(recall)开关。 
 * window.charinsertDontMove – 布尔类型。设为 true 可让字符插入框保持默认位置,不 
 * 移动到编辑摘要上方。 
 * window.updateEditTools() – 函数。更新 window.charinsertCustom 后调用此函数,可重新生成 
 * 编辑工具(EditTools)窗口。 
 */ 
/* 全局变量声明:$, mw, charinsertCustom */ 
window.updateEditTools = function () { }; 

$( function () { 	
	var $currentFocused, 		// 存储当前获取焦点的输入元素(文本框/编辑器)
		editTools; 				// 编辑工具核心对象,封装所有功能和配置

	/**
	 * 获取已选中的字符分类
	 * 优先从本地存储读取,本地存储无数据时从会话存储读取
	 * @returns {string|undefined} 存储的分类索引,无存储数据则返回 undefined
	 */
	function getSelectedSection() { 		
		var selectedSection = mw.storage.get( editTools.storageKey ) 
			|| mw.storage.session.get( editTools.storageKey ); 		
		return selectedSection; 
	} 

	/**
	 * 保存当前选中的字符分类索引
	 * 优先存入本地存储,本地存储失败时存入会话存储
	 * @param {string} newIndex - 新的分类索引(对应下拉选择框的选项序号)
	 */
	function saveSelectedSection( newIndex ) { 		
		mw.storage.set( editTools.storageKey, newIndex ) 
			|| mw.storage.session.set( editTools.storageKey, newIndex ); 
	} 

    editTools = {
        // 以 ␥(U+2425,删除格式符号二)开头的条目,不会在主命名空间(命名空间 0)显示。
        // 如需修改,请同时编辑 [[MediaWiki:Edittools]];但在该页面中,无需使用 ␥ 符号,改用 {{#ifeq:{{NAMESPACE}}|{{ns:0}}| | }} 控制主命名空间显示。
        charinsert: {
            '插入': '– — ′ ″ ‘+’ “+” 《+》 · § [\[+]] {\{+}} <sub>+</sub> ␥在讨论页签名: ␥~~\~~  引用来源: <ref>+</ref>',
            '维基标签': '<ref>+</ref> ␥~~\~~ {\{+}} {\{\{+}}} | [+] [\[+]] [\[Category:+]] #REDIRECT.[\[+]] &nbsp; <u>+</u> <s>+</s> <sup>+</sup> <sub>+</sub> <code>+</code> <pre>+</pre> <blockquote>+</blockquote> <ref.name="+"_/> {\{Reflist}} <references./> <includeonly>+</includeonly> <onlyinclude>+</onlyinclude> <noinclude>+</noinclude> <nowiki>+</nowiki> <code><nowiki>+</nowiki></code> <!--.+_--> [\[:File:+]] <syntaxhighlight.lang="php">+</syntaxhighlight>',
            '常用模板': ' {{subst:+}} {{SERVER}} {{localurl:+}} {{fullurl:+}} {{FULLPAGENAME}} {{PAGENAME}} {{DEFAULTSORT:+}} {{#expr:+}} {{#if:+}} {{#ifeq:+}} {{#ifexpr:+}} {{#switch:+}} {{#ifexist:+}} {{#time:Y年Fj日&#32;H:i|+}}',
            '常用符号': '“+” ‘+’ 「+」 『+』 (+) «+» ‹+› „+“ ‚+‘ - —— …… 《+》 〈+〉 【+】 〖+〗 〔+〕 ~ | ¡¿†‡↔→↑←↓↖↗↘↙•▪¶#∞ ‹+› «+» − °℃℉‰ ¤₳฿₵¢₡₢$₫₯€₠₣ƒ₴₭₤ℳ₥₦№₧₰£៛₨₪৳₮₩¥¥ ♠♣♥♦ 𝄫♭♮♯𝄪 ©®™ ◌ ☉☾☿♀🜨♂♃♄⛢♆',
            '拼音符号': '拼音: Á á À à Ǎ ǎ Ā ā É é È è Ě ě Ē ē Í í Ì ì Ǐ ǐ Ī ī Ó ó Ò ò Ǒ ǒ Ō ō Ú ú Ù ù Ü ü Ǔ ǔ Ū ū Ǘ ǘ Ǜ ǜ Ǚ ǚ Ǖ ǖ ê ê+̄ ế ê+̌ ề  注音: ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ ㄧ ㄨ ㄩ ㄚ ㄛ ㄜ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ ˉ ˊ ˇ ˋ ˙',
            '常用序号': '⓪ ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ㉑ ㉒ ㉓ ㉔ ㉕ ㉖ ㉗ ㉘ ㉙ ㉚ ㉛ ㉜ ㉝ ㉞ ㉟ ㊱ ㊲ ㊳ ㊴ ㊵ ㊶ ㊷ ㊸ ㊹ ㊺ ㊻ ㊼ ㊽ ㊾ ㊿ ㊀ ㊁ ㊂ ㊃ ㊄ ㊅ ㊆ ㊇ ㊈ ㊉ ㈠ ㈡ ㈢ ㈣ ㈤ ㈥ ㈦ ㈧ ㈨ ㈩ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ❶ ❷ ❸ ❹ ❺ ❻ ❼ ❽ ❾ ❿ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ Ⅺ Ⅻ Ⅼ Ⅽ Ⅾ Ⅿ 〇 ⅰ ⅱ ⅲ ⅳ ⅴ ⅵ ⅶ ⅷ ⅸ ⅹ ⅺ ⅻ ⅼ ⅽ ⅾ ⅿ 〡 〢 〣 〤 〥 〦 〧 〨 〩 ㋀ ㋁ ㋂ ㋃ ㋄ ㋅ ㋆ ㋇ ㋈ ㋉ ㋊ ㋋ ㏠ ㏡ ㏢ ㏣ ㏤ ㏥ ㏦ ㏧ ㏨ ㏩ ㏪ ㏫ ㏬ ㏭ ㏮ ㏯ ㏰ ㏱ ㏲ ㏳ ㏴ ㏵ ㏶ ㏷ ㏸ ㏹ ㏺ ㏻ ㏼ ㏽ ㏾ ㍘ ㍙ ㍚ ㍛ ㍜ ㍝ ㍞ ㍟ ㍠ ㍡ ㍢ ㍣ ㍤ ㍥ ㍦ ㍧ ㍨ ㍩ ㍪ ㍫ ㍬ ㍭ ㍮ ㍯ ㍰ ㈰ ㈪ ㈫ ㈬ ㈭ ㈮ ㈯ ㊤ ㊥ ㊦ ㊧ ㊨',
            '数学与逻辑': '+ − × ÷ ⋅ … ¼ ½ ¾ ¹ ² ³ ° ′ ″ ∗ ∘ ± ∓ ≤ ≥ < > ≮ ≯ ≠ ≡ ∥ ∽ ≅ ≜ ≝ ≐ ≃ ≈ ⊕ ⊗ ⇐ ⇔ ⇒ ∞ ← ↔ → ≪ ≫ ∝ √ ∤ ≀ ◅ ▻ ⋉ ⋊ ⋈ ∴ ∵ ∷ ↦ ¬ ∧ ∨ ⊻ ∀ ∃ ∈ ∉ ∋ ⊆ ⊈ ⊊ ⊂ ⊄ ⊇ ⊉ ⊋ ⊃ ⊅ ⌒ ⊙ ∪ ∩ ∑ ∏ ∐ ∫ ∬ ∭ ∮ ∇ ∂ ∆ ∅ ℂ ℍ ℕ ℙ ℚ ℝ ℤ ℵ ‖ ⌊ ⌋ ⌈ ⌉ ⊤ ⊥ ⊢ ⊣ ⊧ □ ∠ ⟨ ⟩ <math>+</math> {\{math|+}} {\{mvar|+}} {\{frac|+|}} {\{sfrac|+|}}',
            '日语': '平假名: あ い う え お か き く け こ さ し す せ そ た ち つ て と な に ぬ ね の は ひ ふ へ ほ ま み む め も や ゆ よ ら り る れ ろ わ ゐ ゑ を ん 片假名: ア イ ウ エ オ カ キ ク ケ コ サ シ ス セ ソ タ チ ツ テ ト ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ マ ミ ム メ モ ヤ ユ ヨ ラ リ ル レ ロ ワ ヰ ヱ ヲ ン 日语罗马字: Ā ā Ē ē Ī ī Ō ō Ū ū',
            '拉丁语': 'A a Á á À à  â Ä ä Ǎ ǎ Ă ă Ā ā à ã Å å Ą ą Æ æ Ǣ ǣ  B b  C c Ć ć Ċ ċ Ĉ ĉ Č č Ç ç  D d Ď ď Đ đ Ḍ ḍ Ð ð  E e É é È è Ė ė Ê ê Ë ë Ě ě Ĕ ĕ Ē ē Ẽ ẽ Ę ę Ẹ ẹ Ɛ ɛ Ǝ ǝ Ə ə  F f  G g Ġ ġ Ĝ ĝ Ğ ğ Ģ ģ  H h Ĥ ĥ Ħ ħ Ḥ ḥ  I i İ ı Í í Ì ì Î î Ï ï Ǐ ǐ Ĭ ĭ Ī ī Ĩ ĩ Į į Ị ị  J j Ĵ ĵ  K k Ķ ķ  L l Ĺ ĺ Ŀ ŀ Ľ ľ Ļ ļ Ł ł Ḷ ḷ Ḹ ḹ  M m Ṃ ṃ  N n Ń ń Ň ň Ñ ñ Ņ ņ Ṇ ṇ Ŋ ŋ  O o Ó ó Ò ò Ô ô Ö ö Ǒ ǒ Ŏ ŏ Ō ō Õ õ Ǫ ǫ Ọ ọ Ő ő Ø ø Œ œ  Ɔ ɔ  P p  Q q  R r Ŕ ŕ Ř ř Ŗ ŗ Ṛ ṛ Ṝ ṝ  S s Ś ś Ŝ ŝ Š š Ş ş Ș ș Ṣ ṣ ß  T t Ť ť Ţ ţ Ț ț Ṭ ṭ Þ þ  U u Ú ú Ù ù Û û Ü ü Ǔ ǔ Ŭ ŭ Ū ū Ũ ũ Ů ů Ų ų Ụ ụ Ű ű Ǘ ǘ Ǜ ǜ Ǚ ǚ Ǖ ǖ  V v  W w Ŵ ŵ  X x  Y y Ý ý Ŷ ŷ Ÿ ÿ Ỹ ỹ Ȳ ȳ  Z z Ź ź Ż ż Ž ž  ß Ð ð Þ þ Ŋ ŋ Ə ə Ɂ ɂ  Ꞌ ꞌ  ʻ  ʼ  ʽ  ꞉ ꞏ',	
            // '希腊语': 'ΆάΈέΉήΊίΌόΎύΏώ  ΑαΒβΓγΔδ  ΕεΖζΗηΘθ  ΙιΚκΛλΜμ  ΝνΞξΟοΠπ  ΡρΣσςΤτΥυ  ΦφΧχΨψΩω Ϝϝυ̯ι̯  ᾼᾳᾴᾺὰᾲᾶᾷἈἀᾈᾀἉἁᾉᾁἌἄᾌᾄἊἂᾊᾂἎἆᾎᾆἍἅᾍᾅἋἃᾋᾃἏἇᾏᾇ  ῈὲἘἐἙἑἜἔἚἒἝἕἛἓ  ῌῃῄῊὴῂῆῇἨἠᾘᾐἩἡᾙᾑἬἤᾜᾔἪἢᾚᾒἮἦᾞᾖἭἥᾝᾕἫἣᾛᾓἯἧᾟᾗ  ῚὶῖἸἰἹἱἼἴἺἲἾἶἽἵἻἳἿἷΪϊΐῒῗ  ῸὸὈὀὉὁὌὄὊὂὍὅὋὃ  ῤῬῥ  ῪὺῦὐὙὑὔὒὖὝὕὛὓὟὗΫϋΰῢῧ  ῼῳῴῺὼῲῶῷὨὠᾨᾠὩὡᾩᾡὬὤᾬᾤὪὢᾪᾢὮὦᾮᾦὭὥᾭᾥὫὣᾫᾣὯὧᾯᾧ ᾹᾱᾸᾰῙῑῘῐῩῡῨῠ',
           //  '西里尔语': 'АаБбВвГг  ҐґЃѓДдЂђ  ЕеЁёЄєЖж  ЗзЅѕИиІі  ЇїЙйЈјКк  ЌќЛлЉљМм  НнЊњОоПп  РрСсТтЋћ  УуЎўФфХх  ЦцЧчЏџШш  ЩщЪъЫыЬь  ЭэЮюЯя ӘәӨөҒғҖҗ ҚқҜҝҢңҮү ҰұҲҳҸҹҺһ  ҔҕӢӣӮӯҘҙ  ҠҡҤҥҪҫӐӑ  ӒӓӔӕӖӗӰӱ  ӲӳӸӹӀ  ҞҟҦҧҨҩҬҭ  ҴҵҶҷҼҽҾҿ  ӁӂӃӄӇӈӋӌ  ӚӛӜӝӞӟӠӡ  ӤӥӦӧӪӫӴӵ  ́',
           //  '阿拉伯语': '  音译: ʾ  ā ī ū ṯ ḥ ḫ ẖ ḏ š ṣ ḍ ṭ ẓ ʿ ġ ẗ á ا ﺁ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه ة و ي ى ء أ إ ؤ ئ',
           //  '国际音标 (英语)': 'ˈ ˌ  ŋ ɡ tʃ dʒ ʃ ʒ θ ð ʔ  ɑː ɒ æ aɪ aʊ ɛ ɛər+ eɪ ɪ ɪər+ iː ɔː ɔɪ oʊ ʊ ʊər+ uː ʌ ɜːr+  ə ər  ɒ̃ æ̃',
            // '国际音标': '辅音: ɱɳɲŋɴ : t̪ d̪ ʈɖɟɡɢʡʔ : ɸβθð  ʃʒʂʐɕʑ  çʝɣχʁ  ħʕʜʢɦɧ : ʋɹɻɥɰʍ : ʙⱱɾɽʀ  ɺ  ɫɬɮɭʎʟ : ɓɗᶑʄɠʛ  ʘǀǃǂǁ  元音: ɪʏɨʉɯʊ : øɘɵɤ  ə ɚ  ɛœɜɝɞʌɔ : æɶɐɑɒ  间隔变音符号: ˈˌːˑʼˀˤᵝᵊᶢˠʰʱʲˡⁿᵑʷᶣ˞‿˕˔  组合变音符号: ̚ ̪ ̺ ̻ ̼ ̬  ̊ ̥ ̞ ̝ ̘ ̙ ̽ ̟ ̠  ̈ ̤ ̹ ̜ ̍ ̩  ̆ ̯  ̃ ̰ ͡ ͜  声调:  ̋  ́  ̄  ̀  ̏  ̌  ̂ ᷄ ᷅ ᷇ ᷆ ᷈ ᷉  ˥˦˧˨˩ꜛꜜ : ↗↘‖  扩展国际音标: ͈ ͉ ͎ ̣ ̫ ͊ ᷽ ͇ : ˭ᵻᵿ',
           //  '古代英语': 'Ā ā Æ æ Ǣ ǣ Ǽ ǽ Ċ ċ Ð ð Ē ē Ġ ġ Ī ī Ō ō Ū ū Ƿ ƿ Ȳ ȳ Þ þ Ȝ ȝ',
            // '希伯来语': 'אבגדהוזחטיכךלמםנןסעפףצץקרשת  ׳ ״  װױײ', 
           //  '意第绪语': ' א אַ אָ ב בֿ ג ד ה ו וּ װ ױ ז זש ח ט י יִ ײ ײַ כ ך כּ ל ל+ מ ם נ ן ס ע ע+ פ פּ פֿ ף צ ץ ק ר ש שׂ תּ ת ׳ ״ ־',
            // '天成文书(梵文)': 'ँ ं ः अ आ इ ई उ ऊ ऋ ऌ ऍ ऎ ए ऐ ऑ ऒ ओ औ क क़ ख ख़ ग ग़ घ ङ च छ ज ज़ झ ञ ट ठ ड ड़ द ढ ढ़ ण त थ ध न ऩ प फ फ़ ब भ म य य़ र ऱ ल ळ ऴ व श ष स ह ़ ऽ ा ि ॊ ो ौ ् ी ु ू ृ ॄ ॅ ॆ े ै ॉ ॐ ॑ ॒ ॓ ॔ ॠ ॡ ॢ ॣ । ॥ ॰',
           //  '德语': 'Ä ä Ö ö Ü ü ß ẞ',
           //  '法语': 'À à  â Ç ç É é È è Ê ê Ë ë Î î Ï ï Ô ô Œ œ Ù ù Û û Ü ü Ÿ ÿ',
          //   '冰岛语': 'Á á Ð ð É é Í í Ó ó Ú ú Ý ý Þ þ Æ æ Ö ö',
          //   '波兰语': 'ą Ą ć Ć ę Ę ł Ł ń Ń ó Ó ś Ś ź Ź ż Ż',
          //   '捷克语': 'Á á Č č Ď ď É é Ě ě Í í Ň ň Ó ó Ř ř Š š Ť ť Ú ú Ů ů Ý ý Ž ž',
           //  '世界语': 'Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŭ ŭ',
           //  '越南语': 'À à Ả ả Á á Ạ ạ Ã ã Ă ă Ằ ằ Ẳ ẳ Ẵ ẵ Ắ ắ Ặ ặ Â â Ầ ầ Ẩ ẩ Ẫ ẫ Ấ ấ Ậ ậ Đ đ È è Ẻ ẻ Ẽ ẽ É é Ẹ ẹ Ê ê Ề ề Ể ể Ễ ễ Ế ế Ệ ệ Ỉ ỉ Ĩ ĩ Í í Ị ị Ì ì Ỏ ỏ Ó ó Ọ ọ Ò ò Õ õ Ô ô Ồ ồ Ổ ổ Ỗ ỗ Ố ố Ộ ộ Ơ ơ Ờ ờ Ở ở Ỡ ỡ Ớ ớ Ợ ợ Ù ù Ủ ủ Ũ ũ Ú ú Ụ ụ Ư ư Ừ ừ Ử ử Ữ ữ Ứ ứ Ự ự Ỳ ỳ Ỷ ỷ Ỹ ỹ Ỵ ỵ Ý ý',
           //  '立陶宛语': 'Ą ą Č č Ę ę Ė ė Į į Š š Ų ų Ū ū Ž ž',
           //  '马耳他语': 'Ċ ċ Ġ ġ Ħ ħ Ż ż',
           //  '葡萄牙语': 'Á á À à Â â Ã ã Ç ç É é Ê ê Í í Ó ó Ô ô Õ õ Ú ú Ü ü',
           //  '土耳其语': 'Ç ç Ğ ğ İ ı Ö ö Ş ş Ü ü Â â Î î Û û',
           //  '夏威夷语': 'Ā ā Ē ē Ī ī Ō ō Ū ū ʻ',
           //  '匈牙利语': 'Ő ő Ű ű',
           //  '西班牙语': 'Á á É é Í í Ñ ñ Ó ó Ú ú Ü ü ¡ ¿',
           //  '意大利语': 'Á á À à É é È è Í í Ì ì Ó ó Ò ò Ú ú Ù ù',
           //  '威尔士语': 'Á á À à Â â Ä ä É é È è Ê ê Ë ë Ì ì Î î Ï ï Ó ó Ò ò Ô ô Ö ö Ù ù Û û Ẁ ẁ Ŵ ŵ Ẅ ẅ Ý ý Ỳ ỳ Ŷ ŷ Ÿ ÿ',
           //  '爱沙尼亚语': 'Č č Š š Ž ž Õ õ Ä ä Ö ö Ü ü',
           
           //  '拉脱维亚语': 'Ā ā Č č Ē ē Ģ ģ Ī ī Ķ ķ Ļ ļ Ņ ņ Š š Ū ū Ž ž',
           //  '罗马尼亚语': 'Ă ă Â â Î î Ş ş Ţ ţ',
           //  '塞尔维亚语': 'А а Б б В в Г г Д д Ђ ђ Е е Ж ж З з И и Ј ј К к Л л Љ љ М м Н н Њ њ О о П п Р р С с Т т Ћ ћ У у Ф ф Х х Ц ц Ч ч Џ џ Ш ш',
           //  '加泰罗尼亚语': 'Á á À à Ç ç É é È è Ë ë Í í Ï ï Ó ó Ò ò Ö ö Ú ú Ù ù',
           //  '斯堪的纳维亚语': 'À à É é Å å Æ æ Ä ä Ø ø Ö ö'
        },
        
        // 字符分隔符:使用不间断空格(\240 对应 Unicode 中的 nbsp)
charinsertDivider: "\240",
storageKey: 'edittoolscharsubset',

/**
 * 创建编辑工具(特殊字符选择面板)
 * @param {HTMLElement} placeholder - 占位元素,将被工具面板替换
 */
createEditTools: function (placeholder) {
    var sel, id;
    var box = document.createElement('div');
    var prevSubset = 0, curSubset = 0;

    // 设置工具面板的基础属性
    box.id = 'editpage-specialchars';
    box.className = "nopopups";
    box.title = '点击字符或标签,将其插入到编辑窗口中';

    if (window.charinsertCustom) {
        for (id in charinsertCustom) {
            if (!editTools.charinsert[id]) {
                editTools.charinsert[id] = '';
            }
        }
    }

    sel = document.createElement('select');
    for (id in editTools.charinsert) {
        sel.options[sel.options.length] = new Option(id, id);
    }
    sel.selectedIndex = 0;
    sel.style.marginRight = '.3em';
    sel.title = '选择字符子集';
    sel.onchange = sel.onkeyup = selectSubset;
    box.appendChild(sel);

    if (window.editToolsRecall) {
        var recall = document.createElement('span');
        recall.appendChild(document.createTextNode('↕')); 
        recall.onclick = function () {
            sel.selectedIndex = prevSubset;
            selectSubset();
        };
        recall.style.cssFloat = 'left';
        recall.style.marginRight = '5px';
        recall.style.cursor = 'pointer';
        box.appendChild(recall);
    }

    if (getSelectedSection()) {
        sel.selectedIndex = getSelectedSection();
    }

    placeholder.parentNode.replaceChild(box, placeholder);
    selectSubset();
    return;

    /**
     * 切换字符子集的核心函数
     * 功能:隐藏其他子集、显示/创建当前选中的子集、保存选中状态到本地存储
     */
    function selectSubset() {
        prevSubset = curSubset;
        curSubset = sel.selectedIndex;
        saveSelectedSection(curSubset);

        var pp = box.getElementsByTagName('p');
        for (var i = 0; i < pp.length; i++) {
            pp[i].style.display = 'none';
        }

        var id = sel.options[curSubset].value;
        var p = document.getElementById(id);

  
        if (!p) {
            p = document.createElement('p');
            p.className = 'nowraplinks'; 
            p.id = id;

            if (id == '阿拉伯语' || id == '希伯来语') {
                p.style.fontSize = '120%';
                p.dir = 'rtl';
            }

            var tokens = editTools.charinsert[id];
            if (window.charinsertCustom && charinsertCustom[id]) {
                if (tokens.length > 0) {
                    tokens += ' ';
                }
                tokens += charinsertCustom[id];
            }

            editTools.createTokens(p, tokens);
            box.appendChild(p);
        }

       p.style.display = 'inline';
    }
},

/**
 * 创建字符/标签元素(将字符子集数据解析为可点击的页面元素)
 * @param {HTMLElement} paragraph - 承载字符/标签的容器(p 标签)
 * @param {string} str - 字符子集的原始数据(空格分隔的 token 字符串)
 */
createTokens: function (paragraph, str) {
    var tokens = str.split(' '), token, i, n;

    for (i = 0; i < tokens.length; i++) {
        token = tokens[i];
        n = token.indexOf('+');

        if (token.charAt(0) === '␥') {
            if (token.length > 1 && mw.config.get('wgNamespaceNumber') === 0) {
                continue;
            } else {
                token = token.substring(1);
            }
        }

        if (token === '' || token === '_') {
            addText(editTools.charinsertDivider + ' ');
        } else if (token === '\n') {
            paragraph.appendChild(document.createElement('br'));
        } else if (token === '___') {
           paragraph.appendChild(document.createElement('hr'));
        } else if (token.charAt(token.length - 1) === ':') {
            addBold(token);
        } else if (n === 0) {
           addLink(token.substring(1), '</' + token.substring(2), token.substring(1));
        } else if (n > 0) {
           addLink(token.substring(0, n), token.substring(n + 1));
        } else {
            var chars = Array.from(token);
            if (chars.length > 2 && token.charCodeAt(0) > 127) {
                for (var j = 0; j < chars.length; j++) {
                    addLink(chars[j], '');
                }
            } else {
                addLink(token, '');
            }
        }
    }
    return;

    /**
     * 创建可点击的链接元素(用于插入字符或标签)
     * @param {string} tagOpen - 起始标签/字符(如 '<b>' 或 'α')
     * @param {string} tagClose - 结束标签(如 '</b>',字符类型为空)
     * @param {string} name - 链接显示的文本(默认使用 tagOpen + tagClose)
     */
    function addLink(tagOpen, tagClose, name) {
        var handler;
        var dle = tagOpen.indexOf('\x10');
        var a = document.createElement('a');

        if (dle > 0) {
            var path = tagOpen.substring(dle + 1).split('.');
            tagOpen = tagOpen.substring(0, dle);
            handler = window;
            for (var i = 0; i < path.length; i++) {
                handler = handler[path[i]];
            }
            $(a).on('click', handler);
        } else {
            tagOpen = tagOpen.replace(/\./g, ' ');
            tagClose = tagClose ? tagClose.replace(/_/g, ' ') : '';
            $(a).on('click', {
                tagOpen: tagOpen,
                sampleText: '',
                tagClose: tagClose
            }, insertTags);
        }

        name = name || tagOpen + tagClose;
        name = name.replace(/\\n/g, '');
        a.appendChild(document.createTextNode(name));
        a.href = '';
        paragraph.appendChild(a);
        addText(' ');
    }

    /**
     * 创建粗体文本元素(用于显示分类标题,不可点击)
     * @param {string} text - 粗体文本内容(将下划线替换为空格)
     */
    function addBold(text) {
        var b = document.createElement('b');
        b.appendChild(document.createTextNode(text.replace(/_/g, ' ')));
        paragraph.appendChild(b);
        addText(' ');
    }

    /**
     * 向容器中添加普通文本(用于分隔符或空格)
     * @param {string} txt - 要添加的文本内容
     */
    function addText(txt) {
        paragraph.appendChild(document.createTextNode(txt));
    }

    /**
     * 插入标签/字符到编辑框的核心函数
     * @param {Event} e - 点击事件对象(包含标签前后缀数据)
     */
    function insertTags(e) {
        e.preventDefault();

        if ($currentFocused && $currentFocused.length && !$currentFocused.prop('readonly')) {
            $currentFocused.textSelection(
                'encapsulateSelection', {
                    pre: e.data.tagOpen, 
                    peri: e.data.sampleText,
                    post: e.data.tagClose  
                }
            );
        }
    }
},

/**
 * 初始化编辑工具(入口函数)
 * 功能:创建工具面板容器、绑定焦点事件、调用 createEditTools 生成面板
 */
setup: function () {
    var placeholder;

    if ($('#editpage-specialchars').length) {
        placeholder = $('#editpage-specialchars')[0];
    } else {
        placeholder = $('<div id="editpage-specialchars"> </div>').prependTo('.mw-editTools')[0];
    }

    if (!placeholder) {
        return;
    }

    if (!window.charinsertDontMove) {
        $('.editOptions').before(placeholder);
    }

    $currentFocused = $('#wpTextbox1');

    $(document).on('focus', 'textarea, input:text, .CodeMirror', function () {
        if ($(this).is('.CodeMirror')) {
            $currentFocused = $('#wpTextbox1');
        } else {
            $currentFocused = $(this);
        }
    });

    editTools.createEditTools(placeholder);

    window.updateEditTools = function () {
        editTools.createEditTools($('#editpage-specialchars')[0]);
    };
}
}; 

editTools.setup();
} );