From ad3025f5bc9e5a5305e6652ebb586743c908af47 Mon Sep 17 00:00:00 2001 From: Patrick Craston Date: Wed, 27 May 2015 17:28:42 +0100 Subject: [PATCH] Add tinymce4 demo and plugin source --- .gitignore | 1 + demo_tinymce4/demo.css | 148 +++++++ demo_tinymce4/index.html | 80 ++++ tinymce4_plugin/css/ice.css | 27 ++ tinymce4_plugin/img/accept.gif | Bin 0 -> 197 bytes tinymce4_plugin/img/ice-accept-change.png | Bin 0 -> 3228 bytes tinymce4_plugin/img/ice-accept.png | Bin 0 -> 3450 bytes tinymce4_plugin/img/ice-reject-change.png | Bin 0 -> 3125 bytes tinymce4_plugin/img/ice-reject.png | Bin 0 -> 3405 bytes tinymce4_plugin/img/ice-showchanges.png | Bin 0 -> 944 bytes tinymce4_plugin/img/ice-togglechanges.png | Bin 0 -> 3259 bytes tinymce4_plugin/img/reject.gif | Bin 0 -> 165 bytes tinymce4_plugin/plugin.js | 501 ++++++++++++++++++++++ 13 files changed, 757 insertions(+) create mode 100644 demo_tinymce4/demo.css create mode 100644 demo_tinymce4/index.html create mode 100644 tinymce4_plugin/css/ice.css create mode 100644 tinymce4_plugin/img/accept.gif create mode 100644 tinymce4_plugin/img/ice-accept-change.png create mode 100644 tinymce4_plugin/img/ice-accept.png create mode 100644 tinymce4_plugin/img/ice-reject-change.png create mode 100644 tinymce4_plugin/img/ice-reject.png create mode 100644 tinymce4_plugin/img/ice-showchanges.png create mode 100644 tinymce4_plugin/img/ice-togglechanges.png create mode 100644 tinymce4_plugin/img/reject.gif create mode 100644 tinymce4_plugin/plugin.js diff --git a/.gitignore b/.gitignore index 040254cb..e8221471 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build .DS_Store .project node_modules +.idea diff --git a/demo_tinymce4/demo.css b/demo_tinymce4/demo.css new file mode 100644 index 00000000..1ecc0d51 --- /dev/null +++ b/demo_tinymce4/demo.css @@ -0,0 +1,148 @@ +body { + font-size: 16px; + line-height: 24px; + background: #EFEFEF; + height: 100%; + color: #6F6F6F; + font-family: Helvetica Neue,Helvetica,Arial; +} +div.container { + width: 720px; + margin: 0 auto; +} +.editor { + box-shadow: rgba(0, 0, 0, 0.4) 0 1px 3px 0; + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 3px 0; + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 3px 0; + margin-bottom: 30px; + padding: 10px; + width: 700px; + height: 455px; +} +h1, h3 { + color: #7F7F7F; + text-shadow: 0 1px 0 white; +} +.control { font: 12px Arial; padding: 5px; display: inline-block; margin-bottom: 5px; } +.control button { margin-right: 5px; } +#content { color: #333333; font: 14px; } +#content, #tinymce-wrapper { height: 300px; } +#text-wrapper { + width: 620px; + background: #CFCFCF; + min-height: 300px; + max-height: 300px; + overflow: auto; + padding: 40px; + border: 1px solid #BFBFBF; + font-size: 15px; + line-height: 24px; +} +#textbody { + margin: 0 auto; + border: solid 2px #BFBFBF; + background: white; + padding: 15px 30px 30px; + outline: none; +} +a.mceButton16 img.mceIcon { width: 16px; height: 16px; padding: 2px;} + +.CT-hide .del, .CT-hide .del { + display: none; +} + +.CT-hide .ins, .CT-hide .ins { + color: #333333; + background: none !important; + border: none !important; + text-decoration: none; +} + +.del { + text-decoration: line-through; +} +.ins { + text-decoration: underline; +} + +.cts-1 { + color: green; +} +.del.cts-1 img { + border-color: green; +} +.ins.cts-1 img { + background-color: green; +} +.cts-2 { + color: #C02000; +} +.del.cts-2 img { + border-color: #C02000; +} +.ins.cts-2 img { + background-color: #C02000; +} +.cts-3 { + color: #004090; +} +.del.cts-3 img { + border-color: #004090; +} +.ins.cts-3 img { + background-color: #004090; +} +.cts-4 { + color: #F06000; +} +.del.cts-4 img { + border-color: #F06000; +} +.ins.cts-4 img { + background-color: #F06000; +} +.cts-5 { + color: purple; +} +.del.cts-5 img { + border-color: purple; +} +.ins.cts-5 img { + background-color: purple; +} +.cts-6 { + color: #801080; +} +.del.cts-6 img { + border-color: #801080; +} +.ins.cts-6 img { + background-color: #801080; +} +.cts-7 { + color: #1080B0; +} +.del.cts-7 img { + border-color: #1080B0; +} +.ins.cts-7 img { + background-color: #1080B0; +} + +.ice-avoid { + color: red; +} + +.del img { + border-top-style: dotted; + border-bottom-style: dotted; + border-width: 4px; +} + +.ins img { + padding: 3px; +} + +.ins .del img { + padding: 0px 3px; +} diff --git a/demo_tinymce4/index.html b/demo_tinymce4/index.html new file mode 100644 index 00000000..e11f2658 --- /dev/null +++ b/demo_tinymce4/index.html @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Ice Demo

+ +

Tinymce4 Plugin

+ +
+
+ Set User: + +
+
+
+ +
+
+
+ + + +
+ + diff --git a/tinymce4_plugin/css/ice.css b/tinymce4_plugin/css/ice.css new file mode 100644 index 00000000..9cf774e5 --- /dev/null +++ b/tinymce4_plugin/css/ice.css @@ -0,0 +1,27 @@ +.CT-hide .del, .CT-hide .del { + display: none; +} + +.CT-hide .ins, .CT-hide .ins { + color: #333333; + background: none !important; + border: none !important; +} + +.ins, +.del { + -webkit-border-radius: 3px; + border-radius: 3px; + color: #ED1B2F; + padding: 1px 0 2px; +} + +.ins { + background-color: #d1d3d4; +} + +.del { + text-decoration: line-through; + color: #555; + background-color: #e8e8e8; +} diff --git a/tinymce4_plugin/img/accept.gif b/tinymce4_plugin/img/accept.gif new file mode 100644 index 0000000000000000000000000000000000000000..d3960ddcf7005b7574488598305c9d0397230d4d GIT binary patch literal 197 zcmZ?wbhEHb6k!lyIKsd%WnK3A<8_-(v~D@owSDXA?WZU2JTraQ>DhbFF24L~^0n8q zZoHX)`|Z+4AGg2$c;n;Or$2suW*`M9{$$}~U=U=`0cir+$-wHApwgF;IWJ??x}4XG z4p=jJ{J9jc_Lg9Q4LgIPYxhr?gNkP^2rw{lFBfuU;1S_$of0Z)ux)LYqsD5#`1F}o KXT#bV7_0#j>P1=r literal 0 HcmV?d00001 diff --git a/tinymce4_plugin/img/ice-accept-change.png b/tinymce4_plugin/img/ice-accept-change.png new file mode 100644 index 0000000000000000000000000000000000000000..535687c6c4ec9896e0dece9ece3935fcf60b73cf GIT binary patch literal 3228 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 P00000NkvXXu0mjfod(wx literal 0 HcmV?d00001 diff --git a/tinymce4_plugin/img/ice-reject.png b/tinymce4_plugin/img/ice-reject.png new file mode 100644 index 0000000000000000000000000000000000000000..fe1f54daf022699685cfb4aab651b15e22da39fc GIT binary patch literal 3405 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 literal 0 HcmV?d00001 diff --git a/tinymce4_plugin/img/reject.gif b/tinymce4_plugin/img/reject.gif new file mode 100644 index 0000000000000000000000000000000000000000..dab6c050532f82d907a3cc1a8d103db47b40bbe5 GIT binary patch literal 165 zcmV;W09yY?Nk%w1VH5xq0J8u9x3#OmMn>CcXxn;v+k%4Iii+EikpE_8|8{o&fPnv_ zqyMk3|G2pS%*_Al>Hq)$A^8Le000jFEC2ui02BZe000D1@X1N5y*TU5yMIAIg)5QhVTr3nZU2t-1_yaWacs#7tjR<2!uZP2ZS Td=Fc(hL(sw@VI>bhyVaP`y5B) literal 0 HcmV?d00001 diff --git a/tinymce4_plugin/plugin.js b/tinymce4_plugin/plugin.js new file mode 100644 index 00000000..d86f8f95 --- /dev/null +++ b/tinymce4_plugin/plugin.js @@ -0,0 +1,501 @@ +(function () { + tinymce.create('tinymce.plugins.IcePlugin', { + + /** + * Tinymce initializtion API for ice. An `ice` object is expected + * with any of the following params. + */ + // set this to false if you want the plugin to load ice.js, in which case you also need to define path_to_ice_js + ice_loaded_externally: true, + path_to_ice_js: '', // required if loading ice.js via plugin, i.e. ice_loaded_externally is false + deleteTag: 'span', + insertTag: 'span', + deleteClass: 'del', + insertClass: 'ins', + changeIdAttribute: 'data-cid', + userIdAttribute: 'data-userid', + userNameAttribute: 'data-username', + timeAttribute: 'data-time', + preserveOnPaste: 'p', + user: {name: 'Unknown User', id: Math.random()}, + isTracking: false, + contentEditable: true, + css: 'css/ice.css', + mergeBlocks: true, + titleDateFormat: 'm/d/Y h:ia', + afterInit: function () {}, + afterClean: function (body) { + return body; + }, + beforePasteClean: function (body) { + return body; + }, + afterPasteClean: function (body) { + return body; + }, + trackChangesButton: function () {}, + showChangesButton: function () {}, + acceptButton: function () {}, + rejectButton: function () {}, + acceptAllButton: function () {}, + rejectAllButton: function () {}, + + /** + * Plugin initialization - register buttons, commands, and take care of setup. + */ + init: function (ed, url) { + var self = this, changeEditor = null; + + ed.handleEvents = function(e) { + return ed.changeEditor.handleEvent(e); + }; + + ed.on('mouseup mousedown keydown keyup keypress', function (e) { + return ed.handleEvents(e); + }); + + /** + * After the editor renders, initialize ice. + */ + ed.on('postrender', function (e) { + var dom = ed.dom; + + tinymce.extend(self, ed.getParam('ice')); + self.insertSelector = '.' + self.insertClass; + self.deleteSelector = '.' + self.deleteClass; + + // Add insert and delete tag/attribute rules. + // Important: keep `id` in attributes list in case `insertTag` is a `span` - tinymce uses temporary spans with ids. + ed.serializer.addRules(self.insertTag + '[id|class|title|' + self.changeIdAttribute + '|' + self.userIdAttribute + '|' + self.userNameAttribute + '|' + self.timeAttribute + ']'); + ed.serializer.addRules(self.deleteTag + '[id|class|title|' + self.changeIdAttribute + '|' + self.userIdAttribute + '|' + self.userNameAttribute + '|' + self.timeAttribute + ']'); + // Temporary tags to act as placeholders for deletes. + ed.serializer.addRules('tempdel[data-allocation]'); + + if (!self.ice_loaded_externally) { + tinymce.ScriptLoader.load(url + self.path_to_ice_js, ed.execCommand('initializeice')); + } else { + ed.execCommand('initializeice'); + } + + // Setting the trackChanges button to whatever isTracking was set on initialisation + ed.plugins.ice.trackChangesButton.active(self.isTracking); + // always show changes on startup in case there was previous changeds + ed.plugins.ice.showChangesButton.active(true); + }); + + /** + * Instantiates a new ice instance using the given `editor` or the current editor body. + * TODO/FIXME: There is some timing conflict that forces us to initialize ice after a + * timeout (maybe mce isn't completely initialized???). Research further... + */ + ed.addCommand('initializeice', function (editor) { + ed = editor || ed; + tinymce.DOM.win.setTimeout(function () { + ed.changeEditor = new ice.InlineChangeEditor({ + element: ed.getBody(), + isTracking: self.isTracking, + contentEditable: self.contentEditable, + changeIdAttribute: self.changeIdAttribute, + userIdAttribute: self.userIdAttribute, + userNameAttribute: self.userNameAttribute, + timeAttribute: self.timeAttribute, + titleDateFormat: self.titleDateFormat, + mergeBlocks: self.mergeBlocks, + currentUser: { + id: self.user.id, + name: self.user.name + }, + plugins: [ + 'IceEmdashPlugin', + 'IceAddTitlePlugin', + 'IceSmartQuotesPlugin', + { + name: 'IceCopyPastePlugin', + settings: { + pasteType: 'formattedClean', + preserve: self.preserveOnPaste, + beforePasteClean: self.beforePasteClean, + afterPasteClean: self.afterPasteClean + } + } + ], + changeTypes: { + insertType: {tag: self.insertTag, alias: self.insertClass}, + deleteType: {tag: self.deleteTag, alias: self.deleteClass} + } + }).startTracking(); + + setTimeout(function () { + self.afterInit.call(self); + }, 10); + }, 500); + }); + + /** + * Re-initializes ice's environment - resets the environment variables for the current page + * and re-initializes the internal ice range. This is useful after tinymce hides/switches + * the current editor, like when toggling to the html source view and back. + */ + ed.addCommand('ice_initenv', function () { + ed.changeEditor.initializeEnvironment(); + ed.changeEditor.initializeRange(); + }); + + /** + * Cleans change tracking tags out of the given, or editor, body. Removes deletes and their + * inner contents; removes insert tags, keeping their inner content in place. + * @param el optional html string or node body. + * @return clean body, void of change tracking tags. + */ + ed.addCommand('icecleanbody', function (el) { + return ed.changeEditor.getCleanContent(el || ed.getContent(), self.afterClean, self.beforeClean); + }); + + /** + * Returns true if delete placeholders are in place; otherwise, false. + */ + ed.addCommand('ice_hasDeletePlaceholders', function () { + return ed.changeEditor.isPlaceholdingDeletes; + }); + + /** + * This command will drop placeholders in place of delete tags in the editor body and + * store away the references which can be reverted back with the `ice_removeDeletePlaceholders`. + */ + ed.addCommand('ice_addDeletePlaceholders', function () { + return ed.changeEditor.placeholdDeletes(); + }); + + /** + * Replaces delete placeholders with their respective delete nodes. + */ + ed.addCommand('ice_removeDeletePlaceholders', function () { + return ed.changeEditor.revertDeletePlaceholders(); + }); + + /** + * Insert content with change tracking tags. + * + * The `insert` object parameter can contain the following properties: + * { `item`, `range` } + * Where `item` is the item to insert (string, or textnode) + * and `range` is an optional range to insert into. + */ + ed.addCommand('iceinsert', function (insert) { + insert = insert || {}; + ed.changeEditor.insert(insert.item, insert.range); + }); + + /** + * Deletes content with change tracking tags. + * + * The `del` object parameter can contain the following properties: + * { `right`, `range` } + * Where `right` is an optional boolean parameter, where true deletes to the right, false to the left + * and `range` is an optional range to delete in. + * + * If the current Selection isn't collapsed then the `right` param is ignored + * and a selection delete is performed. + */ + ed.addCommand('icedelete', function (del) { + del = del || {}; + ed.changeEditor.deleteContents(del.right, del.range); + }); + + /** + * Set the current ice user with the incoming `user`. + */ + ed.addCommand('ice_changeuser', function (user) { + ed.changeEditor.setCurrentUser(user); + }); + + /** + * Uses the given `node` or finds the current node where the selection resides, and in the + * case of a delete tag, removes the node, or in the case of an insert, removes the outer + * insert tag and keeps the contents in place. + */ + ed.addCommand('iceaccept', function (node) { + ed.undoManager.add(); + ed.changeEditor.acceptChange(node || ed.selection.getNode()); + cleanup(); + }); + + /** + * Uses the given `node` or finds the current node where the selection resides, and in the + * case of a delete tag, removes the outer delete tag and keeps the contents in place, or + * in the case of an insert, removes the node. + */ + ed.addCommand('icereject', function (node) { + ed.undoManager.add(); + ed.changeEditor.rejectChange(node || ed.selection.getNode()); + cleanup(); + }); + + /** + * Cleans the editor body of change tags - removes delete nodes, and removes outer insert + * tags keeping the inner content in place. Defers to cleaning technique. + */ + ed.addCommand('iceacceptall', function () { + ed.undoManager.add(); + ed.changeEditor.acceptAll(); + cleanup(); + }); + + /** + * Cleans the editor body of change tags - removes inserts, and removes outer delete tags, + * keeping the inner content in place. + */ + ed.addCommand('icerejectall', function () { + ed.undoManager.add(); + ed.changeEditor.rejectAll(); + cleanup(); + }); + + /** + * Adds a class to the editor body which will toggle, hide or show, track change styling. + */ + ed.addCommand('ice_toggleshowchanges', function () { + var body = ed.getBody(), disabled = true; + + if (ed.dom.hasClass(body, 'CT-hide')) { + //activate show changes button + ed.plugins.ice.showChangesButton.active(true); + ed.dom.removeClass(body, 'CT-hide'); + disabled = false; + } else { + //deactivate show changes button + ed.plugins.ice.showChangesButton.active(false); + ed.dom.addClass(body, 'CT-hide'); + } + + //toggle button disabling + ed.plugins.ice.acceptAllButton.disabled(disabled); + ed.plugins.ice.rejectAllButton.disabled(disabled); + ed.plugins.ice.acceptButton.disabled(disabled); + ed.plugins.ice.rejectButton.disabled(disabled); + + ed.execCommand('mceRepaint'); + }); + + /** + * Calls the ice smart quotes plugin to convert regular quotes to smart quotes. + */ + ed.addCommand('ice_smartquotes', function (quiet) { + ed.changeEditor.pluginsManager.plugins['IceSmartQuotesPlugin'].convert(ed.getBody()); + if (!quiet) ed.windowManager.alert('Regular quotes have been converted into smart quotes.'); + }); + + /** + * Toggle change tracking on or off. Delegates to ice_enable or ice_disable. + */ + ed.addCommand('ice_togglechanges', function () { + if (ed.changeEditor.isTracking) { + ed.execCommand('ice_disable'); + } else { + ed.execCommand('ice_enable'); + } + }); + + /** + * Turns change tracking on - ice will handle incoming key events. + */ + ed.addCommand('ice_enable', function () { + ed.changeEditor.enableChangeTracking(); + //toggle buttons and call show changes + ed.plugins.ice.trackChangesButton.active(true); + self.isTracking = true; + }); + + /** + * Turns change tracking off - ice will be present but it won't listen + * or act on events. + */ + ed.addCommand('ice_disable', function () { + //hide changes and toggle buttons + ed.changeEditor.disableChangeTracking(); + ed.plugins.ice.trackChangesButton.active(false); + self.isTracking = false; + }); + + /** + * Returns 1 if ice is handling events and tracking changes; otherwise, 0. + */ + ed.addCommand('ice_isTracking', function () { + return ed.changeEditor.isTracking ? 1 : 0; + }); + + /** + * Calls the copy-paste ice plugin to strip tags and attributes out of the given `html`. + */ + ed.addCommand('ice_strippaste', function (html) { + return ed.changeEditor.pluginsManager.plugins['IceCopyPastePlugin'].stripPaste(html); + }); + + /** + * Makes a manual call to the paste handler - this feature is only useful when `isTracking` + * is false; otherwise, ice will automatically handle paste events. + */ + ed.addCommand('ice_handlepaste', function (html) { + return ed.changeEditor.pluginsManager.plugins['IceCopyPastePlugin'].handlePaste(); + }); + + /** + * Makes a manual call to the emdash handler - this feature is only useful when `isTracking` + * is false and the emdash plugin is not on; otherwise, ice will handle emdash conversion. + */ + ed.addCommand('ice_handleemdash', function (html) { + return ed.changeEditor.pluginsManager.plugins['IceEmdashPlugin'].convertEmdash() ? 1 : 0; + }); + + /** + * Register Buttons + */ + ed.addButton('iceaccept', { + title: 'Accept Change', + image: url + '/img/accept.gif', + cmd: 'iceaccept', + onPostRender: function () { //assigns button and changes disabled status on node change + var self = this; + ed.plugins.ice.acceptButton = self; + ed.plugins.ice.acceptButton.disabled = self.disabled; + + ed.on('NodeChange', function (e) { + if (isInsideChangeTag(e.element)) { + self.disabled(false); + } else { + self.disabled(true); + } + }); + } + }); + + ed.addButton('icereject', { + title: 'Reject Change', + image: url + '/img/reject.gif', + cmd: 'icereject', + onPostRender: function () {//assigns button and changes disabled status on node change + var self = this; + ed.plugins.ice.rejectButton = self; + ed.plugins.ice.rejectButton.disabled = self.disabled; + + ed.on('NodeChange', function (e) { + if (isInsideChangeTag(e.element)) { + self.disabled(false); + } else { + self.disabled(true); + } + }); + } + }); + + ed.addButton('iceacceptall', { + title: 'Accept All Changes', + image: url + '/img/ice-accept.png', + cmd: 'iceacceptall', + onPostRender: function () { //assigns button + var self = this; + ed.plugins.ice.acceptAllButton = self; + ed.plugins.ice.acceptAllButton.disabled = self.disabled; + } + }); + + ed.addButton('icerejectall', { + title: 'Reject All Changes', + image: url + '/img/ice-reject.png', + cmd: 'icerejectall', + onPostRender: function () { //assigns button + var self = this; + ed.plugins.ice.rejectAllButton = self; + ed.plugins.ice.rejectAllButton.disabled = self.disabled; + } + }); + + ed.addButton('ice_toggleshowchanges', { + title: 'Show/Hide Track Changes', + image: url + '/img/ice-showchanges.png', + onclick: function () { + ed.fire('ice_toggleshowchanges'); + ed.execCommand('ice_toggleshowchanges'); + }, + onPostRender: function () { //assigns button + var self = this; + ed.plugins.ice.showChangesButton = self; + ed.plugins.ice.showChangesButton.disabled = self.disabled; + ed.plugins.ice.showChangesButton.active = self.active; + } + }); + + ed.addButton('ice_smartquotes', { + title: 'Convert quotes to smart quotes', + 'class': 'mce_blockquote', + cmd: 'ice_smartquotes' + }); + + ed.addButton('ice_togglechanges', { + title: 'Toggle Track Changes ', + image: url + '/img/ice-togglechanges.png', + cmd: 'ice_togglechanges', + onPostRender: function () { //assigns button + var self = this; + ed.plugins.ice.trackChangesButton = self; + ed.plugins.ice.trackChangesButton.disabled = self.disabled; + ed.plugins.ice.trackChangesButton.active = self.active; + } + }); + + if (ed.plugins.contextmenu) { + ed.plugins.contextmenu.onContextMenu.add(function (th, menu, node) { + if (isInsideChangeTag(node)) { + menu.add({ + title: "Accept Change", + icon: 'accept', + cmd: 'iceaccept' + }); + menu.add({ + title: "Reject Change", + icon: 'reject', + cmd: 'icereject' + }); + } + }); + } + + /** + * Node Change event - watch for node changes and toggle buttons. + */ + ed.on('NodeChange', function (e) { + if (isInsideChangeTag(e.element)) { + ed.plugins.ice.acceptButton.disabled(false); + ed.plugins.ice.rejectButton.disabled(false); + } else { + ed.plugins.ice.acceptButton.disabled(true); + ed.plugins.ice.rejectButton.disabled(true); + } + cleanup(); + }); + + /** + * Private Methods + */ + + function isInsideChangeTag(n) { + return !!ed.dom.getParent(n, self.insertSelector + ',' + self.deleteSelector); + } + + function cleanup() { + var empty = ed.dom.select(self.insertSelector + ':empty,' + self.deleteSelector + ':empty'); + ed.dom.remove(empty); + // Browsers insert breaks into empty paragraphs as a space holder - clean that up + // Not playing nice with Webkit... + /*tinymce.each(ed.dom.select('br'), function(br, i) { + var p = ed.dom.getParent(br, 'p'); + if(p && (p.innerText || p.textContent) !== '') + ed.dom.remove(br); + });*/ + } + + } + }); + + tinymce.PluginManager.add('ice', tinymce.plugins.IcePlugin); +})();