UNPKG

beansflight-json-form

Version:

> **版本号:verison 0.23.17**

1,654 lines (1,622 loc) 46.1 kB
/** * TinyMCE version 6.1.0 (2022-06-29) */ ;(function () { 'use strict' var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager') const getPrototypeOf = Object.getPrototypeOf const hasProto = (v, constructor, predicate) => { var _a if (predicate(v, constructor.prototype)) { return true } else { return ( ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name ) } } const typeOf = x => { const t = typeof x if (x === null) { return 'null' } else if (t === 'object' && Array.isArray(x)) { return 'array' } else if ( t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o)) ) { return 'string' } else { return t } } const isType = type => value => typeOf(value) === type const isSimpleType = type => value => typeof value === type const eq = t => a => t === a const is = (value, constructor) => isObject(value) && hasProto(value, constructor, (o, proto) => getPrototypeOf(o) === proto) const isString = isType('string') const isObject = isType('object') const isPlainObject = value => is(value, Object) const isArray = isType('array') const isNull = eq(null) const isBoolean = isSimpleType('boolean') const isNullable = a => a === null || a === undefined const isNonNullable = a => !isNullable(a) const isFunction = isSimpleType('function') const isNumber = isSimpleType('number') const isArrayOf = (value, pred) => { if (isArray(value)) { for (let i = 0, len = value.length; i < len; ++i) { if (!pred(value[i])) { return false } } return true } return false } const noop = () => {} 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 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 objAcc = r => (x, i) => { r[i] = x } const internalFilter = (obj, pred, onTrue, onFalse) => { const r = {} each(obj, (x, i) => { ;(pred(x, i) ? onTrue : onFalse)(x, i) }) return r } const filter = (obj, pred) => { const t = {} internalFilter(obj, pred, objAcc(t), noop) return t } const has = (obj, key) => hasOwnProperty.call(obj, key) const hasNonNullableKey = (obj, key) => has(obj, key) && obj[key] !== undefined && obj[key] !== null const nativePush = Array.prototype.push const flatten = xs => { const r = [] for (let i = 0, len = xs.length; i < len; ++i) { if (!isArray(xs[i])) { throw new Error( 'Arr.flatten item ' + i + ' was not an array, input: ' + xs ) } nativePush.apply(r, xs[i]) } return r } const get = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none() const head = xs => get(xs, 0) const findMap = (arr, f) => { for (let i = 0; i < arr.length; i++) { const r = f(arr[i], i) if (r.isSome()) { return r } } return Optional.none() } typeof window !== 'undefined' ? window : Function('return this;')() const rawSet = (dom, key, value) => { if (isString(value) || isBoolean(value) || isNumber(value)) { dom.setAttribute(key, value + '') } else { console.error( 'Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom ) throw new Error('Attribute value was not simple') } } const set = (element, key, value) => { rawSet(element.dom, key, value) } const remove = (element, key) => { element.dom.removeAttribute(key) } const fromHtml = (html, scope) => { const doc = scope || document const div = doc.createElement('div') div.innerHTML = html if (!div.hasChildNodes() || div.childNodes.length > 1) { const message = 'HTML does not have a single root node' console.error(message, html) throw new Error(message) } return fromDom(div.childNodes[0]) } const fromTag = (tag, scope) => { const doc = scope || document const node = doc.createElement(tag) return fromDom(node) } const fromText = (text, scope) => { const doc = scope || document const node = doc.createTextNode(text) return fromDom(node) } const fromDom = node => { if (node === null || node === undefined) { throw new Error('Node cannot be null or undefined') } return { dom: node } } const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom) const SugarElement = { fromHtml, fromTag, fromText, fromDom, fromPoint } var global$3 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils') var global$2 = tinymce.util.Tools.resolve('tinymce.util.URI') const isNotEmpty = s => s.length > 0 const option = name => editor => editor.options.get(name) const register$2 = editor => { const registerOption = editor.options.register registerOption('image_dimensions', { processor: 'boolean', default: true }) registerOption('image_advtab', { processor: 'boolean', default: false }) registerOption('image_uploadtab', { processor: 'boolean', default: true }) registerOption('image_prepend_url', { processor: 'string', default: '' }) registerOption('image_class_list', { processor: 'object[]' }) registerOption('image_description', { processor: 'boolean', default: true }) registerOption('image_title', { processor: 'boolean', default: false }) registerOption('image_caption', { processor: 'boolean', default: false }) registerOption('image_list', { processor: value => { const valid = value === false || isString(value) || isArrayOf(value, isObject) || isFunction(value) return valid ? { value, valid } : { valid: false, message: 'Must be false, a string, an array or a function.' } }, default: false }) } const hasDimensions = option('image_dimensions') const hasAdvTab = option('image_advtab') const hasUploadTab = option('image_uploadtab') const getPrependUrl = option('image_prepend_url') const getClassList = option('image_class_list') const hasDescription = option('image_description') const hasImageTitle = option('image_title') const hasImageCaption = option('image_caption') const getImageList = option('image_list') const showAccessibilityOptions = option('a11y_advanced_options') const isAutomaticUploadsEnabled = option('automatic_uploads') const hasUploadUrl = editor => isNotEmpty(editor.options.get('images_upload_url')) const hasUploadHandler = editor => isNonNullable(editor.options.get('images_upload_handler')) const parseIntAndGetMax = (val1, val2) => Math.max(parseInt(val1, 10), parseInt(val2, 10)) const getImageSize = url => new Promise(callback => { const img = document.createElement('img') const done = dimensions => { img.onload = img.onerror = null if (img.parentNode) { img.parentNode.removeChild(img) } callback(dimensions) } img.onload = () => { const width = parseIntAndGetMax(img.width, img.clientWidth) const height = parseIntAndGetMax(img.height, img.clientHeight) const dimensions = { width, height } done(Promise.resolve(dimensions)) } img.onerror = () => { done(Promise.reject(`Failed to get image dimensions for: ${url}`)) } const style = img.style style.visibility = 'hidden' style.position = 'fixed' style.bottom = style.left = '0px' style.width = style.height = 'auto' document.body.appendChild(img) img.src = url }) const removePixelSuffix = value => { if (value) { value = value.replace(/px$/, '') } return value } const addPixelSuffix = value => { if (value.length > 0 && /^[0-9]+$/.test(value)) { value += 'px' } return value } const mergeMargins = css => { if (css.margin) { const splitMargin = String(css.margin).split(' ') switch (splitMargin.length) { case 1: css['margin-top'] = css['margin-top'] || splitMargin[0] css['margin-right'] = css['margin-right'] || splitMargin[0] css['margin-bottom'] = css['margin-bottom'] || splitMargin[0] css['margin-left'] = css['margin-left'] || splitMargin[0] break case 2: css['margin-top'] = css['margin-top'] || splitMargin[0] css['margin-right'] = css['margin-right'] || splitMargin[1] css['margin-bottom'] = css['margin-bottom'] || splitMargin[0] css['margin-left'] = css['margin-left'] || splitMargin[1] break case 3: css['margin-top'] = css['margin-top'] || splitMargin[0] css['margin-right'] = css['margin-right'] || splitMargin[1] css['margin-bottom'] = css['margin-bottom'] || splitMargin[2] css['margin-left'] = css['margin-left'] || splitMargin[1] break case 4: css['margin-top'] = css['margin-top'] || splitMargin[0] css['margin-right'] = css['margin-right'] || splitMargin[1] css['margin-bottom'] = css['margin-bottom'] || splitMargin[2] css['margin-left'] = css['margin-left'] || splitMargin[3] } delete css.margin } return css } const createImageList = (editor, callback) => { const imageList = getImageList(editor) if (isString(imageList)) { fetch(imageList).then(res => { if (res.ok) { res.json().then(callback) } }) } else if (isFunction(imageList)) { imageList(callback) } else { callback(imageList) } } const waitLoadImage = (editor, data, imgElm) => { const selectImage = () => { imgElm.onload = imgElm.onerror = null if (editor.selection) { editor.selection.select(imgElm) editor.nodeChanged() } } imgElm.onload = () => { if (!data.width && !data.height && hasDimensions(editor)) { editor.dom.setAttribs(imgElm, { width: String(imgElm.clientWidth), height: String(imgElm.clientHeight) }) } selectImage() } imgElm.onerror = selectImage } const blobToDataUri = blob => new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = () => { resolve(reader.result) } reader.onerror = () => { reject(reader.error.message) } reader.readAsDataURL(blob) }) const isPlaceholderImage = imgElm => imgElm.nodeName === 'IMG' && (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder')) const isSafeImageUrl = (editor, src) => { const getOption = editor.options.get return global$2.isDomSafe(src, 'img', { allow_html_data_urls: getOption('allow_html_data_urls'), allow_script_urls: getOption('allow_script_urls'), allow_svg_data_urls: getOption('allow_svg_data_urls') }) } const DOM = global$3.DOM const getHspace = image => { if ( image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight ) { return removePixelSuffix(image.style.marginLeft) } else { return '' } } const getVspace = image => { if ( image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom ) { return removePixelSuffix(image.style.marginTop) } else { return '' } } const getBorder = image => { if (image.style.borderWidth) { return removePixelSuffix(image.style.borderWidth) } else { return '' } } const getAttrib = (image, name) => { if (image.hasAttribute(name)) { return image.getAttribute(name) } else { return '' } } const getStyle = (image, name) => (image.style[name] ? image.style[name] : '') const hasCaption = image => image.parentNode !== null && image.parentNode.nodeName === 'FIGURE' const updateAttrib = (image, name, value) => { if (value === '') { image.removeAttribute(name) } else { image.setAttribute(name, value) } } const wrapInFigure = image => { const figureElm = DOM.create('figure', { class: 'image' }) DOM.insertAfter(figureElm, image) figureElm.appendChild(image) figureElm.appendChild( DOM.create('figcaption', { contentEditable: 'true' }, 'Caption') ) figureElm.contentEditable = 'false' } const removeFigure = image => { const figureElm = image.parentNode DOM.insertAfter(image, figureElm) DOM.remove(figureElm) } const toggleCaption = image => { if (hasCaption(image)) { removeFigure(image) } else { wrapInFigure(image) } } const normalizeStyle = (image, normalizeCss) => { const attrValue = image.getAttribute('style') const value = normalizeCss(attrValue !== null ? attrValue : '') if (value.length > 0) { image.setAttribute('style', value) image.setAttribute('data-mce-style', value) } else { image.removeAttribute('style') } } const setSize = (name, normalizeCss) => (image, name, value) => { if (image.style[name]) { image.style[name] = addPixelSuffix(value) normalizeStyle(image, normalizeCss) } else { updateAttrib(image, name, value) } } const getSize = (image, name) => { if (image.style[name]) { return removePixelSuffix(image.style[name]) } else { return getAttrib(image, name) } } const setHspace = (image, value) => { const pxValue = addPixelSuffix(value) image.style.marginLeft = pxValue image.style.marginRight = pxValue } const setVspace = (image, value) => { const pxValue = addPixelSuffix(value) image.style.marginTop = pxValue image.style.marginBottom = pxValue } const setBorder = (image, value) => { const pxValue = addPixelSuffix(value) image.style.borderWidth = pxValue } const setBorderStyle = (image, value) => { image.style.borderStyle = value } const getBorderStyle = image => getStyle(image, 'borderStyle') const isFigure = elm => elm.nodeName === 'FIGURE' const isImage = elm => elm.nodeName === 'IMG' const getIsDecorative = image => DOM.getAttrib(image, 'alt').length === 0 && DOM.getAttrib(image, 'role') === 'presentation' const getAlt = image => { if (getIsDecorative(image)) { return '' } else { return getAttrib(image, 'alt') } } const defaultData = () => ({ src: '', alt: '', title: '', width: '', height: '', class: '', style: '', caption: false, hspace: '', vspace: '', border: '', borderStyle: '', isDecorative: false }) const getStyleValue = (normalizeCss, data) => { const image = document.createElement('img') updateAttrib(image, 'style', data.style) if (getHspace(image) || data.hspace !== '') { setHspace(image, data.hspace) } if (getVspace(image) || data.vspace !== '') { setVspace(image, data.vspace) } if (getBorder(image) || data.border !== '') { setBorder(image, data.border) } if (getBorderStyle(image) || data.borderStyle !== '') { setBorderStyle(image, data.borderStyle) } return normalizeCss(image.getAttribute('style')) } const create = (normalizeCss, data) => { const image = document.createElement('img') write( normalizeCss, { ...data, caption: false }, image ) setAlt(image, data.alt, data.isDecorative) if (data.caption) { const figure = DOM.create('figure', { class: 'image' }) figure.appendChild(image) figure.appendChild( DOM.create('figcaption', { contentEditable: 'true' }, 'Caption') ) figure.contentEditable = 'false' return figure } else { return image } } const read = (normalizeCss, image) => ({ src: getAttrib(image, 'src'), alt: getAlt(image), title: getAttrib(image, 'title'), width: getSize(image, 'width'), height: getSize(image, 'height'), class: getAttrib(image, 'class'), style: normalizeCss(getAttrib(image, 'style')), caption: hasCaption(image), hspace: getHspace(image), vspace: getVspace(image), border: getBorder(image), borderStyle: getStyle(image, 'borderStyle'), isDecorative: getIsDecorative(image) }) const updateProp = (image, oldData, newData, name, set) => { if (newData[name] !== oldData[name]) { set(image, name, newData[name]) } } const setAlt = (image, alt, isDecorative) => { if (isDecorative) { DOM.setAttrib(image, 'role', 'presentation') const sugarImage = SugarElement.fromDom(image) set(sugarImage, 'alt', '') } else { if (isNull(alt)) { const sugarImage = SugarElement.fromDom(image) remove(sugarImage, 'alt') } else { const sugarImage = SugarElement.fromDom(image) set(sugarImage, 'alt', alt) } if (DOM.getAttrib(image, 'role') === 'presentation') { DOM.setAttrib(image, 'role', '') } } } const updateAlt = (image, oldData, newData) => { if ( newData.alt !== oldData.alt || newData.isDecorative !== oldData.isDecorative ) { setAlt(image, newData.alt, newData.isDecorative) } } const normalized = (set, normalizeCss) => (image, name, value) => { set(image, value) normalizeStyle(image, normalizeCss) } const write = (normalizeCss, newData, image) => { const oldData = read(normalizeCss, image) updateProp(image, oldData, newData, 'caption', (image, _name, _value) => toggleCaption(image) ) updateProp(image, oldData, newData, 'src', updateAttrib) updateProp(image, oldData, newData, 'title', updateAttrib) updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss)) updateProp( image, oldData, newData, 'height', setSize('height', normalizeCss) ) updateProp(image, oldData, newData, 'class', updateAttrib) updateProp( image, oldData, newData, 'style', normalized( (image, value) => updateAttrib(image, 'style', value), normalizeCss ) ) updateProp( image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss) ) updateProp( image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss) ) updateProp( image, oldData, newData, 'border', normalized(setBorder, normalizeCss) ) updateProp( image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss) ) updateAlt(image, oldData, newData) } const normalizeCss$1 = (editor, cssText) => { const css = editor.dom.styles.parse(cssText) const mergedCss = mergeMargins(css) const compressed = editor.dom.styles.parse( editor.dom.styles.serialize(mergedCss) ) return editor.dom.styles.serialize(compressed) } const getSelectedImage = editor => { const imgElm = editor.selection.getNode() const figureElm = editor.dom.getParent(imgElm, 'figure.image') if (figureElm) { return editor.dom.select('img', figureElm)[0] } if (imgElm && (imgElm.nodeName !== 'IMG' || isPlaceholderImage(imgElm))) { return null } return imgElm } const splitTextBlock = (editor, figure) => { const dom = editor.dom const textBlockElements = filter( editor.schema.getTextBlockElements(), (_, parentElm) => !editor.schema.isValidChild(parentElm, 'figure') ) const textBlock = dom.getParent( figure.parentNode, node => hasNonNullableKey(textBlockElements, node.nodeName), editor.getBody() ) if (textBlock) { return dom.split(textBlock, figure) } else { return figure } } const readImageDataFromSelection = editor => { const image = getSelectedImage(editor) return image ? read(css => normalizeCss$1(editor, css), image) : defaultData() } const insertImageAtCaret = (editor, data) => { const elm = create(css => normalizeCss$1(editor, css), data) editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew') editor.focus() editor.selection.setContent(elm.outerHTML) const insertedElm = editor.dom.select('*[data-mce-id="__mcenew"]')[0] editor.dom.setAttrib(insertedElm, 'data-mce-id', null) if (isFigure(insertedElm)) { const figure = splitTextBlock(editor, insertedElm) editor.selection.select(figure) } else { editor.selection.select(insertedElm) } } const syncSrcAttr = (editor, image) => { editor.dom.setAttrib(image, 'src', image.getAttribute('src')) } const deleteImage = (editor, image) => { if (image) { const elm = editor.dom.is(image.parentNode, 'figure.image') ? image.parentNode : image editor.dom.remove(elm) editor.focus() editor.nodeChanged() if (editor.dom.isEmpty(editor.getBody())) { editor.setContent('') editor.selection.setCursorLocation() } } } const writeImageDataToSelection = (editor, data) => { const image = getSelectedImage(editor) write(css => normalizeCss$1(editor, css), data, image) syncSrcAttr(editor, image) if (isFigure(image.parentNode)) { const figure = image.parentNode splitTextBlock(editor, figure) editor.selection.select(image.parentNode) } else { editor.selection.select(image) waitLoadImage(editor, data, image) } } const sanitizeImageData = (editor, data) => { const src = data.src return { ...data, src: isSafeImageUrl(editor, src) ? src : '' } } const insertOrUpdateImage = (editor, partialData) => { const image = getSelectedImage(editor) if (image) { const selectedImageData = read(css => normalizeCss$1(editor, css), image) const data = { ...selectedImageData, ...partialData } const sanitizedData = sanitizeImageData(editor, data) if (data.src) { writeImageDataToSelection(editor, sanitizedData) } else { deleteImage(editor, image) } } else if (partialData.src) { insertImageAtCaret(editor, { ...defaultData(), ...partialData }) } } const deep = (old, nu) => { const bothObjects = isPlainObject(old) && isPlainObject(nu) return bothObjects ? deepMerge(old, nu) : 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 deepMerge = baseMerge(deep) var global$1 = tinymce.util.Tools.resolve('tinymce.util.ImageUploader') var global = tinymce.util.Tools.resolve('tinymce.util.Tools') const getValue = item => (isString(item.value) ? item.value : '') const getText = item => { if (isString(item.text)) { return item.text } else if (isString(item.title)) { return item.title } else { return '' } } const sanitizeList = (list, extractValue) => { const out = [] global.each(list, item => { const text = getText(item) if (item.menu !== undefined) { const items = sanitizeList(item.menu, extractValue) out.push({ text, items }) } else { const value = extractValue(item) out.push({ text, value }) } }) return out } const sanitizer = (extractor = getValue) => list => { if (list) { return Optional.from(list).map(list => sanitizeList(list, extractor)) } else { return Optional.none() } } const sanitize = list => sanitizer(getValue)(list) const isGroup = item => has(item, 'items') const findEntryDelegate = (list, value) => findMap(list, item => { if (isGroup(item)) { return findEntryDelegate(item.items, value) } else if (item.value === value) { return Optional.some(item) } else { return Optional.none() } }) const findEntry = (optList, value) => optList.bind(list => findEntryDelegate(list, value)) const ListUtils = { sanitizer, sanitize, findEntry } const makeTab$2 = _info => ({ title: 'Advanced', name: 'advanced', items: [ { type: 'grid', columns: 2, items: [ { type: 'input', label: 'Vertical space', name: 'vspace', inputMode: 'numeric' }, { type: 'input', label: 'Horizontal space', name: 'hspace', inputMode: 'numeric' }, { type: 'input', label: 'Border width', name: 'border', inputMode: 'numeric' }, { type: 'listbox', name: 'borderstyle', label: 'Border style', items: [ { text: 'Select...', value: '' }, { text: 'Solid', value: 'solid' }, { text: 'Dotted', value: 'dotted' }, { text: 'Dashed', value: 'dashed' }, { text: 'Double', value: 'double' }, { text: 'Groove', value: 'groove' }, { text: 'Ridge', value: 'ridge' }, { text: 'Inset', value: 'inset' }, { text: 'Outset', value: 'outset' }, { text: 'None', value: 'none' }, { text: 'Hidden', value: 'hidden' } ] } ] } ] }) const AdvTab = { makeTab: makeTab$2 } const collect = editor => { const urlListSanitizer = ListUtils.sanitizer(item => editor.convertURL(item.value || item.url, 'src') ) const futureImageList = new Promise(completer => { createImageList(editor, imageList => { completer( urlListSanitizer(imageList).map(items => flatten([ [ { text: 'None', value: '' } ], items ]) ) ) }) }) const classList = ListUtils.sanitize(getClassList(editor)) const hasAdvTab$1 = hasAdvTab(editor) const hasUploadTab$1 = hasUploadTab(editor) const hasUploadUrl$1 = hasUploadUrl(editor) const hasUploadHandler$1 = hasUploadHandler(editor) const image = readImageDataFromSelection(editor) const hasDescription$1 = hasDescription(editor) const hasImageTitle$1 = hasImageTitle(editor) const hasDimensions$1 = hasDimensions(editor) const hasImageCaption$1 = hasImageCaption(editor) const hasAccessibilityOptions = showAccessibilityOptions(editor) const automaticUploads = isAutomaticUploadsEnabled(editor) const prependURL = Optional.some(getPrependUrl(editor)).filter( preUrl => isString(preUrl) && preUrl.length > 0 ) return futureImageList.then(imageList => ({ image, imageList, classList, hasAdvTab: hasAdvTab$1, hasUploadTab: hasUploadTab$1, hasUploadUrl: hasUploadUrl$1, hasUploadHandler: hasUploadHandler$1, hasDescription: hasDescription$1, hasImageTitle: hasImageTitle$1, hasDimensions: hasDimensions$1, hasImageCaption: hasImageCaption$1, prependURL, hasAccessibilityOptions, automaticUploads })) } const makeItems = info => { const imageUrl = { name: 'src', type: 'urlinput', filetype: 'image', label: 'Source' } const imageList = info.imageList.map(items => ({ name: 'images', type: 'listbox', label: 'Image list', items })) const imageDescription = { name: 'alt', type: 'input', label: 'Alternative description', enabled: !(info.hasAccessibilityOptions && info.image.isDecorative) } const imageTitle = { name: 'title', type: 'input', label: 'Image title' } const imageDimensions = { name: 'dimensions', type: 'sizeinput' } const isDecorative = { type: 'label', label: 'Accessibility', items: [ { name: 'isDecorative', type: 'checkbox', label: 'Image is decorative' } ] } const classList = info.classList.map(items => ({ name: 'classes', type: 'listbox', label: 'Class', items })) const caption = { type: 'label', label: 'Caption', items: [ { type: 'checkbox', name: 'caption', label: 'Show caption' } ] } const getDialogContainerType = useColumns => useColumns ? { type: 'grid', columns: 2 } : { type: 'panel' } return flatten([ [imageUrl], imageList.toArray(), info.hasAccessibilityOptions && info.hasDescription ? [isDecorative] : [], info.hasDescription ? [imageDescription] : [], info.hasImageTitle ? [imageTitle] : [], info.hasDimensions ? [imageDimensions] : [], [ { ...getDialogContainerType( info.classList.isSome() && info.hasImageCaption ), items: flatten([ classList.toArray(), info.hasImageCaption ? [caption] : [] ]) } ] ]) } const makeTab$1 = info => ({ title: 'General', name: 'general', items: makeItems(info) }) const MainTab = { makeTab: makeTab$1, makeItems } const makeTab = _info => { const items = [ { type: 'dropzone', name: 'fileinput' } ] return { title: 'Upload', name: 'upload', items } } const UploadTab = { makeTab } const createState = info => ({ prevImage: ListUtils.findEntry(info.imageList, info.image.src), prevAlt: info.image.alt, open: true }) const fromImageData = image => ({ src: { value: image.src, meta: {} }, images: image.src, alt: image.alt, title: image.title, dimensions: { width: image.width, height: image.height }, classes: image.class, caption: image.caption, style: image.style, vspace: image.vspace, border: image.border, hspace: image.hspace, borderstyle: image.borderStyle, fileinput: [], isDecorative: image.isDecorative }) const toImageData = (data, removeEmptyAlt) => ({ src: data.src.value, alt: data.alt.length === 0 && removeEmptyAlt ? null : data.alt, title: data.title, width: data.dimensions.width, height: data.dimensions.height, class: data.classes, style: data.style, caption: data.caption, hspace: data.hspace, vspace: data.vspace, border: data.border, borderStyle: data.borderstyle, isDecorative: data.isDecorative }) const addPrependUrl2 = (info, srcURL) => { if (!/^(?:[a-zA-Z]+:)?\/\//.test(srcURL)) { return info.prependURL.bind(prependUrl => { if (srcURL.substring(0, prependUrl.length) !== prependUrl) { return Optional.some(prependUrl + srcURL) } return Optional.none() }) } return Optional.none() } const addPrependUrl = (info, api) => { const data = api.getData() addPrependUrl2(info, data.src.value).each(srcURL => { api.setData({ src: { value: srcURL, meta: data.src.meta } }) }) } const formFillFromMeta2 = (info, data, meta) => { if (info.hasDescription && isString(meta.alt)) { data.alt = meta.alt } if (info.hasAccessibilityOptions) { data.isDecorative = meta.isDecorative || data.isDecorative || false } if (info.hasImageTitle && isString(meta.title)) { data.title = meta.title } if (info.hasDimensions) { if (isString(meta.width)) { data.dimensions.width = meta.width } if (isString(meta.height)) { data.dimensions.height = meta.height } } if (isString(meta.class)) { ListUtils.findEntry(info.classList, meta.class).each(entry => { data.classes = entry.value }) } if (info.hasImageCaption) { if (isBoolean(meta.caption)) { data.caption = meta.caption } } if (info.hasAdvTab) { if (isString(meta.style)) { data.style = meta.style } if (isString(meta.vspace)) { data.vspace = meta.vspace } if (isString(meta.border)) { data.border = meta.border } if (isString(meta.hspace)) { data.hspace = meta.hspace } if (isString(meta.borderstyle)) { data.borderstyle = meta.borderstyle } } } const formFillFromMeta = (info, api) => { const data = api.getData() const meta = data.src.meta if (meta !== undefined) { const newData = deepMerge({}, data) formFillFromMeta2(info, newData, meta) api.setData(newData) } } const calculateImageSize = (helpers, info, state, api) => { const data = api.getData() const url = data.src.value const meta = data.src.meta || {} if (!meta.width && !meta.height && info.hasDimensions) { if (isNotEmpty(url)) { helpers .imageSize(url) .then(size => { if (state.open) { api.setData({ dimensions: size }) } }) .catch(e => console.error(e)) } else { api.setData({ dimensions: { width: '', height: '' } }) } } } const updateImagesDropdown = (info, state, api) => { const data = api.getData() const image = ListUtils.findEntry(info.imageList, data.src.value) state.prevImage = image api.setData({ images: image.map(entry => entry.value).getOr('') }) } const changeSrc = (helpers, info, state, api) => { addPrependUrl(info, api) formFillFromMeta(info, api) calculateImageSize(helpers, info, state, api) updateImagesDropdown(info, state, api) } const changeImages = (helpers, info, state, api) => { const data = api.getData() const image = ListUtils.findEntry(info.imageList, data.images) image.each(img => { const updateAlt = data.alt === '' || state.prevImage.map(image => image.text === data.alt).getOr(false) if (updateAlt) { if (img.value === '') { api.setData({ src: img, alt: state.prevAlt }) } else { api.setData({ src: img, alt: img.text }) } } else { api.setData({ src: img }) } }) state.prevImage = image changeSrc(helpers, info, state, api) } const changeFileInput = (helpers, info, state, api) => { const data = api.getData() api.block('Uploading image') head(data.fileinput).fold( () => { api.unblock() }, file => { const blobUri = URL.createObjectURL(file) const finalize = () => { api.unblock() URL.revokeObjectURL(blobUri) } const updateSrcAndSwitchTab = url => { api.setData({ src: { value: url, meta: {} } }) api.showTab('general') changeSrc(helpers, info, state, api) } blobToDataUri(file).then(dataUrl => { const blobInfo = helpers.createBlobCache(file, blobUri, dataUrl) if (info.automaticUploads) { helpers .uploadImage(blobInfo) .then(result => { updateSrcAndSwitchTab(result.url) finalize() }) .catch(err => { finalize() helpers.alertErr(err) }) } else { helpers.addToBlobCache(blobInfo) updateSrcAndSwitchTab(blobInfo.blobUri()) api.unblock() } }) } ) } const changeHandler = (helpers, info, state) => (api, evt) => { if (evt.name === 'src') { changeSrc(helpers, info, state, api) } else if (evt.name === 'images') { changeImages(helpers, info, state, api) } else if (evt.name === 'alt') { state.prevAlt = api.getData().alt } else if (evt.name === 'fileinput') { changeFileInput(helpers, info, state, api) } else if (evt.name === 'isDecorative') { api.setEnabled('alt', !api.getData().isDecorative) } } const closeHandler = state => () => { state.open = false } const makeDialogBody = info => { if (info.hasAdvTab || info.hasUploadUrl || info.hasUploadHandler) { const tabPanel = { type: 'tabpanel', tabs: flatten([ [MainTab.makeTab(info)], info.hasAdvTab ? [AdvTab.makeTab(info)] : [], info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : [] ]) } return tabPanel } else { const panel = { type: 'panel', items: MainTab.makeItems(info) } return panel } } const submitHandler = (editor, info, helpers) => api => { const data = deepMerge(fromImageData(info.image), api.getData()) const finalData = { ...data, style: getStyleValue(helpers.normalizeCss, toImageData(data, false)) } editor.execCommand( 'mceUpdateImage', false, toImageData(finalData, info.hasAccessibilityOptions) ) editor.editorUpload.uploadImagesAuto() api.close() } const imageSize = editor => url => { if (!isSafeImageUrl(editor, url)) { return Promise.resolve({ width: '', height: '' }) } else { return getImageSize(editor.documentBaseURI.toAbsolute(url)).then( dimensions => ({ width: String(dimensions.width), height: String(dimensions.height) }) ) } } const createBlobCache = editor => (file, blobUri, dataUrl) => editor.editorUpload.blobCache.create({ blob: file, blobUri, name: file.name ? file.name.replace(/\.[^\.]+$/, '') : null, filename: file.name, base64: dataUrl.split(',')[1] }) const addToBlobCache = editor => blobInfo => { editor.editorUpload.blobCache.add(blobInfo) } const alertErr = editor => message => { editor.windowManager.alert(message) } const normalizeCss = editor => cssText => normalizeCss$1(editor, cssText) const parseStyle = editor => cssText => editor.dom.parseStyle(cssText) const serializeStyle = editor => (stylesArg, name) => editor.dom.serializeStyle(stylesArg, name) const uploadImage = editor => blobInfo => global$1(editor) .upload([blobInfo], false) .then(results => { if (results.length === 0) { return Promise.reject('Failed to upload image') } else if (results[0].status === false) { return Promise.reject(results[0].error.message) } else { return results[0] } }) const Dialog = editor => { const helpers = { imageSize: imageSize(editor), addToBlobCache: addToBlobCache(editor), createBlobCache: createBlobCache(editor), alertErr: alertErr(editor), normalizeCss: normalizeCss(editor), parseStyle: parseStyle(editor), serializeStyle: serializeStyle(editor), uploadImage: uploadImage(editor) } const open = () => { collect(editor) .then(info => { const state = createState(info) return { title: 'Insert/Edit Image', size: 'normal', body: makeDialogBody(info), buttons: [ { type: 'cancel', name: 'cancel', text: 'Cancel' }, { type: 'submit', name: 'save', text: 'Save', primary: true } ], initialData: fromImageData(info.image), onSubmit: submitHandler(editor, info, helpers), onChange: changeHandler(helpers, info, state), onClose: closeHandler(state) } }) .then(editor.windowManager.open) } return { open } } const register$1 = editor => { editor.addCommand('mceImage', Dialog(editor).open) editor.addCommand('mceUpdateImage', (_ui, data) => { editor.undoManager.transact(() => insertOrUpdateImage(editor, data)) }) } const hasImageClass = node => { const className = node.attr('class') return className && /\bimage\b/.test(className) } const toggleContentEditableState = state => nodes => { let i = nodes.length const toggleContentEditable = node => { node.attr('contenteditable', state ? 'true' : null) } while (i--) { const node = nodes[i] if (hasImageClass(node)) { node.attr('contenteditable', state ? 'false' : null) global.each(node.getAll('figcaption'), toggleContentEditable) } } } const setup = editor => { editor.on('PreInit', () => { editor.parser.addNodeFilter('figure', toggleContentEditableState(true)) editor.serializer.addNodeFilter( 'figure', toggleContentEditableState(false) ) }) } const register = editor => { editor.ui.registry.addToggleButton('image', { icon: 'image', tooltip: 'Insert/edit image', onAction: Dialog(editor).open, onSetup: buttonApi => { buttonApi.setActive(isNonNullable(getSelectedImage(editor))) return editor.selection.selectorChangedWithUnbind( 'img:not([data-mce-object]):not([data-mce-placeholder]),figure.image', buttonApi.setActive ).unbind } }) editor.ui.registry.addMenuItem('image', { icon: 'image', text: 'Image...', onAction: Dialog(editor).open }) editor.ui.registry.addContextMenu('image', { update: element => isFigure(element) || (isImage(element) && !isPlaceholderImage(element)) ? ['image'] : [] }) } var Plugin = () => { global$4.add('image', editor => { register$2(editor) setup(editor) register(editor) register$1(editor) }) } Plugin() })()