123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- /**
- * TinyMCE version 6.4.2 (2023-04-26)
- */
- (function () {
- 'use strict';
- var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
- const eq = t => a => t === a;
- const isNull = eq(null);
- const isUndefined = eq(undefined);
- const isNullable = a => a === null || a === undefined;
- const isNonNullable = a => !isNullable(a);
- const noop = () => {
- };
- const constant = value => {
- return () => {
- return value;
- };
- };
- const never = constant(false);
- class Optional {
- constructor(tag, value) {
- this.tag = tag;
- this.value = value;
- }
- static some(value) {
- return new Optional(true, value);
- }
- static none() {
- return Optional.singletonNone;
- }
- fold(onNone, onSome) {
- if (this.tag) {
- return onSome(this.value);
- } else {
- return onNone();
- }
- }
- isSome() {
- return this.tag;
- }
- isNone() {
- return !this.tag;
- }
- map(mapper) {
- if (this.tag) {
- return Optional.some(mapper(this.value));
- } else {
- return Optional.none();
- }
- }
- bind(binder) {
- if (this.tag) {
- return binder(this.value);
- } else {
- return Optional.none();
- }
- }
- exists(predicate) {
- return this.tag && predicate(this.value);
- }
- forall(predicate) {
- return !this.tag || predicate(this.value);
- }
- filter(predicate) {
- if (!this.tag || predicate(this.value)) {
- return this;
- } else {
- return Optional.none();
- }
- }
- getOr(replacement) {
- return this.tag ? this.value : replacement;
- }
- or(replacement) {
- return this.tag ? this : replacement;
- }
- getOrThunk(thunk) {
- return this.tag ? this.value : thunk();
- }
- orThunk(thunk) {
- return this.tag ? this : thunk();
- }
- getOrDie(message) {
- if (!this.tag) {
- throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
- } else {
- return this.value;
- }
- }
- static from(value) {
- return isNonNullable(value) ? Optional.some(value) : Optional.none();
- }
- getOrNull() {
- return this.tag ? this.value : null;
- }
- getOrUndefined() {
- return this.value;
- }
- each(worker) {
- if (this.tag) {
- worker(this.value);
- }
- }
- toArray() {
- return this.tag ? [this.value] : [];
- }
- toString() {
- return this.tag ? `some(${ this.value })` : 'none()';
- }
- }
- Optional.singletonNone = new Optional(false);
- const exists = (xs, pred) => {
- for (let i = 0, len = xs.length; i < len; i++) {
- const x = xs[i];
- if (pred(x, i)) {
- return true;
- }
- }
- return false;
- };
- const map$1 = (xs, f) => {
- const len = xs.length;
- const r = new Array(len);
- for (let i = 0; i < len; i++) {
- const x = xs[i];
- r[i] = f(x, i);
- }
- return r;
- };
- const each$1 = (xs, f) => {
- for (let i = 0, len = xs.length; i < len; i++) {
- const x = xs[i];
- f(x, i);
- }
- };
- const Cell = initial => {
- let value = initial;
- const get = () => {
- return value;
- };
- const set = v => {
- value = v;
- };
- return {
- get,
- set
- };
- };
- const last = (fn, rate) => {
- let timer = null;
- const cancel = () => {
- if (!isNull(timer)) {
- clearTimeout(timer);
- timer = null;
- }
- };
- const throttle = (...args) => {
- cancel();
- timer = setTimeout(() => {
- timer = null;
- fn.apply(null, args);
- }, rate);
- };
- return {
- cancel,
- throttle
- };
- };
- const insertEmoticon = (editor, ch) => {
- editor.insertContent(ch);
- };
- const keys = Object.keys;
- const hasOwnProperty = Object.hasOwnProperty;
- const each = (obj, f) => {
- const props = keys(obj);
- for (let k = 0, len = props.length; k < len; k++) {
- const i = props[k];
- const x = obj[i];
- f(x, i);
- }
- };
- const map = (obj, f) => {
- return tupleMap(obj, (x, i) => ({
- k: i,
- v: f(x, i)
- }));
- };
- const tupleMap = (obj, f) => {
- const r = {};
- each(obj, (x, i) => {
- const tuple = f(x, i);
- r[tuple.k] = tuple.v;
- });
- return r;
- };
- const has = (obj, key) => hasOwnProperty.call(obj, key);
- const shallow = (old, nu) => {
- return nu;
- };
- const baseMerge = merger => {
- return (...objects) => {
- if (objects.length === 0) {
- throw new Error(`Can't merge zero objects`);
- }
- const ret = {};
- for (let j = 0; j < objects.length; j++) {
- const curObject = objects[j];
- for (const key in curObject) {
- if (has(curObject, key)) {
- ret[key] = merger(ret[key], curObject[key]);
- }
- }
- }
- return ret;
- };
- };
- const merge = baseMerge(shallow);
- const singleton = doRevoke => {
- const subject = Cell(Optional.none());
- const revoke = () => subject.get().each(doRevoke);
- const clear = () => {
- revoke();
- subject.set(Optional.none());
- };
- const isSet = () => subject.get().isSome();
- const get = () => subject.get();
- const set = s => {
- revoke();
- subject.set(Optional.some(s));
- };
- return {
- clear,
- isSet,
- get,
- set
- };
- };
- const value = () => {
- const subject = singleton(noop);
- const on = f => subject.get().each(f);
- return {
- ...subject,
- on
- };
- };
- const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
- const contains = (str, substr, start = 0, end) => {
- const idx = str.indexOf(substr, start);
- if (idx !== -1) {
- return isUndefined(end) ? true : idx + substr.length <= end;
- } else {
- return false;
- }
- };
- const startsWith = (str, prefix) => {
- return checkRange(str, prefix, 0);
- };
- var global = tinymce.util.Tools.resolve('tinymce.Resource');
- const DEFAULT_ID = 'tinymce.plugins.emoticons';
- const option = name => editor => editor.options.get(name);
- const register$2 = (editor, pluginUrl) => {
- const registerOption = editor.options.register;
- registerOption('emoticons_database', {
- processor: 'string',
- default: 'emojis'
- });
- registerOption('emoticons_database_url', {
- processor: 'string',
- default: `${ pluginUrl }/js/${ getEmojiDatabase(editor) }${ editor.suffix }.js`
- });
- registerOption('emoticons_database_id', {
- processor: 'string',
- default: DEFAULT_ID
- });
- registerOption('emoticons_append', {
- processor: 'object',
- default: {}
- });
- registerOption('emoticons_images_url', {
- processor: 'string',
- default: 'https://twemoji.maxcdn.com/v/13.0.1/72x72/'
- });
- };
- const getEmojiDatabase = option('emoticons_database');
- const getEmojiDatabaseUrl = option('emoticons_database_url');
- const getEmojiDatabaseId = option('emoticons_database_id');
- const getAppendedEmoji = option('emoticons_append');
- const getEmojiImageUrl = option('emoticons_images_url');
- const ALL_CATEGORY = 'All';
- const categoryNameMap = {
- symbols: 'Symbols',
- people: 'People',
- animals_and_nature: 'Animals and Nature',
- food_and_drink: 'Food and Drink',
- activity: 'Activity',
- travel_and_places: 'Travel and Places',
- objects: 'Objects',
- flags: 'Flags',
- user: 'User Defined'
- };
- const translateCategory = (categories, name) => has(categories, name) ? categories[name] : name;
- const getUserDefinedEmoji = editor => {
- const userDefinedEmoticons = getAppendedEmoji(editor);
- return map(userDefinedEmoticons, value => ({
- keywords: [],
- category: 'user',
- ...value
- }));
- };
- const initDatabase = (editor, databaseUrl, databaseId) => {
- const categories = value();
- const all = value();
- const emojiImagesUrl = getEmojiImageUrl(editor);
- const getEmoji = lib => {
- if (startsWith(lib.char, '<img')) {
- return lib.char.replace(/src="([^"]+)"/, (match, url) => `src="${ emojiImagesUrl }${ url }"`);
- } else {
- return lib.char;
- }
- };
- const processEmojis = emojis => {
- const cats = {};
- const everything = [];
- each(emojis, (lib, title) => {
- const entry = {
- title,
- keywords: lib.keywords,
- char: getEmoji(lib),
- category: translateCategory(categoryNameMap, lib.category)
- };
- const current = cats[entry.category] !== undefined ? cats[entry.category] : [];
- cats[entry.category] = current.concat([entry]);
- everything.push(entry);
- });
- categories.set(cats);
- all.set(everything);
- };
- editor.on('init', () => {
- global.load(databaseId, databaseUrl).then(emojis => {
- const userEmojis = getUserDefinedEmoji(editor);
- processEmojis(merge(emojis, userEmojis));
- }, err => {
- console.log(`Failed to load emojis: ${ err }`);
- categories.set({});
- all.set([]);
- });
- });
- const listCategory = category => {
- if (category === ALL_CATEGORY) {
- return listAll();
- }
- return categories.get().bind(cats => Optional.from(cats[category])).getOr([]);
- };
- const listAll = () => all.get().getOr([]);
- const listCategories = () => [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
- const waitForLoad = () => {
- if (hasLoaded()) {
- return Promise.resolve(true);
- } else {
- return new Promise((resolve, reject) => {
- let numRetries = 15;
- const interval = setInterval(() => {
- if (hasLoaded()) {
- clearInterval(interval);
- resolve(true);
- } else {
- numRetries--;
- if (numRetries < 0) {
- console.log('Could not load emojis from url: ' + databaseUrl);
- clearInterval(interval);
- reject(false);
- }
- }
- }, 100);
- });
- }
- };
- const hasLoaded = () => categories.isSet() && all.isSet();
- return {
- listCategories,
- hasLoaded,
- waitForLoad,
- listAll,
- listCategory
- };
- };
- const emojiMatches = (emoji, lowerCasePattern) => contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, k => contains(k.toLowerCase(), lowerCasePattern));
- const emojisFrom = (list, pattern, maxResults) => {
- const matches = [];
- const lowerCasePattern = pattern.toLowerCase();
- const reachedLimit = maxResults.fold(() => never, max => size => size >= max);
- for (let i = 0; i < list.length; i++) {
- if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {
- matches.push({
- value: list[i].char,
- text: list[i].title,
- icon: list[i].char
- });
- if (reachedLimit(matches.length)) {
- break;
- }
- }
- }
- return matches;
- };
- const patternName = 'pattern';
- const open = (editor, database) => {
- const initialState = {
- pattern: '',
- results: emojisFrom(database.listAll(), '', Optional.some(300))
- };
- const currentTab = Cell(ALL_CATEGORY);
- const scan = dialogApi => {
- const dialogData = dialogApi.getData();
- const category = currentTab.get();
- const candidates = database.listCategory(category);
- const results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());
- dialogApi.setData({ results });
- };
- const updateFilter = last(dialogApi => {
- scan(dialogApi);
- }, 200);
- const searchField = {
- label: 'Search',
- type: 'input',
- name: patternName
- };
- const resultsField = {
- type: 'collection',
- name: 'results'
- };
- const getInitialState = () => {
- const body = {
- type: 'tabpanel',
- tabs: map$1(database.listCategories(), cat => ({
- title: cat,
- name: cat,
- items: [
- searchField,
- resultsField
- ]
- }))
- };
- return {
- title: 'Emojis',
- size: 'normal',
- body,
- initialData: initialState,
- onTabChange: (dialogApi, details) => {
- currentTab.set(details.newTabName);
- updateFilter.throttle(dialogApi);
- },
- onChange: updateFilter.throttle,
- onAction: (dialogApi, actionData) => {
- if (actionData.name === 'results') {
- insertEmoticon(editor, actionData.value);
- dialogApi.close();
- }
- },
- buttons: [{
- type: 'cancel',
- text: 'Close',
- primary: true
- }]
- };
- };
- const dialogApi = editor.windowManager.open(getInitialState());
- dialogApi.focus(patternName);
- if (!database.hasLoaded()) {
- dialogApi.block('Loading emojis...');
- database.waitForLoad().then(() => {
- dialogApi.redial(getInitialState());
- updateFilter.throttle(dialogApi);
- dialogApi.focus(patternName);
- dialogApi.unblock();
- }).catch(_err => {
- dialogApi.redial({
- title: 'Emojis',
- body: {
- type: 'panel',
- items: [{
- type: 'alertbanner',
- level: 'error',
- icon: 'warning',
- text: 'Could not load emojis'
- }]
- },
- buttons: [{
- type: 'cancel',
- text: 'Close',
- primary: true
- }],
- initialData: {
- pattern: '',
- results: []
- }
- });
- dialogApi.focus(patternName);
- dialogApi.unblock();
- });
- }
- };
- const register$1 = (editor, database) => {
- editor.addCommand('mceEmoticons', () => open(editor, database));
- };
- const setup = editor => {
- editor.on('PreInit', () => {
- editor.parser.addAttributeFilter('data-emoticon', nodes => {
- each$1(nodes, node => {
- node.attr('data-mce-resize', 'false');
- node.attr('data-mce-placeholder', '1');
- });
- });
- });
- };
- const init = (editor, database) => {
- editor.ui.registry.addAutocompleter('emoticons', {
- trigger: ':',
- columns: 'auto',
- minChars: 2,
- fetch: (pattern, maxResults) => database.waitForLoad().then(() => {
- const candidates = database.listAll();
- return emojisFrom(candidates, pattern, Optional.some(maxResults));
- }),
- onAction: (autocompleteApi, rng, value) => {
- editor.selection.setRng(rng);
- editor.insertContent(value);
- autocompleteApi.hide();
- }
- });
- };
- const register = editor => {
- const onAction = () => editor.execCommand('mceEmoticons');
- editor.ui.registry.addButton('emoticons', {
- tooltip: 'Emojis',
- icon: 'emoji',
- onAction
- });
- editor.ui.registry.addMenuItem('emoticons', {
- text: 'Emojis...',
- icon: 'emoji',
- onAction
- });
- };
- var Plugin = () => {
- global$1.add('emoticons', (editor, pluginUrl) => {
- register$2(editor, pluginUrl);
- const databaseUrl = getEmojiDatabaseUrl(editor);
- const databaseId = getEmojiDatabaseId(editor);
- const database = initDatabase(editor, databaseUrl, databaseId);
- register$1(editor, database);
- register(editor);
- init(editor, database);
- setup(editor);
- });
- };
- Plugin();
- })();
|