beansflight-json-form
Version:
> **版本号:verison 0.23.17**
611 lines (586 loc) • 15.5 kB
JavaScript
/**
* TinyMCE version 6.1.0 (2022-06-29)
*/
;(function () {
'use strict'
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager')
const eq = t => a => t === a
const isNull = eq(null)
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) => {
return str.indexOf(substr) !== -1
}
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', {
ch: ':',
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()
})()