@richscripts/richtexteditor 2.4.0
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/README.md +360 -0
- package/admin/index.d.ts +100 -0
- package/admin/index.js +94 -0
- package/angular/component.ts +284 -0
- package/angular/index.d.ts +80 -0
- package/angular/index.ts +24 -0
- package/headless/index.d.ts +65 -0
- package/headless/index.js +272 -0
- package/index.d.ts +1412 -0
- package/integrations/shared.d.ts +138 -0
- package/integrations/shared.js +5182 -0
- package/package.json +130 -0
- package/react/index.d.ts +104 -0
- package/react/index.js +310 -0
- package/richtexteditor/CHANGELOG.md +1876 -0
- package/richtexteditor/all_plugins.js +31125 -0
- package/richtexteditor/all_plugins.min.js +78 -0
- package/richtexteditor/lang/rte-lang-af.js +193 -0
- package/richtexteditor/lang/rte-lang-ar.js +193 -0
- package/richtexteditor/lang/rte-lang-bg.js +193 -0
- package/richtexteditor/lang/rte-lang-bn.js +193 -0
- package/richtexteditor/lang/rte-lang-bs.js +193 -0
- package/richtexteditor/lang/rte-lang-ca.js +193 -0
- package/richtexteditor/lang/rte-lang-cs.js +193 -0
- package/richtexteditor/lang/rte-lang-cy.js +193 -0
- package/richtexteditor/lang/rte-lang-da.js +193 -0
- package/richtexteditor/lang/rte-lang-de.js +193 -0
- package/richtexteditor/lang/rte-lang-el.js +193 -0
- package/richtexteditor/lang/rte-lang-en.js +6 -0
- package/richtexteditor/lang/rte-lang-es.js +193 -0
- package/richtexteditor/lang/rte-lang-et.js +193 -0
- package/richtexteditor/lang/rte-lang-fa.js +193 -0
- package/richtexteditor/lang/rte-lang-fi.js +193 -0
- package/richtexteditor/lang/rte-lang-fil.js +193 -0
- package/richtexteditor/lang/rte-lang-fj.js +193 -0
- package/richtexteditor/lang/rte-lang-fr.js +193 -0
- package/richtexteditor/lang/rte-lang-ga.js +193 -0
- package/richtexteditor/lang/rte-lang-gu.js +193 -0
- package/richtexteditor/lang/rte-lang-he.js +193 -0
- package/richtexteditor/lang/rte-lang-hi.js +193 -0
- package/richtexteditor/lang/rte-lang-hr.js +193 -0
- package/richtexteditor/lang/rte-lang-ht.js +193 -0
- package/richtexteditor/lang/rte-lang-hu.js +193 -0
- package/richtexteditor/lang/rte-lang-id.js +193 -0
- package/richtexteditor/lang/rte-lang-is.js +193 -0
- package/richtexteditor/lang/rte-lang-it.js +193 -0
- package/richtexteditor/lang/rte-lang-ja.js +193 -0
- package/richtexteditor/lang/rte-lang-kn.js +193 -0
- package/richtexteditor/lang/rte-lang-ko.js +193 -0
- package/richtexteditor/lang/rte-lang-lt.js +193 -0
- package/richtexteditor/lang/rte-lang-lv.js +193 -0
- package/richtexteditor/lang/rte-lang-mg.js +193 -0
- package/richtexteditor/lang/rte-lang-mi.js +193 -0
- package/richtexteditor/lang/rte-lang-ml.js +193 -0
- package/richtexteditor/lang/rte-lang-mr.js +193 -0
- package/richtexteditor/lang/rte-lang-ms.js +193 -0
- package/richtexteditor/lang/rte-lang-mt.js +193 -0
- package/richtexteditor/lang/rte-lang-mww.js +193 -0
- package/richtexteditor/lang/rte-lang-nb.js +193 -0
- package/richtexteditor/lang/rte-lang-nl.js +193 -0
- package/richtexteditor/lang/rte-lang-otq.js +193 -0
- package/richtexteditor/lang/rte-lang-pa.js +193 -0
- package/richtexteditor/lang/rte-lang-pl.js +193 -0
- package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-ro.js +193 -0
- package/richtexteditor/lang/rte-lang-ru.js +193 -0
- package/richtexteditor/lang/rte-lang-sk.js +193 -0
- package/richtexteditor/lang/rte-lang-sl.js +193 -0
- package/richtexteditor/lang/rte-lang-sm.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-sv.js +193 -0
- package/richtexteditor/lang/rte-lang-sw.js +193 -0
- package/richtexteditor/lang/rte-lang-ta.js +193 -0
- package/richtexteditor/lang/rte-lang-te.js +193 -0
- package/richtexteditor/lang/rte-lang-th.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
- package/richtexteditor/lang/rte-lang-to.js +193 -0
- package/richtexteditor/lang/rte-lang-tr.js +193 -0
- package/richtexteditor/lang/rte-lang-ty.js +193 -0
- package/richtexteditor/lang/rte-lang-uk.js +193 -0
- package/richtexteditor/lang/rte-lang-ur.js +193 -0
- package/richtexteditor/lang/rte-lang-vi.js +193 -0
- package/richtexteditor/lang/rte-lang-yua.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
- package/richtexteditor/lang/rte-lang.js +1 -0
- package/richtexteditor/plugins/-combine-plugin.txt +2 -0
- package/richtexteditor/plugins/-combine-plugins.bat +20 -0
- package/richtexteditor/plugins/a11yenhance.js +90 -0
- package/richtexteditor/plugins/accessibilitychecker.js +579 -0
- package/richtexteditor/plugins/aitoolkit.css +15830 -0
- package/richtexteditor/plugins/aitoolkit.js +19171 -0
- package/richtexteditor/plugins/all_plugins.js +31125 -0
- package/richtexteditor/plugins/all_plugins.min.js +78 -0
- package/richtexteditor/plugins/autoembed.js +174 -0
- package/richtexteditor/plugins/blocktypes.js +175 -0
- package/richtexteditor/plugins/bookmarkcard.js +226 -0
- package/richtexteditor/plugins/charlimit.js +195 -0
- package/richtexteditor/plugins/comments.js +672 -0
- package/richtexteditor/plugins/contentminimap.js +369 -0
- package/richtexteditor/plugins/crdt-engine.js +8905 -0
- package/richtexteditor/plugins/crdt-engine.min.js +5 -0
- package/richtexteditor/plugins/dialoga11y.js +174 -0
- package/richtexteditor/plugins/dictation.js +268 -0
- package/richtexteditor/plugins/documentimport.js +369 -0
- package/richtexteditor/plugins/documentoutline.js +341 -0
- package/richtexteditor/plugins/emailtoolkit.js +165 -0
- package/richtexteditor/plugins/emojiautocomplete.js +241 -0
- package/richtexteditor/plugins/foldheadings.js +181 -0
- package/richtexteditor/plugins/ghostcomplete.js +297 -0
- package/richtexteditor/plugins/html2pdf.js +158 -0
- package/richtexteditor/plugins/imageeditor.js +159 -0
- package/richtexteditor/plugins/insertcode.js +319 -0
- package/richtexteditor/plugins/insertemoji.js +229 -0
- package/richtexteditor/plugins/insertgallery.js +362 -0
- package/richtexteditor/plugins/inserttemplate.js +261 -0
- package/richtexteditor/plugins/localdraft.js +213 -0
- package/richtexteditor/plugins/markdownexport.js +110 -0
- package/richtexteditor/plugins/mathdialog.js +153 -0
- package/richtexteditor/plugins/mentionplugin.js +534 -0
- package/richtexteditor/plugins/mermaiddiagram.js +294 -0
- package/richtexteditor/plugins/readabilitystats.js +148 -0
- package/richtexteditor/plugins/readaloud.js +243 -0
- package/richtexteditor/plugins/restrictedediting.js +208 -0
- package/richtexteditor/plugins/revisionhistory.js +670 -0
- package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
- package/richtexteditor/plugins/slashcommand.js +705 -0
- package/richtexteditor/plugins/smartchips.js +250 -0
- package/richtexteditor/plugins/spellcheck.js +181 -0
- package/richtexteditor/plugins/todolist.js +137 -0
- package/richtexteditor/plugins/trackedchanges.js +658 -0
- package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
- package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
- package/richtexteditor/plugins/typewriter.js +203 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +693 -0
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
- package/richtexteditor/rte-config.js +933 -0
- package/richtexteditor/rte.js +943 -0
- package/richtexteditor/rte_mobile.css +234 -0
- package/richtexteditor/rte_theme_default.css +4819 -0
- package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
- package/richtexteditor/runtime/help.htm +162 -0
- package/richtexteditor/runtime/images/1x1.gif +0 -0
- package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
- package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
- package/richtexteditor/runtime/images/formbn.gif +0 -0
- package/richtexteditor/runtime/images/multiclavier.gif +0 -0
- package/richtexteditor/runtime/richtexteditor_content.css +362 -0
- package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
- package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
- package/server/index.d.ts +21 -0
- package/server/index.js +23 -0
- package/svelte/index.d.ts +19 -0
- package/svelte/index.js +59 -0
- package/vue/index.d.ts +94 -0
- package/vue/index.js +291 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-06-05 Emoji :shortcode: autocomplete (GitHub / Slack / Discord style).
|
|
4
|
+
// Two behaviours, both client-side, no data bundled beyond a curated shortcode
|
|
5
|
+
// map:
|
|
6
|
+
// 1. Type a complete `:shortcode:` (closing colon) -> replaced with the emoji.
|
|
7
|
+
// 2. Type `:` + a partial query -> a popup of matches appears; Arrow keys move,
|
|
8
|
+
// Enter/Tab insert, Esc dismisses.
|
|
9
|
+
// Complements the existing emoji picker (insertemoji) with inline, keyboard-only
|
|
10
|
+
// entry. Extend or replace the map via config.emojiShortcodes.
|
|
11
|
+
RTE_DefaultConfig.plugin_emojiautocomplete = RTE_Plugin_EmojiAutocomplete;
|
|
12
|
+
RTE_DefaultConfig.emojiAutocompleteEnabled = (typeof RTE_DefaultConfig.emojiAutocompleteEnabled === "undefined")
|
|
13
|
+
? true : RTE_DefaultConfig.emojiAutocompleteEnabled;
|
|
14
|
+
RTE_DefaultConfig.emojiShortcodes = RTE_DefaultConfig.emojiShortcodes || {
|
|
15
|
+
smile: "😄", smiley: "😃", grin: "😁", laughing: "😆", satisfied: "😆", joy: "😂", rofl: "🤣",
|
|
16
|
+
sweat_smile: "😅", wink: "😉", blush: "😊", innocent: "😇", heart_eyes: "😍", star_struck: "🤩",
|
|
17
|
+
kissing_heart: "😘", yum: "😋", stuck_out_tongue: "😛", zany: "🤪", thinking: "🤔", shush: "🤫",
|
|
18
|
+
neutral_face: "😐", expressionless: "😑", no_mouth: "😶", smirk: "😏", unamused: "😒", roll_eyes: "🙄",
|
|
19
|
+
grimacing: "😬", relieved: "😌", pensive: "😔", sleepy: "😪", sleeping: "😴", mask: "😷",
|
|
20
|
+
face_with_thermometer: "🤒", nauseated_face: "🤢", sneezing_face: "🤧", dizzy_face: "😵",
|
|
21
|
+
exploding_head: "🤯", cowboy: "🤠", sunglasses: "😎", nerd: "🤓", monocle: "🧐", confused: "😕",
|
|
22
|
+
worried: "😟", slightly_frowning_face: "🙁", open_mouth: "😮", astonished: "😲", flushed: "😳",
|
|
23
|
+
fearful: "😨", cold_sweat: "😰", cry: "😢", sob: "😭", scream: "😱", confounded: "😖", persevere: "😣",
|
|
24
|
+
disappointed: "😞", sweat: "😓", weary: "😩", tired_face: "😫", triumph: "😤", rage: "😡", angry: "😠",
|
|
25
|
+
cursing_face: "🤬", smiling_imp: "😈", imp: "👿", skull: "💀", poop: "💩", hankey: "💩", clown: "🤡",
|
|
26
|
+
ghost: "👻", alien: "👽", robot: "🤖", heart_eyes_cat: "😻", see_no_evil: "🙈", hear_no_evil: "🙉",
|
|
27
|
+
speak_no_evil: "🙊",
|
|
28
|
+
heart: "❤️", orange_heart: "🧡", yellow_heart: "💛", green_heart: "💚", blue_heart: "💙",
|
|
29
|
+
purple_heart: "💜", black_heart: "🖤", broken_heart: "💔", two_hearts: "💕", sparkling_heart: "💖",
|
|
30
|
+
heartpulse: "💗", cupid: "💘", gift_heart: "💝", revolving_hearts: "💞", kiss: "💋",
|
|
31
|
+
"+1": "👍", thumbsup: "👍", "-1": "👎", thumbsdown: "👎", ok_hand: "👌", punch: "👊", fist: "✊",
|
|
32
|
+
facepunch: "👊", wave: "👋", raised_hand: "✋", raised_hands: "🙌", pray: "🙏", clap: "👏",
|
|
33
|
+
muscle: "💪", point_up: "☝️", point_down: "👇", point_left: "👈", point_right: "👉", v: "✌️",
|
|
34
|
+
crossed_fingers: "🤞", handshake: "🤝", writing_hand: "✍️", selfie: "🤳", nail_care: "💅", eyes: "👀",
|
|
35
|
+
fire: "🔥", star: "⭐", star2: "🌟", sparkles: "✨", zap: "⚡", boom: "💥", collision: "💥",
|
|
36
|
+
dizzy: "💫", sweat_drops: "💦", droplet: "💧", snowflake: "❄️", sunny: "☀️", cloud: "☁️",
|
|
37
|
+
rainbow: "🌈", umbrella: "☔", ocean: "🌊", sun_with_face: "🌞", full_moon: "🌕", crescent_moon: "🌙",
|
|
38
|
+
tada: "🎉", confetti_ball: "🎊", balloon: "🎈", gift: "🎁", trophy: "🏆", medal: "🏅", crown: "👑",
|
|
39
|
+
gem: "💎", rocket: "🚀", airplane: "✈️", car: "🚗", rotating_light: "🚨",
|
|
40
|
+
"100": "💯", white_check_mark: "✅", heavy_check_mark: "✔️", ballot_box_with_check: "☑️", x: "❌",
|
|
41
|
+
warning: "⚠️", no_entry: "⛔", question: "❓", exclamation: "❗", bulb: "💡", lock: "🔒", key: "🔑",
|
|
42
|
+
mag: "🔍", bell: "🔔", recycle: "♻️", heavy_plus_sign: "➕", heavy_minus_sign: "➖",
|
|
43
|
+
coffee: "☕", tea: "🍵", beer: "🍺", beers: "🍻", wine_glass: "🍷", cocktail: "🍸", pizza: "🍕",
|
|
44
|
+
hamburger: "🍔", fries: "🍟", taco: "🌮", cake: "🎂", birthday: "🎂", cookie: "🍪", doughnut: "🍩",
|
|
45
|
+
apple: "🍎", banana: "🍌", strawberry: "🍓", avocado: "🥑", bread: "🍞", cheese: "🧀",
|
|
46
|
+
dog: "🐶", cat: "🐱", mouse: "🐭", rabbit: "🐰", bear: "🐻", panda_face: "🐼", koala: "🐨",
|
|
47
|
+
tiger: "🐯", lion: "🦁", cow: "🐮", pig: "🐷", frog: "🐸", monkey_face: "🐵", chicken: "🐔",
|
|
48
|
+
penguin: "🐧", bird: "🐦", unicorn: "🦄", bee: "🐝", bug: "🐛", butterfly: "🦋", snail: "🐌",
|
|
49
|
+
book: "📖", books: "📚", memo: "📝", pencil: "📝", email: "✉️", envelope: "✉️", calendar: "📅",
|
|
50
|
+
chart: "📊", bar_chart: "📊", chart_with_upwards_trend: "📈", clipboard: "📋", pushpin: "📌",
|
|
51
|
+
paperclip: "📎", scissors: "✂️", hammer: "🔨", wrench: "🔧", gear: "⚙️", nut_and_bolt: "🔩",
|
|
52
|
+
bomb: "💣", hourglass: "⌛", watch: "⌚", alarm_clock: "⏰", moneybag: "💰", dollar: "💵",
|
|
53
|
+
credit_card: "💳", chart_increasing: "📈", link: "🔗", mag_right: "🔎", telescope: "🔭",
|
|
54
|
+
microscope: "🔬", syringe: "💉", pill: "💊", phone: "📞", telephone: "📞", iphone: "📱",
|
|
55
|
+
computer: "💻", keyboard: "⌨️", printer: "🖨️", camera: "📷", movie_camera: "🎥", tv: "📺",
|
|
56
|
+
seedling: "🌱", evergreen_tree: "🌲", deciduous_tree: "🌳", palm_tree: "🌴", cactus: "🌵",
|
|
57
|
+
four_leaf_clover: "🍀", rose: "🌹", sunflower: "🌻", tulip: "🌷", blossom: "🌼", herb: "🌿",
|
|
58
|
+
musical_note: "🎵", notes: "🎶", microphone: "🎤", headphones: "🎧", guitar: "🎸", trumpet: "🎺",
|
|
59
|
+
soccer: "⚽", basketball: "🏀", football: "🏈", baseball: "⚾", tennis: "🎾", "8ball": "🎱",
|
|
60
|
+
dart: "🎯", video_game: "🎮", game_die: "🎲", trophy2: "🏆",
|
|
61
|
+
sos: "🆘", new: "🆕", ok: "🆗", up: "🆙", cool: "🆒", free: "🆓",
|
|
62
|
+
arrow_up: "⬆️", arrow_down: "⬇️", arrow_left: "⬅️", arrow_right: "➡️", arrow_forward: "▶️",
|
|
63
|
+
bangbang: "‼️", interrobang: "⁉️", grey_question: "❔", grey_exclamation: "❕"
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function RTE_Plugin_EmojiAutocomplete() {
|
|
67
|
+
var obj = this;
|
|
68
|
+
var config, editor, editable, editdoc;
|
|
69
|
+
var pop = null, items = [], sel = 0, queryStart = -1, queryNode = null;
|
|
70
|
+
|
|
71
|
+
obj.PluginName = "EmojiAutocomplete";
|
|
72
|
+
|
|
73
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
74
|
+
|
|
75
|
+
obj.InitEditor = function (argeditor) {
|
|
76
|
+
editor = argeditor;
|
|
77
|
+
editable = editor.getEditable();
|
|
78
|
+
editdoc = editor.getDocument();
|
|
79
|
+
if (!editable || !editdoc) return;
|
|
80
|
+
editdoc.addEventListener("input", onInput, false);
|
|
81
|
+
// keydown in capture so we intercept nav keys before the editor does.
|
|
82
|
+
editdoc.addEventListener("keydown", onKeyDown, true);
|
|
83
|
+
editdoc.addEventListener("blur", function () { setTimeout(closePopup, 150); }, true);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function map() { return config.emojiShortcodes || {}; }
|
|
87
|
+
|
|
88
|
+
function getCaret() {
|
|
89
|
+
var s = editor.getSelection();
|
|
90
|
+
if (!s || s.rangeCount === 0 || !s.isCollapsed) return null;
|
|
91
|
+
var r = s.getRangeAt(0);
|
|
92
|
+
if (r.startContainer.nodeType !== 3) return null;
|
|
93
|
+
// never inside code / pre
|
|
94
|
+
var p = r.startContainer.parentNode;
|
|
95
|
+
while (p && p !== editable) { if (p.nodeName === "CODE" || p.nodeName === "PRE") return null; p = p.parentNode; }
|
|
96
|
+
return r;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function onInput() {
|
|
100
|
+
if (!config.emojiAutocompleteEnabled) return;
|
|
101
|
+
var r = getCaret();
|
|
102
|
+
if (!r) { closePopup(); return; }
|
|
103
|
+
var node = r.startContainer, off = r.startOffset;
|
|
104
|
+
var before = node.data.slice(0, off);
|
|
105
|
+
|
|
106
|
+
// 1. complete :shortcode: -> immediate replace
|
|
107
|
+
var full = /(^|[^a-z0-9_])(:[a-z0-9_+\-]{1,30}:)$/i.exec(before);
|
|
108
|
+
if (full) {
|
|
109
|
+
var code = full[2].slice(1, -1).toLowerCase();
|
|
110
|
+
var emo = map()[code];
|
|
111
|
+
if (emo) {
|
|
112
|
+
replaceRange(node, off - full[2].length, off, emo);
|
|
113
|
+
closePopup();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 2. partial :query -> popup
|
|
118
|
+
var part = /(^|[^a-z0-9_])(:)([a-z0-9_+\-]{1,30})$/i.exec(before);
|
|
119
|
+
if (part) {
|
|
120
|
+
var q = part[3].toLowerCase();
|
|
121
|
+
queryNode = node; queryStart = off - q.length - 1; // include the ":"
|
|
122
|
+
showMatches(q, r);
|
|
123
|
+
} else {
|
|
124
|
+
closePopup();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function showMatches(q, range) {
|
|
129
|
+
var m = map(), keys = Object.keys(m), starts = [], contains = [];
|
|
130
|
+
for (var i = 0; i < keys.length; i++) {
|
|
131
|
+
var k = keys[i];
|
|
132
|
+
if (k.indexOf(q) === 0) starts.push(k);
|
|
133
|
+
else if (k.indexOf(q) !== -1) contains.push(k);
|
|
134
|
+
}
|
|
135
|
+
// de-dupe emoji glyphs while keeping the first (best) shortcode
|
|
136
|
+
var ordered = starts.concat(contains), seen = {}, list = [];
|
|
137
|
+
for (var j = 0; j < ordered.length && list.length < 8; j++) {
|
|
138
|
+
var emo = m[ordered[j]];
|
|
139
|
+
if (seen[emo]) continue;
|
|
140
|
+
seen[emo] = 1; list.push({ code: ordered[j], emo: emo });
|
|
141
|
+
}
|
|
142
|
+
if (!list.length) { closePopup(); return; }
|
|
143
|
+
items = list; sel = 0;
|
|
144
|
+
renderPopup(range);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function renderPopup(range) {
|
|
148
|
+
var hostDoc = document;
|
|
149
|
+
if (!pop) {
|
|
150
|
+
pop = hostDoc.createElement("div");
|
|
151
|
+
pop.className = "rte-emoji-pop";
|
|
152
|
+
pop.setAttribute("role", "listbox");
|
|
153
|
+
pop.setAttribute("aria-label", "Emoji suggestions");
|
|
154
|
+
pop.style.cssText = "position:fixed;z-index:2147483600;";
|
|
155
|
+
hostDoc.body.appendChild(pop);
|
|
156
|
+
}
|
|
157
|
+
pop.innerHTML = "";
|
|
158
|
+
items.forEach(function (it, i) {
|
|
159
|
+
var row = hostDoc.createElement("div");
|
|
160
|
+
row.className = "rte-emoji-pop-row" + (i === sel ? " is-active" : "");
|
|
161
|
+
row.setAttribute("role", "option");
|
|
162
|
+
row.setAttribute("aria-selected", i === sel ? "true" : "false");
|
|
163
|
+
var glyph = hostDoc.createElement("span");
|
|
164
|
+
glyph.className = "rte-emoji-pop-glyph";
|
|
165
|
+
glyph.textContent = it.emo;
|
|
166
|
+
var code = hostDoc.createElement("span");
|
|
167
|
+
code.className = "rte-emoji-pop-code";
|
|
168
|
+
code.textContent = ":" + it.code + ":";
|
|
169
|
+
row.appendChild(glyph);
|
|
170
|
+
row.appendChild(code);
|
|
171
|
+
row.onmousedown = function (e) { e.preventDefault(); sel = i; commit(); };
|
|
172
|
+
row.onmouseenter = function () { sel = i; highlightRows(); };
|
|
173
|
+
pop.appendChild(row);
|
|
174
|
+
});
|
|
175
|
+
// position near the caret (iframe rect + caret rect)
|
|
176
|
+
try {
|
|
177
|
+
var cr = range.getBoundingClientRect();
|
|
178
|
+
var fr = (editdoc.defaultView && editdoc.defaultView.frameElement) ? editdoc.defaultView.frameElement.getBoundingClientRect() : { left: 0, top: 0 };
|
|
179
|
+
var x = fr.left + cr.left, y = fr.top + cr.bottom + 4;
|
|
180
|
+
var vw = hostDoc.documentElement.clientWidth, vh = hostDoc.documentElement.clientHeight;
|
|
181
|
+
pop.style.left = Math.max(4, Math.min(x, vw - 290)) + "px";
|
|
182
|
+
pop.style.top = Math.max(4, Math.min(y, vh - 20)) + "px";
|
|
183
|
+
pop.style.display = "block";
|
|
184
|
+
} catch (e) { /* ignore */ }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function highlightRows() {
|
|
188
|
+
if (!pop) return;
|
|
189
|
+
var rows = pop.childNodes;
|
|
190
|
+
for (var i = 0; i < rows.length; i++) {
|
|
191
|
+
rows[i].classList.toggle("is-active", i === sel);
|
|
192
|
+
rows[i].setAttribute("aria-selected", i === sel ? "true" : "false");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function onKeyDown(e) {
|
|
197
|
+
if (!pop || pop.style.display === "none" || !items.length) return;
|
|
198
|
+
switch (e.key) {
|
|
199
|
+
case "ArrowDown": e.preventDefault(); e.stopPropagation(); sel = (sel + 1) % items.length; highlightRows(); break;
|
|
200
|
+
case "ArrowUp": e.preventDefault(); e.stopPropagation(); sel = (sel - 1 + items.length) % items.length; highlightRows(); break;
|
|
201
|
+
case "Enter":
|
|
202
|
+
case "Tab": e.preventDefault(); e.stopPropagation(); commit(); break;
|
|
203
|
+
case "Escape": e.preventDefault(); e.stopPropagation(); closePopup(); break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function commit() {
|
|
208
|
+
if (!items.length || !queryNode || queryStart < 0) { closePopup(); return; }
|
|
209
|
+
var it = items[sel] || items[0];
|
|
210
|
+
var r = getCaret();
|
|
211
|
+
if (!r || r.startContainer !== queryNode) { closePopup(); return; }
|
|
212
|
+
replaceRange(queryNode, queryStart, r.startOffset, it.emo);
|
|
213
|
+
closePopup();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function replaceRange(node, start, end, text) {
|
|
217
|
+
try {
|
|
218
|
+
var data = node.data;
|
|
219
|
+
var head = data.slice(0, start), tail = data.slice(end);
|
|
220
|
+
var parent = node.parentNode; if (!parent) return;
|
|
221
|
+
var frag = editdoc.createDocumentFragment();
|
|
222
|
+
if (head) frag.appendChild(editdoc.createTextNode(head));
|
|
223
|
+
var emoNode = editdoc.createTextNode(text);
|
|
224
|
+
frag.appendChild(emoNode);
|
|
225
|
+
var tailNode = tail ? editdoc.createTextNode(tail) : null;
|
|
226
|
+
if (tailNode) frag.appendChild(tailNode);
|
|
227
|
+
parent.replaceChild(frag, node);
|
|
228
|
+
var rr = editdoc.createRange();
|
|
229
|
+
if (tailNode) rr.setStart(tailNode, 0); else rr.setStartAfter(emoNode);
|
|
230
|
+
rr.collapse(true);
|
|
231
|
+
var s = editor.getSelection();
|
|
232
|
+
if (s) { s.removeAllRanges(); s.addRange(rr); }
|
|
233
|
+
if (typeof editor.fireChange === "function") { try { editor.fireChange(); } catch (e) {} }
|
|
234
|
+
} catch (e) { /* ignore */ }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function closePopup() {
|
|
238
|
+
if (pop) pop.style.display = "none";
|
|
239
|
+
items = []; sel = 0; queryStart = -1; queryNode = null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-06-06 Foldable headings. Obsidian / Notion / Word-style section folding:
|
|
4
|
+
// click the gutter chevron next to a heading (or call the API) to collapse every
|
|
5
|
+
// block beneath it, up to the next heading of the same or higher level. Great for
|
|
6
|
+
// navigating long structured documents.
|
|
7
|
+
//
|
|
8
|
+
// Serialization-safe by design: the fold state lives ONLY in runtime CSS classes
|
|
9
|
+
// (rte-fold-collapsed on the heading, rte-fold-hidden on the blocks it hides) and
|
|
10
|
+
// the chevron is a CSS ::before pseudo-element — no DOM is injected into the
|
|
11
|
+
// content. getHTMLCode / getJSON are wrapped to strip those classes around the
|
|
12
|
+
// call, so saved markup is always clean and the folded content is never lost.
|
|
13
|
+
RTE_DefaultConfig.plugin_foldheadings = RTE_Plugin_FoldHeadings;
|
|
14
|
+
RTE_DefaultConfig.foldHeadingsEnabled = (typeof RTE_DefaultConfig.foldHeadingsEnabled === "undefined")
|
|
15
|
+
? true : RTE_DefaultConfig.foldHeadingsEnabled;
|
|
16
|
+
|
|
17
|
+
function RTE_Plugin_FoldHeadings() {
|
|
18
|
+
var obj = this;
|
|
19
|
+
var config, editor;
|
|
20
|
+
var wrapped = false, clickDoc = null;
|
|
21
|
+
|
|
22
|
+
obj.PluginName = "FoldHeadings";
|
|
23
|
+
|
|
24
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
25
|
+
|
|
26
|
+
obj.InitEditor = function (argeditor) {
|
|
27
|
+
editor = argeditor;
|
|
28
|
+
setup();
|
|
29
|
+
try { editor.attachEvent("ready", setup); } catch (e) { /* ignore */ }
|
|
30
|
+
try { editor.attachEvent("aftersethtml", setup); } catch (e) { /* ignore */ }
|
|
31
|
+
setTimeout(setup, 0); setTimeout(setup, 200);
|
|
32
|
+
// Public API.
|
|
33
|
+
editor.toggleHeadingFold = function (h) { if (isHeading(h)) toggleFold(h); };
|
|
34
|
+
editor.unfoldAll = function () { var ed = editor.getEditable(); if (!ed) return; rm(ed.querySelectorAll(".rte-fold-collapsed,.rte-fold-hidden")); };
|
|
35
|
+
editor.foldAtCaret = function () { var h = headingAtCaret(); if (h) toggleFold(h); return !!h; };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Re-runnable setup (the editor can recreate the document/body after init).
|
|
39
|
+
function setup() {
|
|
40
|
+
var doc = editor.getDocument();
|
|
41
|
+
if (doc) {
|
|
42
|
+
injectStyles(doc);
|
|
43
|
+
if (doc !== clickDoc) { doc.addEventListener("click", onClick, true); clickDoc = doc; }
|
|
44
|
+
}
|
|
45
|
+
wrapSerializers();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isHeading(n) { return n && n.nodeType === 1 && /^H[1-6]$/.test(n.nodeName); }
|
|
49
|
+
function level(n) { return isHeading(n) ? +n.nodeName.charAt(1) : 0; }
|
|
50
|
+
function rm(list) { for (var i = 0; i < list.length; i++) { list[i].classList.remove("rte-fold-collapsed"); list[i].classList.remove("rte-fold-hidden"); } }
|
|
51
|
+
|
|
52
|
+
// Blocks from after `heading` up to the next heading of <= its level.
|
|
53
|
+
function blocksUnder(heading) {
|
|
54
|
+
var lv = level(heading), out = [], n = heading.nextElementSibling;
|
|
55
|
+
while (n) {
|
|
56
|
+
if (isHeading(n) && level(n) <= lv) break;
|
|
57
|
+
out.push(n);
|
|
58
|
+
n = n.nextElementSibling;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The heading of the section the caret is in: the caret's own heading, else
|
|
64
|
+
// the nearest preceding top-level heading sibling.
|
|
65
|
+
function headingAtCaret() {
|
|
66
|
+
try {
|
|
67
|
+
var s = editor.getSelection(); if (!s || s.rangeCount === 0) return null;
|
|
68
|
+
var editable = editor.getEditable();
|
|
69
|
+
var n = s.getRangeAt(0).startContainer;
|
|
70
|
+
while (n && n !== editable) { if (isHeading(n)) return n; n = n.parentNode; }
|
|
71
|
+
// walk up to the top-level block, then back to the nearest heading
|
|
72
|
+
var block = s.getRangeAt(0).startContainer;
|
|
73
|
+
while (block && block.parentNode !== editable && block !== editable) block = block.parentNode;
|
|
74
|
+
while (block && block !== editable) { if (isHeading(block)) return block; block = block.previousElementSibling; }
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function toggleFold(heading) {
|
|
80
|
+
if (heading.classList.contains("rte-fold-collapsed")) unfold(heading);
|
|
81
|
+
else fold(heading);
|
|
82
|
+
}
|
|
83
|
+
function fold(heading) {
|
|
84
|
+
var blocks = blocksUnder(heading);
|
|
85
|
+
if (!blocks.length) return;
|
|
86
|
+
// Caret safety: if the caret sits in a block we're about to hide, move it
|
|
87
|
+
// to the end of the heading so it never ends up stuck in hidden content.
|
|
88
|
+
try {
|
|
89
|
+
var s = editor.getSelection();
|
|
90
|
+
if (s && s.rangeCount) {
|
|
91
|
+
var c = s.getRangeAt(0).startContainer, host = (c.nodeType === 1 ? c : c.parentNode);
|
|
92
|
+
for (var k = 0; k < blocks.length; k++) {
|
|
93
|
+
if (blocks[k] === host || blocks[k].contains(host)) {
|
|
94
|
+
var doc = editor.getDocument(), r = doc.createRange();
|
|
95
|
+
r.selectNodeContents(heading); r.collapse(false);
|
|
96
|
+
s.removeAllRanges(); s.addRange(r);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {}
|
|
102
|
+
heading.classList.add("rte-fold-collapsed");
|
|
103
|
+
for (var i = 0; i < blocks.length; i++) blocks[i].classList.add("rte-fold-hidden");
|
|
104
|
+
fireChange();
|
|
105
|
+
}
|
|
106
|
+
function unfold(heading) {
|
|
107
|
+
heading.classList.remove("rte-fold-collapsed");
|
|
108
|
+
var blocks = blocksUnder(heading);
|
|
109
|
+
for (var i = 0; i < blocks.length; i++) blocks[i].classList.remove("rte-fold-hidden");
|
|
110
|
+
fireChange();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function onClick(e) {
|
|
114
|
+
if (!config.foldHeadingsEnabled) return;
|
|
115
|
+
var t = e.target;
|
|
116
|
+
if (!isHeading(t)) return;
|
|
117
|
+
// Only the left gutter chevron (rendered at left:-0.95em, just OUTSIDE
|
|
118
|
+
// the heading box, so its click offset is negative) toggles. Clicking
|
|
119
|
+
// anywhere in the heading text (offset >= 0) places the caret normally.
|
|
120
|
+
var rect = t.getBoundingClientRect();
|
|
121
|
+
var dx = e.clientX - rect.left;
|
|
122
|
+
if (dx > 2 || dx < -40) return;
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
toggleFold(t);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function fireChange() { try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) {} }
|
|
128
|
+
|
|
129
|
+
function injectStyles(doc) {
|
|
130
|
+
if (!doc || doc.getElementById("rte-foldheadings-styles")) return;
|
|
131
|
+
var css =
|
|
132
|
+
".rte-fold-hidden{display:none !important;}" +
|
|
133
|
+
"h1,h2,h3,h4,h5,h6{position:relative;}" +
|
|
134
|
+
"h1::before,h2::before,h3::before,h4::before,h5::before,h6::before{" +
|
|
135
|
+
"content:'\\25BE';position:absolute;left:-0.95em;top:0;width:0.8em;text-align:center;" +
|
|
136
|
+
"color:#cbd5e1;cursor:pointer;opacity:0;transition:opacity .12s,transform .12s;font-size:.9em;}" +
|
|
137
|
+
"h1:hover::before,h2:hover::before,h3:hover::before,h4:hover::before,h5:hover::before,h6:hover::before{opacity:1;}" +
|
|
138
|
+
"h1.rte-fold-collapsed::before,h2.rte-fold-collapsed::before,h3.rte-fold-collapsed::before," +
|
|
139
|
+
"h4.rte-fold-collapsed::before,h5.rte-fold-collapsed::before,h6.rte-fold-collapsed::before{" +
|
|
140
|
+
"content:'\\25B8';opacity:1;color:#1d67ba;}" +
|
|
141
|
+
"@media (prefers-reduced-motion: reduce){h1::before,h2::before,h3::before,h4::before,h5::before,h6::before{transition:none !important;}}" +
|
|
142
|
+
// Touch / no-hover devices can't reveal a hover-only chevron — keep it
|
|
143
|
+
// visible so headings remain foldable by tap.
|
|
144
|
+
"@media (hover: none),(pointer: coarse){h1::before,h2::before,h3::before,h4::before,h5::before,h6::before{opacity:.5;}}";
|
|
145
|
+
var st = doc.createElement("style");
|
|
146
|
+
st.id = "rte-foldheadings-styles";
|
|
147
|
+
st.appendChild(doc.createTextNode(css));
|
|
148
|
+
(doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(st);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Strip fold classes for the duration of a serialization call so saved markup
|
|
152
|
+
// is clean and folded content is never lost.
|
|
153
|
+
function stripFor() {
|
|
154
|
+
var ed = editor.getEditable(); if (!ed) return function () {};
|
|
155
|
+
var collapsed = ed.querySelectorAll(".rte-fold-collapsed");
|
|
156
|
+
var hidden = ed.querySelectorAll(".rte-fold-hidden");
|
|
157
|
+
var i;
|
|
158
|
+
for (i = 0; i < collapsed.length; i++) collapsed[i].classList.remove("rte-fold-collapsed");
|
|
159
|
+
for (i = 0; i < hidden.length; i++) hidden[i].classList.remove("rte-fold-hidden");
|
|
160
|
+
return function restore() {
|
|
161
|
+
for (var j = 0; j < collapsed.length; j++) collapsed[j].classList.add("rte-fold-collapsed");
|
|
162
|
+
for (var k = 0; k < hidden.length; k++) hidden[k].classList.add("rte-fold-hidden");
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function wrapSerializers() {
|
|
166
|
+
if (wrapped) return;
|
|
167
|
+
var names = ["getHTMLCode", "getJSON", "getHTMLContent"];
|
|
168
|
+
var did = false;
|
|
169
|
+
for (var i = 0; i < names.length; i++) {
|
|
170
|
+
(function (name) {
|
|
171
|
+
var orig = editor[name];
|
|
172
|
+
if (typeof orig !== "function" || orig.__rteFoldWrapped) return;
|
|
173
|
+
var w = function () { var restore = stripFor(); try { return orig.apply(editor, arguments); } finally { restore(); } };
|
|
174
|
+
w.__rteFoldWrapped = true;
|
|
175
|
+
editor[name] = w;
|
|
176
|
+
did = true;
|
|
177
|
+
})(names[i]);
|
|
178
|
+
}
|
|
179
|
+
if (did) wrapped = true;
|
|
180
|
+
}
|
|
181
|
+
}
|