praiadeseselle/wire/modules/Inputfield/InputfieldTinyMCE/InputfieldTinyMCE.js

949 lines
24 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* InputfieldTinyMCE.js
*
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* TinyMCE 6.x, Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
* https://www.tiny.cloud/docs/tinymce/6/
*
*/
/**
* Handler for image uploads
*
* @param blobInfo
* @param progress
* @returns {Promise<unknown>}
*
*/
var InputfieldTinyMCEUploadHandler = (blobInfo, progress) => new Promise((resolve, reject) => {
var editor = tinymce.activeEditor;
var $inputfield = $('#' + editor.id).closest('.InputfieldTinyMCE');
var imageFieldName = $inputfield.attr('data-upload-field');
var $imageInputfield = $('#wrap_Inputfield_' + imageFieldName);
var pageId = $inputfield.attr('data-upload-page');
var uploadUrl = ProcessWire.config.urls.admin + 'page/edit/?id=' + pageId + '&InputfieldFileAjax=1&ckeupload=1';
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.upload.onprogress = (e) => { progress(e.loaded / e.total * 100); };
xhr.open('POST', uploadUrl);
xhr.onload = () => {
if(xhr.status === 403) {
reject({ message: 'HTTP Error: ' + xhr.status, remove: true });
return;
} else if(xhr.status < 200 || xhr.status >= 300) {
reject('HTTP Error: ' + xhr.status);
return;
}
var response = JSON.parse(xhr.responseText);
if(!response) {
reject('Invalid JSON in response: ' + xhr.responseText);
return;
}
resolve(response.url);
};
xhr.onerror = () => {
reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
};
$imageInputfield.trigger('pwimageupload', {
'name': blobInfo.filename(),
'file': blobInfo.blob(),
'xhr': xhr
});
});
/**
* InputfieldTinyMCE main
*
*/
var InputfieldTinyMCE = {
/**
* Debug mode?
*
*/
debug: false,
/**
* Are document events attached?
*
*/
eventsReady: false,
/**
* Is document ready?
*
*/
isDocumentReady: false,
/**
* Editor selectors to init at document ready
*
*/
editorIds: [],
/**
* Are we currently processing an editor init? (bool or string)
*
*/
initializing: false,
/**
* Allow lazy loaded editor init()? (adjusted by this class at runtime)
*
*/
allowLazy: true,
/**
* Ccallback functions
*
*/
callbacks: { onSetup: [], onConfig: [], onReady: [] },
/**
* Recognized class names
*
*/
cls: {
lazy: 'InputfieldTinyMCELazy',
inline: 'InputfieldTinyMCEInline',
normal: 'InputfieldTinyMCENormal',
loaded: 'InputfieldTinyMCELoaded',
editor: 'InputfieldTinyMCEEditor',
placeholder: 'InputfieldTinyMCEPlaceholder'
},
/**
* Console log
*
* @param a
* @param b
*
*/
log: function(a, b) {
if(!this.debug) return;
if(typeof b !== 'undefined') {
if(typeof a === 'string') a = 'TinyMCE ' + a;
console.log(a, b);
} else {
console.log('TinyMCE', a);
}
},
/**
* Add a setup callback function
*
* ~~~~~
* InputfieldTinyMCE.onSetup(function(editor) {
* // ...
* });
* ~~~~~
*
* @param callback
*
*/
onSetup: function(callback) {
this.callbacks.onSetup.push(callback);
},
/**
* Add a config callback function
*
* ~~~~~
* InputfieldTinyMCE.onConfig(function(settings, $editor, $inputfield) {
* // ...
* });
* ~~~~~
*
* @param callback
*
*/
onConfig: function(callback) {
this.callbacks.onConfig.push(callback);
},
/**
* Add a ready callback function
*
* ~~~~~
* InputfieldTinyMCE.onReady(function(editor) {
* // ...
* });
* ~~~~~
*
* @param callback
*
*/
onReady: function(callback) {
this.callbacks.onReady.push(callback);
},
/**
* Set editor initializing state
*
* @param {boolean|string} initializing Boolean or editor ID
*
*/
setInitializing: function(initializing) {
this.initializing = initializing;
},
/**
* Is editor initializing?
*
* @returns boolean|string False or editor id (string)
*
*/
isInitializing: function() {
return this.initializing;
},
/**
* Modify image dimensions
*
* @param editor
* @param img
* @param width
*
*/
imageResized: function(editor, img, width) {
var t = this;
var src = img.src;
var hidpi = img.className.indexOf('hidpi') > -1 ? 1 : 0;
var basename = src.substring(src.lastIndexOf('/')+1);
var path = src.substring(0, src.lastIndexOf('/')+1);
var dot1 = basename.indexOf('.');
var dot2 = basename.lastIndexOf('.');
var crop = '';
if(basename.indexOf('-cropx') > -1 || basename.indexOf('.cropx') > -1) {
// example: gonzo_the_great.205x183-cropx38y2-is.jpg
// use image file as-is
// @todo re-crop and resize from original?
} else if(dot1 !== dot2) {
// extract any existing resize data present to get original file
// i.e. file.123x456-is-hidpi.jpg => file.jpg
var basename2 = basename.substring(0, dot1) + basename.substring(dot2);
src = path + basename2;
}
var url = ProcessWire.config.urls.admin + 'page/image/resize' +
'?json=1' +
'&width=' + width +
'&hidpi=' + hidpi +
'&file=' + src;
t.log('Resizing image to width=' + width, url);
jQuery.getJSON(url, function(data) {
editor.dom.setAttrib(img, 'src', data.src);
// editor.dom.setAttrib(img, 'width', data.width);
// editor.dom.setAttrib(img, 'height', data.height);
t.log('Resized image to width=' + data.width, data.src);
});
},
/**
* Called when an element has an align class applied to it
*
* This function ensures only 1 align class is applied at a time.
*
* @param editor
*
*/
elementAligned: function(editor) {
var selection = editor.selection;
var node = selection.getNode();
var className = node.className;
var n;
// if only one align class then return now
if(className.indexOf('align') === className.lastIndexOf('align')) return;
var alignNames = [];
var classNames = className.split(' ');
for(n = 0; n < classNames.length; n++) {
if(classNames[n].indexOf('align') === 0) {
alignNames.push(classNames[n]);
}
}
// pop off last align class, which we will keep
alignNames.pop();
for(n = 0; n < alignNames.length; n++) {
className = className.replace(alignNames[n], '');
}
node.className = className.trim();
},
/**
* Lazy load placeholder click event
*
* @param e
* @returns {boolean}
*
*/
clickPlaceholder: function(e) {
var t = InputfieldTinyMCE;
var $placeholder = jQuery(this);
var $textarea = $placeholder.next('textarea');
$placeholder.remove();
t.log('placeholderClick', $placeholder);
if($textarea.length) t.init('#' + $textarea.attr('id'), 'event.' + e.type);
return false;
},
/**
* Init/populate lazy load placeholder elements within given target
*
* @param $placeholders Placeholders are wrapper that has them within
*
*/
initPlaceholders: function($placeholders) {
if(!$placeholders.length) return;
var t = this;
var $item = $placeholders.first();
if(!$item.hasClass(t.cls.placeholder)) $placeholders = $item.find('.' + t.cls.placeholder);
$placeholders.each(function() {
var $placeholder = jQuery(this);
var $textarea = $placeholder.next('textarea');
t.log('initPlaceholder', $placeholder);
$placeholder.children('.mce-content-body').html($textarea.val());
$placeholder.on('click touchstart', t.clickPlaceholder);
});
},
/**
* Init callback function
*
* @param editor
* @param features
*
*/
editorReady: function(editor, features) {
var t = this;
var $editor = jQuery('#' + editor.id);
var $inputfield = $editor.closest('.InputfieldTinyMCE');
var inputTimeout = null;
if(!$inputfield.length) $inputfield = $editor.closest('.Inputfield');
function changed() {
if(inputTimeout) clearTimeout(inputTimeout);
inputTimeout = setTimeout(function() {
$inputfield.trigger('change');
}, 500);
}
editor.on('Dirty', function() { changed() });
editor.on('SetContent', function() { changed() });
editor.on('input', function() { changed() });
// for image resizes
if(features.indexOf('imgResize') > -1) {
editor.on('ObjectResized', function(e, data) {
// @todo account for case where image in figure is resized, and figure needs its width updated with the image
if(e.target.nodeName === 'IMG') {
t.imageResized(editor, e.target, e.width);
changed();
}
});
}
for(var n = 0; n < t.callbacks.onReady.length; n++) {
t.callbacks.onReady[n](editor);
}
editor.on('ExecCommand', function(e, f) {
if(e.command === 'mceFocus') return;
t.log('command: ' + e.command, e);
if(e.command === 'mceToggleFormat') {
if(e.value && e.value.indexOf('align') === 0) {
var editor = this;
t.elementAligned(editor);
}
changed();
}
});
/*
* uncomment to show inline init effect
if(jQuery.ui) {
if($editor.hasClass('InputfieldTinyMCEInline')) {
$editor.effect('highlight', {}, 500);
}
}
*/
/*
editor.on('ResizeEditor', function(e) {
// editor resized
t.log('ResizeEditor');
});
*/
},
/**
* config.setup handler function
*
* @param editor
*
*/
setupEditor: function(editor) {
var t = InputfieldTinyMCE;
var $editor = jQuery('#' + editor.id);
if($editor.hasClass(t.cls.loaded)) {
t.log('mceInit called on input that is already loaded', editor.id);
} else {
$editor.addClass(t.cls.loaded);
}
for(var n = 0; n < t.callbacks.onSetup.length; n++) {
t.callbacks.onSetup[n](editor);
}
/*
editor.on('init', function() {
// var n = performance.now();
// t.log(editor.id + ': ' + (n - mceTimer) + ' ms');
});
*/
/*
editor.on('Load', function() {
// t.log('iframe loaded', editor.id);
});
*/
},
/**
* Destroy given editors
*
* @param $editors
*
*/
destroyEditors: function($editors) {
var t = this;
$editors.each(function() {
var $editor = jQuery(this);
if(!$editor.hasClass(t.cls.loaded)) return;
var editorId = $editor.attr('id');
var editor = tinymce.get(editorId);
$editor.removeClass(t.cls.loaded).removeClass(t.cls.lazy);
t.log('destroyEditor', editor.id);
// $editor.css('display', 'none');
editor.destroy();
});
},
/**
* Destroy editors in given wrapper
*
* @param $wrapper
*
*/
destroyEditorsIn($wrapper) {
this.destroyEditors($wrapper.find('.' + this.cls.loaded));
},
/**
* Reset given editors (destroy and re-init)
*
* @param $editors
*
*/
resetEditors: function($editors) {
var t = this;
t.allowLazy = false;
$editors.each(function() {
var $editor = jQuery(this);
if(!$editor.hasClass(t.cls.loaded)) return;
var editorId = $editor.attr('id');
var editor = tinymce.get(editorId);
editor.destroy();
$editor.removeClass(t.cls.loaded);
// t.init('#' + editorId, 'resetEditors');
});
t.initEditors($editors);
t.allowLazy = true;
},
/**
* Initialize given jQuery object editors
*
* @param $editors
*
*/
initEditors: function($editors) {
var t = this;
$editors.each(function() {
var $editor = jQuery(this);
var editorId = $editor.attr('id');
if($editor.hasClass(t.cls.loaded)) return;
//t.log('init', id);
t.init('#' + editorId, 'initEditors');
});
},
/**
* Find and initialize editors within a wrapper
*
* @param $wrapper
* @param selector Optional
*
*/
initEditorsIn: function($wrapper, selector) {
if(typeof selector === 'undefined') {
selector =
'.' + this.cls.lazy + ':visible, ' +
'.' + this.cls.editor +
':not(.' + this.cls.loaded + ')' +
':not(.' + this.cls.lazy + ')' +
':not(.' + this.cls.inline + ')';
}
var $placeholders = $wrapper.find('.' + this.cls.placeholder);
var $editors = $wrapper.find(selector);
if($placeholders.length) this.initPlaceholders($placeholders);
if($editors.length) this.initEditors($editors);
},
/**
* Get config (config + custom settings)
*
* @param $editor Editor Textarea (Regular) or div (Inline)
* @param $inputfield Editor Wrapping .Inputfield element
* @param features
* @returns {{}}
*
*/
getConfig: function($editor, $inputfield, features) {
var configName = $inputfield.attr('data-configName');
var globalConfig = ProcessWire.config.InputfieldTinyMCE;
var settings = globalConfig.settings.default;
var namedSettings = globalConfig.settings[configName];
var dataSettings = $inputfield.attr('data-settings');
if(typeof settings === 'undefined') {
settings = {};
} else {
settings = jQuery.extend(true, {}, settings);
}
if(typeof namedSettings === 'undefined') {
this.log('Cant find ProcessWire.config.InputfieldTinyMCE.settings.' + configName);
} else {
jQuery.extend(settings, namedSettings);
}
if(typeof dataSettings === 'undefined') {
dataSettings = null;
} else if(dataSettings && dataSettings.length > 2) {
dataSettings = JSON.parse(dataSettings);
jQuery.extend(settings, dataSettings);
}
if(settings.inline) settings.content_css = null; // we load this separately for inline mode
for(var n = 0; n < this.callbacks.onConfig.length; n++) {
this.callbacks.onConfig[n](settings, $editor, $inputfield);
}
if(features.indexOf('pasteFilter') > -1) {
if(globalConfig.pasteFilter === 'text') {
settings.paste_as_text = true;
} else if(globalConfig.pasteFilter.length) {
settings.paste_preprocess = this.pastePreprocess;
}
}
/*
settings.paste_postprocess = function(editor, args) {
console.log(args.node);
args.node.setAttribute('id', '42');
};
*/
return settings;
},
/**
* Pre-process paste
*
* @param editor
* @param args
*
*/
pastePreprocess: function(editor, args) {
var t = InputfieldTinyMCE;
var allow = ',' + ProcessWire.config.InputfieldTinyMCE.pasteFilter + ',';
var regexTag = /<([a-z0-9]+)([^>]*)>/gi;
var regexAttr = /([-_a-z0-9]+)=["']([^"']*)["']/gi;
var html = args.content;
var matchTag, matchAttr;
var removals = [];
var finds = [];
var replaces = [];
var startLength = html.length;
if(args.internal) {
t.log('Skipping pasteFilter for interal copy/paste');
return; // skip filtering for internal copy/paste operations
}
if(allow === ',text,') {
t.log('Skipping pasteFilter since paste_as_text settingw will be used');
return; // will be processed by paste_as_text setting
}
while((matchTag = regexTag.exec(html)) !== null) {
var tagOpen = matchTag[0]; // i.e. <strong>, <img src="..">, <h2>, etc.
var tagName = matchTag[1]; // i.e. 'strong', 'img', 'h2', etc.
var tagClose = '</' + tagName + '>'; // i.e. </strong>, </h2>
var tagAttrs = matchTag[2]; // i.e. 'src="a.jpg" alt="alt"'
var allowAttrs = false;
// first see if we can match a tag replacement
var find = ',' + tagName + '='; // i.e. ',b=strong'
var pos = allow.indexOf(find);
if(pos > -1) {
// tag replacement
var rule = allow.substring(pos + 1); // i.e. b=strong,and,more
rule = rule.substring(0, rule.indexOf(',')); // i.e. b=strong
rule = rule.split('=');
var replaceTag = rule[1];
finds.push(tagOpen);
replaces.push('<' + replaceTag + '>');
finds.push(tagClose);
replaces.push('</' + replaceTag + '>');
}
if(allow.indexOf(',' + tagName + '[') > -1) {
// tag appears in whitelist with attributes
allowAttrs = true;
} else if(allow.indexOf(',' + tagName + ',') === -1) {
// tag does not appear in whitelist
removals.push(tagOpen);
removals.push(tagClose);
continue;
} else {
// tag appears in whitelist (no attributes)
}
if(tagAttrs.length) {
// tag has attributes
if(!allowAttrs) {
// attributes not allowed, replace with non-attribute tag
finds.push(tagOpen);
replaces.push('<' + tagName + '>');
continue;
}
} else {
// no attributes, nothing further to do
continue;
}
var attrRemoves = [];
while((matchAttr = regexAttr.exec(tagAttrs)) !== null) {
var attrStr = matchAttr[0]; // i.e. alt="hello"
var attrName = matchAttr[1]; // i.e. alt
var attrVal = matchAttr[2]; // i.e. hello
if(allow.indexOf(',' + tagName + '[' + attrName + ']') > -1) {
// matches whitelist of tag with allowed attribute
} else if(allow.indexOf(',' + tagName + '[' + attrName + '=' + attrVal + ']') > -1) {
// matches whitelist of tag with allowed attribute having allowed value
} else {
// attributes do not match whitelist
attrRemoves.push(attrStr);
}
}
if(attrRemoves.length) {
var replaceOpenTag = tagOpen;
for(var n = 0; n < attrRemoves.length; n++) {
replaceOpenTag = replaceOpenTag.replace(attrRemoves[n], '');
}
finds.push(tagOpen);
replaces.push(replaceOpenTag);
}
}
// console.log('removals', removals);
for(var n = 0; n < removals.length; n++) {
html = html.replace(removals[n], '');
}
for(var n = 0; n < finds.length; n++) {
html = html.replace(finds[n], replaces[n]);
// console.log(finds[n] + ' => ' + replaces[n]);
}
while(html.indexOf('< ') > -1) html = html.replace('< ', '<');
while(html.indexOf(' >') > -1) html = html.replace(' >', '>');
while(html.indexOf('&nbsp;') > -1) html = html.replace('&nbsp;', ' ', html);
html = html.replaceAll(/<([-a-z0-9]+)[^>]*>\s*<\/\1>/ig, ''); // remove empty tags
t.log('Completed pasteFilter ' + startLength + ' => ' + html.length + ' bytes');
args.content = html;
},
/**
* Document ready events
*
*/
initDocumentEvents: function() {
var t = this;
jQuery(document)
.on('click mouseover focus touchstart', '.' + t.cls.inline + ':not(.' + t.cls.loaded + ')', function(e) {
// we initialize the inline editor only when moused over
// so that a page can handle lots of editors at once without
// them all being active
if(InputfieldTinyMCE.isInitializing() !== false) return;
t.init('#' + this.id, 'event.' + e.type);
})
.on('image-edit sort-stop', '.InputfieldTinyMCE', function(e) {
// all "normal" editors that are also "loaded"
var $editors = jQuery(this).find('.' + t.cls.normal + '.' + t.cls.loaded);
if($editors.length) {
t.log(e.type + '.resetEditors', $editors);
// force all loaded to reset
t.resetEditors($editors);
}
// all "normal" non-placeholder editors that are not yet "loaded"
var $editors = jQuery(this).find('.' + t.cls.normal + ':not(.' + t.cls.loaded + '):not(.' + t.cls.placeholder + ')');
if($editors.length) {
t.log(e.type + '.initEditors', $editors);
t.initEditors($editors);
}
})
.on('reload', '.Inputfield', function() {
var $inputfield = jQuery(this);
var $editors = $inputfield.find('.' + t.cls.loaded);
if($editors.length) {
t.log('reload', $inputfield.attr('id'));
t.destroyEditors($editors);
}
})
.on('reloaded', '.Inputfield', function(e) {
t.initEditorsIn(jQuery(this));
/*
var $inputfield = $(this);
var s = '.' + t.cls.editor + ':not(.' + t.cls.loaded + '):not(.' + t.cls.lazy + ')';
var $editors = $inputfield.find(s);
if($editors.length) {
t.log(e.type, $inputfield.attr('id'));
t.initEditors($editors);
}
var $placeholders = $inputfield.find('.' + t.cls.placeholder);
if($placeholders.length) t.initPlaceholders($placeholders);
return false;
*/
})
.on('sortstop', function(e) {
var $editors = jQuery(e.target).find('.' + t.cls.loaded);
if($editors.length) {
t.log('sortstop');
t.resetEditors($editors);
}
})
.on('opened', '.Inputfield', function(e) {
t.initEditorsIn(jQuery(this));
})
.on('clicklangtab wiretabclick', function(e, $newTab) {
t.initEditorsIn($newTab);
/*
var $placeholders = $newTab.find('.' + t.cls.placeholder);
var $editors = $newTab.find('.' + t.cls.lazy + ':visible');
t.log(e.type, $newTab.attr('id'));
if($placeholders.length) t.initPlaceholders($placeholders);
if($editors.length) t.initEditors($editors);
*/
});
/*
.on('sortstart', function() {
var $editors = $(e.target).find('.InputfieldTinyMCELoaded');
$editors.each(function() {
var $editor = $(this);
t.log('sortstart', $editor.attr('id'));
});
})
*/
this.eventsReady = true;
},
/**
* Document ready
*
*/
documentReady: function() {
this.debug = ProcessWire.config.InputfieldTinyMCE.debug;
this.isDocumentReady = true;
this.log('documentReady', this.editorIds);
while(this.editorIds.length > 0) {
var editorId = this.editorIds.shift();
this.init(editorId, 'documentReady');
}
this.initDocumentEvents();
var $placeholders = jQuery('.' + this.cls.placeholder + ':visible');
if($placeholders.length) this.initPlaceholders($placeholders);
if(this.debug) {
this.log('qty',
'normal=' + jQuery('.' + this.cls.normal).length + ', ' +
'inline=' + jQuery('.' + this.cls.inline).length + ', ' +
'lazy=' + jQuery('.' + this.cls.lazy).length + ', ' +
'loaded=' + jQuery('.' + this.cls.loaded).length + ', ' +
'placeholders=' + $placeholders.length
);
}
},
/**
* Initialize an editor
*
* ~~~~~
* InputfieldTinyMCE.init('#my-textarea');
* ~~~~~
*
* @param id Editor id or selector string
* @param caller Optional name of caller (for debugging purposes)
* @returns {boolean}
*
*/
init: function(id, caller) {
var $editor, config, features, $inputfield, isFront = false,
selector, isLazy, useLazy, _id = id, t = this;
if(!this.isDocumentReady) {
this.editorIds.push(id);
return true;
}
this.setInitializing(id);
caller = (t.debug && typeof caller !== 'undefined' ? ' (caller=' + caller + ')' : '');
if(typeof id === 'string') {
// literal id or selector string
if(id.indexOf('#') === 0 || id.indexOf('.') === 0) {
selector = id;
id = '';
} else {
selector = '#' + id;
}
$editor = jQuery(selector);
if(id === '') id = $editor.attr('id');
} else if(typeof id === 'object') {
// element or jQuery element
if(id instanceof jQuery) {
$editor = id;
} else {
$editor = jQuery(id);
}
id = $editor.attr('id');
selector = '#' + id;
}
if(!$editor.length) {
console.error('Cannot find element to init TinyMCE: ' + _id);
this.setInitializing(false);
return false;
}
if(id.indexOf('Inputfield_') === 0) {
$inputfield = jQuery('#wrap_' + id);
} else if($editor.hasClass('pw-edit-copy')) {
$inputfield = $editor.closest('.pw-edit-InputfieldTinyMCE');
isFront = true; // PageFrontEdit
} else {
$inputfield = jQuery('#wrap_Inputfield_' + id);
}
if(!$inputfield.length) {
$inputfield = $editor.closest('.InputfieldTinyMCE');
if(!$inputfield.length) $inputfield = $editor.closest('.Inputfield');
}
features = $inputfield.attr('data-features');
if(typeof features === 'undefined') features = '';
useLazy = t.allowLazy && features.indexOf('lazyMode') > -1;
isLazy = $editor.hasClass(t.cls.lazy);
if(useLazy && !isLazy && !$editor.is(':visible') && !$editor.hasClass(t.cls.inline)) {
$editor.addClass(t.cls.lazy);
this.log('init-lazy', id + caller);
this.setInitializing(false);
return true;
} else if(isLazy) {
$editor.removeClass(t.cls.lazy);
}
this.log('init', id + caller);
config = this.getConfig($editor, $inputfield, features);
config.selector = selector;
config.setup = this.setupEditor;
config.init_instance_callback = function(editor) {
t.setInitializing('');
setTimeout(function() { if(t.isInitializing() === '') t.setInitializing(false); }, 100);
t.log('ready', editor.id);
t.editorReady(editor, features);
}
if(isFront) {
// disable drag/drop image uploads when PageFrontEdit
config.images_upload_url = '';
config.automatic_uploads = false;
config.paste_data_images = false;
} else if(features.indexOf('imgUpload') > -1) {
config.images_upload_handler = InputfieldTinyMCEUploadHandler;
}
tinymce.init(config);
return true;
}
};
jQuery(document).ready(function() {
InputfieldTinyMCE.documentReady();
});
/*
InputfieldTinyMCE.onSetup(function(editor) {
editor.ui.registry.addButton('hello', {
icon: 'user',
text: 'Hello',
onAction: function() {
editor.insertContent('Hello World!')
}
});
});
*/