(function () { var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)} // Used when there is no 'main' module. // The name is probably (hopefully) unique so minification removes for releases. var register_3795 = function (id) { var module = dem(id); var fragments = id.split('.'); var target = Function('return this;')(); for (var i = 0; i < fragments.length - 1; ++i) { if (target[fragments[i]] === undefined) target[fragments[i]] = {}; target = target[fragments[i]]; } target[fragments[fragments.length - 1]] = module; }; var instantiate = function (id) { var actual = defs[id]; var dependencies = actual.deps; var definition = actual.defn; var len = dependencies.length; var instances = new Array(len); for (var i = 0; i < len; ++i) instances[i] = dem(dependencies[i]); var defResult = definition.apply(null, instances); if (defResult === undefined) throw 'module [' + id + '] returned undefined'; actual.instance = defResult; }; var def = function (id, dependencies, definition) { if (typeof id !== 'string') throw 'module id must be a string'; else if (dependencies === undefined) throw 'no dependencies for ' + id; else if (definition === undefined) throw 'no definition function for ' + id; defs[id] = { deps: dependencies, defn: definition, instance: undefined }; }; var dem = function (id) { var actual = defs[id]; if (actual === undefined) throw 'module [' + id + '] was undefined'; else if (actual.instance === undefined) instantiate(id); return actual.instance; }; var req = function (ids, callback) { var len = ids.length; var instances = new Array(len); for (var i = 0; i < len; ++i) instances.push(dem(ids[i])); callback.apply(null, callback); }; var ephox = {}; ephox.bolt = { module: { api: { define: def, require: req, demand: dem } } }; var define = def; var require = req; var demand = dem; // this helps with minificiation when using a lot of global references var defineGlobal = function (id, ref) { define(id, [], function () { return ref; }); }; /*jsc ["tinymce.plugins.contextmenu.Plugin","tinymce.core.dom.DOMUtils","tinymce.core.Env","tinymce.core.PluginManager","tinymce.core.ui.Menu","tinymce.core.util.Tools","tinymce.plugins.contextmenu.RangePoint","global!tinymce.util.Tools.resolve","ephox.katamari.api.Arr","ephox.katamari.api.Option","global!Array","global!Error","global!String","ephox.katamari.api.Fun","global!Object"] jsc*/ defineGlobal("global!tinymce.util.Tools.resolve", tinymce.util.Tools.resolve); /** * ResolveGlobal.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.core.dom.DOMUtils', [ 'global!tinymce.util.Tools.resolve' ], function (resolve) { return resolve('tinymce.dom.DOMUtils'); } ); /** * ResolveGlobal.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.core.Env', [ 'global!tinymce.util.Tools.resolve' ], function (resolve) { return resolve('tinymce.Env'); } ); /** * ResolveGlobal.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.core.PluginManager', [ 'global!tinymce.util.Tools.resolve' ], function (resolve) { return resolve('tinymce.PluginManager'); } ); /** * ResolveGlobal.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.core.ui.Menu', [ 'global!tinymce.util.Tools.resolve' ], function (resolve) { return resolve('tinymce.ui.Menu'); } ); /** * ResolveGlobal.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.core.util.Tools', [ 'global!tinymce.util.Tools.resolve' ], function (resolve) { return resolve('tinymce.util.Tools'); } ); defineGlobal("global!Array", Array); defineGlobal("global!Error", Error); define( 'ephox.katamari.api.Fun', [ 'global!Array', 'global!Error' ], function (Array, Error) { var noop = function () { }; var compose = function (fa, fb) { return function () { return fa(fb.apply(null, arguments)); }; }; var constant = function (value) { return function () { return value; }; }; var identity = function (x) { return x; }; var tripleEquals = function(a, b) { return a === b; }; // Don't use array slice(arguments), makes the whole function unoptimisable on Chrome var curry = function (f) { // equivalent to arguments.slice(1) // starting at 1 because 0 is the f, makes things tricky. // Pay attention to what variable is where, and the -1 magic. // thankfully, we have tests for this. var args = new Array(arguments.length - 1); for (var i = 1; i < arguments.length; i++) args[i-1] = arguments[i]; return function () { var newArgs = new Array(arguments.length); for (var j = 0; j < newArgs.length; j++) newArgs[j] = arguments[j]; var all = args.concat(newArgs); return f.apply(null, all); }; }; var not = function (f) { return function () { return !f.apply(null, arguments); }; }; var die = function (msg) { return function () { throw new Error(msg); }; }; var apply = function (f) { return f(); }; var call = function(f) { f(); }; var never = constant(false); var always = constant(true); return { noop: noop, compose: compose, constant: constant, identity: identity, tripleEquals: tripleEquals, curry: curry, not: not, die: die, apply: apply, call: call, never: never, always: always }; } ); defineGlobal("global!Object", Object); define( 'ephox.katamari.api.Option', [ 'ephox.katamari.api.Fun', 'global!Object' ], function (Fun, Object) { var never = Fun.never; var always = Fun.always; /** Option objects support the following methods: fold :: this Option a -> ((() -> b, a -> b)) -> Option b is :: this Option a -> a -> Boolean isSome :: this Option a -> () -> Boolean isNone :: this Option a -> () -> Boolean getOr :: this Option a -> a -> a getOrThunk :: this Option a -> (() -> a) -> a getOrDie :: this Option a -> String -> a or :: this Option a -> Option a -> Option a - if some: return self - if none: return opt orThunk :: this Option a -> (() -> Option a) -> Option a - Same as "or", but uses a thunk instead of a value map :: this Option a -> (a -> b) -> Option b - "fmap" operation on the Option Functor. - same as 'each' ap :: this Option a -> Option (a -> b) -> Option b - "apply" operation on the Option Apply/Applicative. - Equivalent to <*> in Haskell/PureScript. each :: this Option a -> (a -> b) -> Option b - same as 'map' bind :: this Option a -> (a -> Option b) -> Option b - "bind"/"flatMap" operation on the Option Bind/Monad. - Equivalent to >>= in Haskell/PureScript; flatMap in Scala. flatten :: {this Option (Option a))} -> () -> Option a - "flatten"/"join" operation on the Option Monad. exists :: this Option a -> (a -> Boolean) -> Boolean forall :: this Option a -> (a -> Boolean) -> Boolean filter :: this Option a -> (a -> Boolean) -> Option a equals :: this Option a -> Option a -> Boolean equals_ :: this Option a -> (Option a, a -> Boolean) -> Boolean toArray :: this Option a -> () -> [a] */ var none = function () { return NONE; }; var NONE = (function () { var eq = function (o) { return o.isNone(); }; // inlined from peanut, maybe a micro-optimisation? var call = function (thunk) { return thunk(); }; var id = function (n) { return n; }; var noop = function () { }; var me = { fold: function (n, s) { return n(); }, is: never, isSome: never, isNone: always, getOr: id, getOrThunk: call, getOrDie: function (msg) { throw new Error(msg || 'error: getOrDie called on none.'); }, or: id, orThunk: call, map: none, ap: none, each: noop, bind: none, flatten: none, exists: never, forall: always, filter: none, equals: eq, equals_: eq, toArray: function () { return []; }, toString: Fun.constant("none()") }; if (Object.freeze) Object.freeze(me); return me; })(); /** some :: a -> Option a */ var some = function (a) { // inlined from peanut, maybe a micro-optimisation? var constant_a = function () { return a; }; var self = function () { // can't Fun.constant this one return me; }; var map = function (f) { return some(f(a)); }; var bind = function (f) { return f(a); }; var me = { fold: function (n, s) { return s(a); }, is: function (v) { return a === v; }, isSome: always, isNone: never, getOr: constant_a, getOrThunk: constant_a, getOrDie: constant_a, or: self, orThunk: self, map: map, ap: function (optfab) { return optfab.fold(none, function(fab) { return some(fab(a)); }); }, each: function (f) { f(a); }, bind: bind, flatten: constant_a, exists: bind, forall: bind, filter: function (f) { return f(a) ? me : NONE; }, equals: function (o) { return o.is(a); }, equals_: function (o, elementEq) { return o.fold( never, function (b) { return elementEq(a, b); } ); }, toArray: function () { return [a]; }, toString: function () { return 'some(' + a + ')'; } }; return me; }; /** from :: undefined|null|a -> Option a */ var from = function (value) { return value === null || value === undefined ? NONE : some(value); }; return { some: some, none: none, from: from }; } ); defineGlobal("global!String", String); define( 'ephox.katamari.api.Arr', [ 'ephox.katamari.api.Option', 'global!Array', 'global!Error', 'global!String' ], function (Option, Array, Error, String) { // Use the native Array.indexOf if it is available (IE9+) otherwise fall back to manual iteration // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf var rawIndexOf = (function () { var pIndexOf = Array.prototype.indexOf; var fastIndex = function (xs, x) { return pIndexOf.call(xs, x); }; var slowIndex = function(xs, x) { return slowIndexOf(xs, x); }; return pIndexOf === undefined ? slowIndex : fastIndex; })(); var indexOf = function (xs, x) { // The rawIndexOf method does not wrap up in an option. This is for performance reasons. var r = rawIndexOf(xs, x); return r === -1 ? Option.none() : Option.some(r); }; var contains = function (xs, x) { return rawIndexOf(xs, x) > -1; }; // Using findIndex is likely less optimal in Chrome (dynamic return type instead of bool) // but if we need that micro-optimisation we can inline it later. var exists = function (xs, pred) { return findIndex(xs, pred).isSome(); }; var range = function (num, f) { var r = []; for (var i = 0; i < num; i++) { r.push(f(i)); } return r; }; // It's a total micro optimisation, but these do make some difference. // Particularly for browsers other than Chrome. // - length caching // http://jsperf.com/browser-diet-jquery-each-vs-for-loop/69 // - not using push // http://jsperf.com/array-direct-assignment-vs-push/2 var chunk = function (array, size) { var r = []; for (var i = 0; i < array.length; i += size) { var s = array.slice(i, i + size); r.push(s); } return r; }; var map = function(xs, f) { // pre-allocating array size when it's guaranteed to be known // http://jsperf.com/push-allocated-vs-dynamic/22 var len = xs.length; var r = new Array(len); for (var i = 0; i < len; i++) { var x = xs[i]; r[i] = f(x, i, xs); } return r; }; // Unwound implementing other functions in terms of each. // The code size is roughly the same, and it should allow for better optimisation. var each = function(xs, f) { for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; f(x, i, xs); } }; var eachr = function (xs, f) { for (var i = xs.length - 1; i >= 0; i--) { var x = xs[i]; f(x, i, xs); } }; var partition = function(xs, pred) { var pass = []; var fail = []; for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; var arr = pred(x, i, xs) ? pass : fail; arr.push(x); } return { pass: pass, fail: fail }; }; var filter = function(xs, pred) { var r = []; for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; if (pred(x, i, xs)) { r.push(x); } } return r; }; /* * Groups an array into contiguous arrays of like elements. Whether an element is like or not depends on f. * * f is a function that derives a value from an element - e.g. true or false, or a string. * Elements are like if this function generates the same value for them (according to ===). * * * Order of the elements is preserved. Arr.flatten() on the result will return the original list, as with Haskell groupBy function. * For a good explanation, see the group function (which is a special case of groupBy) * http://hackage.haskell.org/package/base-4.7.0.0/docs/Data-List.html#v:group */ var groupBy = function (xs, f) { if (xs.length === 0) { return []; } else { var wasType = f(xs[0]); // initial case for matching var r = []; var group = []; for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; var type = f(x); if (type !== wasType) { r.push(group); group = []; } wasType = type; group.push(x); } if (group.length !== 0) { r.push(group); } return r; } }; var foldr = function (xs, f, acc) { eachr(xs, function (x) { acc = f(acc, x); }); return acc; }; var foldl = function (xs, f, acc) { each(xs, function (x) { acc = f(acc, x); }); return acc; }; var find = function (xs, pred) { for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; if (pred(x, i, xs)) { return Option.some(x); } } return Option.none(); }; var findIndex = function (xs, pred) { for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; if (pred(x, i, xs)) { return Option.some(i); } } return Option.none(); }; var slowIndexOf = function (xs, x) { for (var i = 0, len = xs.length; i < len; ++i) { if (xs[i] === x) { return i; } } return -1; }; var push = Array.prototype.push; var flatten = function (xs) { // Note, this is possible because push supports multiple arguments: // http://jsperf.com/concat-push/6 // Note that in the past, concat() would silently work (very slowly) for array-like objects. // With this change it will throw an error. var r = []; for (var i = 0, len = xs.length; i < len; ++i) { // Ensure that each value is an array itself if (! Array.prototype.isPrototypeOf(xs[i])) throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); push.apply(r, xs[i]); } return r; }; var bind = function (xs, f) { var output = map(xs, f); return flatten(output); }; var forall = function (xs, pred) { for (var i = 0, len = xs.length; i < len; ++i) { var x = xs[i]; if (pred(x, i, xs) !== true) { return false; } } return true; }; var equal = function (a1, a2) { return a1.length === a2.length && forall(a1, function (x, i) { return x === a2[i]; }); }; var slice = Array.prototype.slice; var reverse = function (xs) { var r = slice.call(xs, 0); r.reverse(); return r; }; var difference = function (a1, a2) { return filter(a1, function (x) { return !contains(a2, x); }); }; var mapToObject = function(xs, f) { var r = {}; for (var i = 0, len = xs.length; i < len; i++) { var x = xs[i]; r[String(x)] = f(x, i); } return r; }; var pure = function(x) { return [x]; }; var sort = function (xs, comparator) { var copy = slice.call(xs, 0); copy.sort(comparator); return copy; }; return { map: map, each: each, eachr: eachr, partition: partition, filter: filter, groupBy: groupBy, indexOf: indexOf, foldr: foldr, foldl: foldl, find: find, findIndex: findIndex, flatten: flatten, bind: bind, forall: forall, exists: exists, contains: contains, equal: equal, reverse: reverse, chunk: chunk, difference: difference, mapToObject: mapToObject, pure: pure, sort: sort, range: range }; } ); /** * RangePoint.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ define( 'tinymce.plugins.contextmenu.RangePoint', [ 'ephox.katamari.api.Arr' ], function (Arr) { var containsXY = function (clientRect, clientX, clientY) { return ( clientX >= clientRect.left && clientX <= clientRect.right && clientY >= clientRect.top && clientY <= clientRect.bottom ); }; var isXYWithinRange = function (clientX, clientY, range) { if (range.collapsed) { return false; } return Arr.foldl(range.getClientRects(), function (state, rect) { return state || containsXY(rect, clientX, clientY); }, false); }; return { isXYWithinRange: isXYWithinRange }; } ); /** * Plugin.js * * Released under LGPL License. * Copyright (c) 1999-2017 Ephox Corp. All rights reserved * * License: http://www.tinymce.com/license * Contributing: http://www.tinymce.com/contributing */ /** * This class contains all core logic for the contextmenu plugin. * * @class tinymce.contextmenu.Plugin * @private */ define( 'tinymce.plugins.contextmenu.Plugin', [ 'tinymce.core.dom.DOMUtils', 'tinymce.core.Env', 'tinymce.core.PluginManager', 'tinymce.core.ui.Menu', 'tinymce.core.util.Tools', 'tinymce.plugins.contextmenu.RangePoint' ], function (DOMUtils, Env, PluginManager, Menu, Tools, RangePoint) { var DOM = DOMUtils.DOM; PluginManager.add('contextmenu', function (editor) { var menu, visibleState, contextmenuNeverUseNative = editor.settings.contextmenu_never_use_native; var isNativeOverrideKeyEvent = function (e) { return e.ctrlKey && !contextmenuNeverUseNative; }; var isMacWebKit = function () { return Env.mac && Env.webkit; }; var isContextMenuVisible = function () { return visibleState === true; }; var isImage = function (elm) { return elm && elm.nodeName === 'IMG'; }; var isEventOnImageOutsideRange = function (evt, range) { return isImage(evt.target) && RangePoint.isXYWithinRange(evt.clientX, evt.clientY, range) === false; }; /** * This takes care of a os x native issue where it expands the selection * to the word at the caret position to do "lookups". Since we are overriding * the context menu we also need to override this expanding so the behavior becomes * normalized. Firefox on os x doesn't expand to the word when using the context menu. */ editor.on('mousedown', function (e) { if (isMacWebKit() && e.button === 2 && !isNativeOverrideKeyEvent(e) && editor.selection.isCollapsed()) { editor.once('contextmenu', function (e2) { if (!isImage(e2.target)) { editor.selection.placeCaretAt(e2.clientX, e2.clientY); } }); } }); editor.on('contextmenu', function (e) { var contextmenu; if (isNativeOverrideKeyEvent(e)) { return; } if (isEventOnImageOutsideRange(e, editor.selection.getRng())) { editor.selection.select(e.target); } e.preventDefault(); contextmenu = editor.settings.contextmenu || 'link openlink image inserttable | cell row column deletetable'; // Render menu if (!menu) { var items = []; Tools.each(contextmenu.split(/[ ,]/), function (name) { var item = editor.menuItems[name]; if (name == '|') { item = { text: name }; } if (item) { item.shortcut = ''; // Hide shortcuts items.push(item); } }); for (var i = 0; i < items.length; i++) { if (items[i].text == '|') { if (i === 0 || i == items.length - 1) { items.splice(i, 1); } } } menu = new Menu({ items: items, context: 'contextmenu', classes: 'contextmenu' }).renderTo(); menu.on('hide', function (e) { if (e.control === this) { visibleState = false; } }); editor.on('remove', function () { menu.remove(); menu = null; }); } else { menu.show(); } // Position menu var pos = { x: e.pageX, y: e.pageY }; if (!editor.inline) { pos = DOM.getPos(editor.getContentAreaContainer()); pos.x += e.clientX; pos.y += e.clientY; } menu.moveTo(pos.x, pos.y); visibleState = true; }); return { isContextMenuVisible: isContextMenuVisible }; }); return function () { }; } ); dem('tinymce.plugins.contextmenu.Plugin')(); })();