606 lines
17 KiB
JavaScript
606 lines
17 KiB
JavaScript
/**
|
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
|
* Licensed under the LGPL or a commercial license.
|
|
* For LGPL see License.txt in the project root for license information.
|
|
* For commercial licenses see https://www.tiny.cloud/
|
|
*
|
|
* Version: 5.7.0 (2021-02-10)
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
|
|
|
var DEFAULT_ID = 'tinymce.plugins.emoticons';
|
|
var getEmoticonDatabase = function (editor) {
|
|
return editor.getParam('emoticons_database', 'emojis', 'string');
|
|
};
|
|
var getEmoticonDatabaseUrl = function (editor, pluginUrl) {
|
|
var database = getEmoticonDatabase(editor);
|
|
return editor.getParam('emoticons_database_url', pluginUrl + '/js/' + database + editor.suffix + '.js', 'string');
|
|
};
|
|
var getEmoticonDatabaseId = function (editor) {
|
|
return editor.getParam('emoticons_database_id', DEFAULT_ID, 'string');
|
|
};
|
|
var getAppendedEmoticons = function (editor) {
|
|
return editor.getParam('emoticons_append', {}, 'object');
|
|
};
|
|
var getEmotionsImageUrl = function (editor) {
|
|
return editor.getParam('emoticons_images_url', 'https://twemoji.maxcdn.com/v/13.0.1/72x72/', 'string');
|
|
};
|
|
|
|
var __assign = function () {
|
|
__assign = Object.assign || function __assign(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s)
|
|
if (Object.prototype.hasOwnProperty.call(s, p))
|
|
t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
|
|
var Cell = function (initial) {
|
|
var value = initial;
|
|
var get = function () {
|
|
return value;
|
|
};
|
|
var set = function (v) {
|
|
value = v;
|
|
};
|
|
return {
|
|
get: get,
|
|
set: set
|
|
};
|
|
};
|
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
var shallow = function (old, nu) {
|
|
return nu;
|
|
};
|
|
var baseMerge = function (merger) {
|
|
return function () {
|
|
var objects = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
objects[_i] = arguments[_i];
|
|
}
|
|
if (objects.length === 0) {
|
|
throw new Error('Can\'t merge zero objects');
|
|
}
|
|
var ret = {};
|
|
for (var j = 0; j < objects.length; j++) {
|
|
var curObject = objects[j];
|
|
for (var key in curObject) {
|
|
if (hasOwnProperty.call(curObject, key)) {
|
|
ret[key] = merger(ret[key], curObject[key]);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
};
|
|
var merge = baseMerge(shallow);
|
|
|
|
var noop = function () {
|
|
};
|
|
var constant = function (value) {
|
|
return function () {
|
|
return value;
|
|
};
|
|
};
|
|
var never = constant(false);
|
|
var always = constant(true);
|
|
|
|
var none = function () {
|
|
return NONE;
|
|
};
|
|
var NONE = function () {
|
|
var eq = function (o) {
|
|
return o.isNone();
|
|
};
|
|
var call = function (thunk) {
|
|
return thunk();
|
|
};
|
|
var id = function (n) {
|
|
return n;
|
|
};
|
|
var me = {
|
|
fold: function (n, _s) {
|
|
return n();
|
|
},
|
|
is: never,
|
|
isSome: never,
|
|
isNone: always,
|
|
getOr: id,
|
|
getOrThunk: call,
|
|
getOrDie: function (msg) {
|
|
throw new Error(msg || 'error: getOrDie called on none.');
|
|
},
|
|
getOrNull: constant(null),
|
|
getOrUndefined: constant(undefined),
|
|
or: id,
|
|
orThunk: call,
|
|
map: none,
|
|
each: noop,
|
|
bind: none,
|
|
exists: never,
|
|
forall: always,
|
|
filter: none,
|
|
equals: eq,
|
|
equals_: eq,
|
|
toArray: function () {
|
|
return [];
|
|
},
|
|
toString: constant('none()')
|
|
};
|
|
return me;
|
|
}();
|
|
var some = function (a) {
|
|
var constant_a = constant(a);
|
|
var self = function () {
|
|
return me;
|
|
};
|
|
var bind = function (f) {
|
|
return f(a);
|
|
};
|
|
var me = {
|
|
fold: function (n, s) {
|
|
return s(a);
|
|
},
|
|
is: function (v) {
|
|
return a === v;
|
|
},
|
|
isSome: always,
|
|
isNone: never,
|
|
getOr: constant_a,
|
|
getOrThunk: constant_a,
|
|
getOrDie: constant_a,
|
|
getOrNull: constant_a,
|
|
getOrUndefined: constant_a,
|
|
or: self,
|
|
orThunk: self,
|
|
map: function (f) {
|
|
return some(f(a));
|
|
},
|
|
each: function (f) {
|
|
f(a);
|
|
},
|
|
bind: bind,
|
|
exists: bind,
|
|
forall: bind,
|
|
filter: function (f) {
|
|
return f(a) ? me : NONE;
|
|
},
|
|
toArray: function () {
|
|
return [a];
|
|
},
|
|
toString: function () {
|
|
return 'some(' + a + ')';
|
|
},
|
|
equals: function (o) {
|
|
return o.is(a);
|
|
},
|
|
equals_: function (o, elementEq) {
|
|
return o.fold(never, function (b) {
|
|
return elementEq(a, b);
|
|
});
|
|
}
|
|
};
|
|
return me;
|
|
};
|
|
var from = function (value) {
|
|
return value === null || value === undefined ? NONE : some(value);
|
|
};
|
|
var Optional = {
|
|
some: some,
|
|
none: none,
|
|
from: from
|
|
};
|
|
|
|
var keys = Object.keys;
|
|
var hasOwnProperty$1 = Object.hasOwnProperty;
|
|
var each = function (obj, f) {
|
|
var props = keys(obj);
|
|
for (var k = 0, len = props.length; k < len; k++) {
|
|
var i = props[k];
|
|
var x = obj[i];
|
|
f(x, i);
|
|
}
|
|
};
|
|
var map = function (obj, f) {
|
|
return tupleMap(obj, function (x, i) {
|
|
return {
|
|
k: i,
|
|
v: f(x, i)
|
|
};
|
|
});
|
|
};
|
|
var tupleMap = function (obj, f) {
|
|
var r = {};
|
|
each(obj, function (x, i) {
|
|
var tuple = f(x, i);
|
|
r[tuple.k] = tuple.v;
|
|
});
|
|
return r;
|
|
};
|
|
var has = function (obj, key) {
|
|
return hasOwnProperty$1.call(obj, key);
|
|
};
|
|
|
|
var checkRange = function (str, substr, start) {
|
|
return substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
|
|
};
|
|
var contains = function (str, substr) {
|
|
return str.indexOf(substr) !== -1;
|
|
};
|
|
var startsWith = function (str, prefix) {
|
|
return checkRange(str, prefix, 0);
|
|
};
|
|
|
|
var global$1 = tinymce.util.Tools.resolve('tinymce.Resource');
|
|
|
|
var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay');
|
|
|
|
var global$3 = tinymce.util.Tools.resolve('tinymce.util.Promise');
|
|
|
|
var ALL_CATEGORY = 'All';
|
|
var 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'
|
|
};
|
|
var translateCategory = function (categories, name) {
|
|
return has(categories, name) ? categories[name] : name;
|
|
};
|
|
var getUserDefinedEmoticons = function (editor) {
|
|
var userDefinedEmoticons = getAppendedEmoticons(editor);
|
|
return map(userDefinedEmoticons, function (value) {
|
|
return __assign({
|
|
keywords: [],
|
|
category: 'user'
|
|
}, value);
|
|
});
|
|
};
|
|
var initDatabase = function (editor, databaseUrl, databaseId) {
|
|
var categories = Cell(Optional.none());
|
|
var all = Cell(Optional.none());
|
|
var emojiImagesUrl = getEmotionsImageUrl(editor);
|
|
var getEmoji = function (lib) {
|
|
if (startsWith(lib.char, '<img')) {
|
|
return lib.char.replace(/src="([^"]+)"/, function (match, url) {
|
|
return 'src="' + emojiImagesUrl + url + '"';
|
|
});
|
|
} else {
|
|
return lib.char;
|
|
}
|
|
};
|
|
var processEmojis = function (emojis) {
|
|
var cats = {};
|
|
var everything = [];
|
|
each(emojis, function (lib, title) {
|
|
var entry = {
|
|
title: title,
|
|
keywords: lib.keywords,
|
|
char: getEmoji(lib),
|
|
category: translateCategory(categoryNameMap, lib.category)
|
|
};
|
|
var current = cats[entry.category] !== undefined ? cats[entry.category] : [];
|
|
cats[entry.category] = current.concat([entry]);
|
|
everything.push(entry);
|
|
});
|
|
categories.set(Optional.some(cats));
|
|
all.set(Optional.some(everything));
|
|
};
|
|
editor.on('init', function () {
|
|
global$1.load(databaseId, databaseUrl).then(function (emojis) {
|
|
var userEmojis = getUserDefinedEmoticons(editor);
|
|
processEmojis(merge(emojis, userEmojis));
|
|
}, function (err) {
|
|
console.log('Failed to load emoticons: ' + err);
|
|
categories.set(Optional.some({}));
|
|
all.set(Optional.some([]));
|
|
});
|
|
});
|
|
var listCategory = function (category) {
|
|
if (category === ALL_CATEGORY) {
|
|
return listAll();
|
|
}
|
|
return categories.get().bind(function (cats) {
|
|
return Optional.from(cats[category]);
|
|
}).getOr([]);
|
|
};
|
|
var listAll = function () {
|
|
return all.get().getOr([]);
|
|
};
|
|
var listCategories = function () {
|
|
return [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
|
|
};
|
|
var waitForLoad = function () {
|
|
if (hasLoaded()) {
|
|
return global$3.resolve(true);
|
|
} else {
|
|
return new global$3(function (resolve, reject) {
|
|
var numRetries = 15;
|
|
var interval = global$2.setInterval(function () {
|
|
if (hasLoaded()) {
|
|
global$2.clearInterval(interval);
|
|
resolve(true);
|
|
} else {
|
|
numRetries--;
|
|
if (numRetries < 0) {
|
|
console.log('Could not load emojis from url: ' + databaseUrl);
|
|
global$2.clearInterval(interval);
|
|
reject(false);
|
|
}
|
|
}
|
|
}, 100);
|
|
});
|
|
}
|
|
};
|
|
var hasLoaded = function () {
|
|
return categories.get().isSome() && all.get().isSome();
|
|
};
|
|
return {
|
|
listCategories: listCategories,
|
|
hasLoaded: hasLoaded,
|
|
waitForLoad: waitForLoad,
|
|
listAll: listAll,
|
|
listCategory: listCategory
|
|
};
|
|
};
|
|
|
|
var exists = function (xs, pred) {
|
|
for (var i = 0, len = xs.length; i < len; i++) {
|
|
var x = xs[i];
|
|
if (pred(x, i)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
var map$1 = function (xs, f) {
|
|
var len = xs.length;
|
|
var r = new Array(len);
|
|
for (var i = 0; i < len; i++) {
|
|
var x = xs[i];
|
|
r[i] = f(x, i);
|
|
}
|
|
return r;
|
|
};
|
|
var each$1 = function (xs, f) {
|
|
for (var i = 0, len = xs.length; i < len; i++) {
|
|
var x = xs[i];
|
|
f(x, i);
|
|
}
|
|
};
|
|
|
|
var setup = function (editor) {
|
|
editor.on('PreInit', function () {
|
|
editor.parser.addAttributeFilter('data-emoticon', function (nodes) {
|
|
each$1(nodes, function (node) {
|
|
node.attr('data-mce-resize', 'false');
|
|
node.attr('data-mce-placeholder', '1');
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
var emojiMatches = function (emoji, lowerCasePattern) {
|
|
return contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, function (k) {
|
|
return contains(k.toLowerCase(), lowerCasePattern);
|
|
});
|
|
};
|
|
var emojisFrom = function (list, pattern, maxResults) {
|
|
var matches = [];
|
|
var lowerCasePattern = pattern.toLowerCase();
|
|
var reachedLimit = maxResults.fold(function () {
|
|
return never;
|
|
}, function (max) {
|
|
return function (size) {
|
|
return size >= max;
|
|
};
|
|
});
|
|
for (var 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;
|
|
};
|
|
|
|
var init = function (editor, database) {
|
|
editor.ui.registry.addAutocompleter('emoticons', {
|
|
ch: ':',
|
|
columns: 'auto',
|
|
minChars: 2,
|
|
fetch: function (pattern, maxResults) {
|
|
return database.waitForLoad().then(function () {
|
|
var candidates = database.listAll();
|
|
return emojisFrom(candidates, pattern, Optional.some(maxResults));
|
|
});
|
|
},
|
|
onAction: function (autocompleteApi, rng, value) {
|
|
editor.selection.setRng(rng);
|
|
editor.insertContent(value);
|
|
autocompleteApi.hide();
|
|
}
|
|
});
|
|
};
|
|
|
|
var last = function (fn, rate) {
|
|
var timer = null;
|
|
var cancel = function () {
|
|
if (timer !== null) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
};
|
|
var throttle = function () {
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments[_i];
|
|
}
|
|
if (timer !== null) {
|
|
clearTimeout(timer);
|
|
}
|
|
timer = setTimeout(function () {
|
|
fn.apply(null, args);
|
|
timer = null;
|
|
}, rate);
|
|
};
|
|
return {
|
|
cancel: cancel,
|
|
throttle: throttle
|
|
};
|
|
};
|
|
|
|
var insertEmoticon = function (editor, ch) {
|
|
editor.insertContent(ch);
|
|
};
|
|
|
|
var patternName = 'pattern';
|
|
var open = function (editor, database) {
|
|
var initialState = {
|
|
pattern: '',
|
|
results: emojisFrom(database.listAll(), '', Optional.some(300))
|
|
};
|
|
var currentTab = Cell(ALL_CATEGORY);
|
|
var scan = function (dialogApi) {
|
|
var dialogData = dialogApi.getData();
|
|
var category = currentTab.get();
|
|
var candidates = database.listCategory(category);
|
|
var results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());
|
|
dialogApi.setData({ results: results });
|
|
};
|
|
var updateFilter = last(function (dialogApi) {
|
|
scan(dialogApi);
|
|
}, 200);
|
|
var searchField = {
|
|
label: 'Search',
|
|
type: 'input',
|
|
name: patternName
|
|
};
|
|
var resultsField = {
|
|
type: 'collection',
|
|
name: 'results'
|
|
};
|
|
var getInitialState = function () {
|
|
var body = {
|
|
type: 'tabpanel',
|
|
tabs: map$1(database.listCategories(), function (cat) {
|
|
return {
|
|
title: cat,
|
|
name: cat,
|
|
items: [
|
|
searchField,
|
|
resultsField
|
|
]
|
|
};
|
|
})
|
|
};
|
|
return {
|
|
title: 'Emoticons',
|
|
size: 'normal',
|
|
body: body,
|
|
initialData: initialState,
|
|
onTabChange: function (dialogApi, details) {
|
|
currentTab.set(details.newTabName);
|
|
updateFilter.throttle(dialogApi);
|
|
},
|
|
onChange: updateFilter.throttle,
|
|
onAction: function (dialogApi, actionData) {
|
|
if (actionData.name === 'results') {
|
|
insertEmoticon(editor, actionData.value);
|
|
dialogApi.close();
|
|
}
|
|
},
|
|
buttons: [{
|
|
type: 'cancel',
|
|
text: 'Close',
|
|
primary: true
|
|
}]
|
|
};
|
|
};
|
|
var dialogApi = editor.windowManager.open(getInitialState());
|
|
dialogApi.focus(patternName);
|
|
if (!database.hasLoaded()) {
|
|
dialogApi.block('Loading emoticons...');
|
|
database.waitForLoad().then(function () {
|
|
dialogApi.redial(getInitialState());
|
|
updateFilter.throttle(dialogApi);
|
|
dialogApi.focus(patternName);
|
|
dialogApi.unblock();
|
|
}).catch(function (_err) {
|
|
dialogApi.redial({
|
|
title: 'Emoticons',
|
|
body: {
|
|
type: 'panel',
|
|
items: [{
|
|
type: 'alertbanner',
|
|
level: 'error',
|
|
icon: 'warning',
|
|
text: '<p>Could not load emoticons</p>'
|
|
}]
|
|
},
|
|
buttons: [{
|
|
type: 'cancel',
|
|
text: 'Close',
|
|
primary: true
|
|
}],
|
|
initialData: {
|
|
pattern: '',
|
|
results: []
|
|
}
|
|
});
|
|
dialogApi.focus(patternName);
|
|
dialogApi.unblock();
|
|
});
|
|
}
|
|
};
|
|
|
|
var register = function (editor, database) {
|
|
var onAction = function () {
|
|
return open(editor, database);
|
|
};
|
|
editor.ui.registry.addButton('emoticons', {
|
|
tooltip: 'Emoticons',
|
|
icon: 'emoji',
|
|
onAction: onAction
|
|
});
|
|
editor.ui.registry.addMenuItem('emoticons', {
|
|
text: 'Emoticons...',
|
|
icon: 'emoji',
|
|
onAction: onAction
|
|
});
|
|
};
|
|
|
|
function Plugin () {
|
|
global.add('emoticons', function (editor, pluginUrl) {
|
|
var databaseUrl = getEmoticonDatabaseUrl(editor, pluginUrl);
|
|
var databaseId = getEmoticonDatabaseId(editor);
|
|
var database = initDatabase(editor, databaseUrl, databaseId);
|
|
register(editor, database);
|
|
init(editor, database);
|
|
setup(editor);
|
|
});
|
|
}
|
|
|
|
Plugin();
|
|
|
|
}());
|