mirror of
https://github.com/PanJiaChen/vue-element-admin.git
synced 2025-04-28 04:49:16 +08:00
19070 lines
534 KiB
JavaScript
19070 lines
534 KiB
JavaScript
// import '../stable/plugins.min.js'
|
||
/* Ephox PowerPaste plugin
|
||
*
|
||
* Copyright 2010-2015 Ephox Corporation. All rights reserved.
|
||
*
|
||
* Version: 2.1.0.0
|
||
*/
|
||
(function () {
|
||
|
||
if (this.ephox)
|
||
var old = this.ephox.bolt;
|
||
|
||
var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)}
|
||
|
||
var register = 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 dependencies = defs[id].dependencies;
|
||
var definition = defs[id].definition;
|
||
var instances = [];
|
||
for (var i = 0; i < dependencies.length; ++i)
|
||
instances.push(dem(dependencies[i]));
|
||
defs[id].instance = definition.apply(null, instances);
|
||
if (defs[id].instance === undefined)
|
||
throw 'required module [' + id + '] could not be defined (definition function returned undefined)';
|
||
};
|
||
|
||
var def = function (id, dependencies, definition) {
|
||
if (typeof id !== 'string')
|
||
throw 'invalid module definition, module id must be defined and be a string';
|
||
if (dependencies === undefined)
|
||
throw 'invalid module definition, dependencies must be specified';
|
||
if (definition === undefined)
|
||
throw 'invalid module definition, definition function must be specified';
|
||
defs[id] = {
|
||
dependencies: dependencies,
|
||
definition: definition,
|
||
instance: undefined
|
||
};
|
||
};
|
||
|
||
var dem = function (id) {
|
||
if (defs[id] === undefined)
|
||
throw 'required module [' + id + '] is not defined';
|
||
if (defs[id].instance === undefined)
|
||
instantiate(id);
|
||
return defs[id].instance;
|
||
};
|
||
|
||
var req = function (ids, callback) {
|
||
var instances = [];
|
||
for (var i = 0; i < ids.length; ++i)
|
||
instances.push(dem(ids[i]));
|
||
callback.apply(null, callback);
|
||
};
|
||
|
||
var ephox = this.ephox || {};
|
||
|
||
ephox.bolt = {
|
||
module: {
|
||
api: {
|
||
define: def,
|
||
require: req,
|
||
demand: dem
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
// This is here to give hints to minification
|
||
// ephox.bolt.module.api.define
|
||
var eeephox_def_eeephox = def;
|
||
// ephox.bolt.module.api.require
|
||
var eeephox_req_eeephox = req;
|
||
// ephox.bolt.module.api.demand
|
||
var eeephox_dem_eeephox = dem;
|
||
|
||
/*jsc
|
||
["ephox.powerpaste.util.NodeUtil","ephox.powerpaste.i18n.I18n","ephox.powerpaste.alien.Once","ephox.powerpaste.PowerPastePlugin","ephox.powerpaste.settings.Defaults","ephox.powerpaste.styles.Styles","ephox.powerpaste.legacy.data.tokens.Helper","ephox.powerpaste.legacy.data.tokens.Tokenizer","ephox.powerpaste.legacy.data.tokens.Serializer","ephox.powerpaste.legacy.data.tokens.Filter","ephox.powerpaste.legacy.data.tokens.Attributes","ephox.powerpaste.legacy.data.tokens.Token","ephox.powerpaste.legacy.data.Insert","ephox.powerpaste.legacy.wordimport.WordOnlyFilters","ephox.powerpaste.legacy.wordimport.WordImport","ephox.powerpaste.legacy.wordimport.CommonFilters","ephox.powerpaste.legacy.filters.list.Emitter","ephox.powerpaste.legacy.filters.list.Lists","ephox.powerpaste.legacy.filters.list.ListTypes","ephox.powerpaste.legacy.filters.list.ListStates","ephox.powerpaste.legacy.filters.list.CommentHeuristics","ephox.powerpaste.legacy.filters.StripImages","ephox.powerpaste.legacy.filters.FilterInlineStyles","ephox.powerpaste.legacy.filters.StripBookmarks","ephox.powerpaste.legacy.filters.StripScripts","ephox.powerpaste.legacy.filters.StripLangAttribute","ephox.powerpaste.legacy.filters.Text","ephox.powerpaste.legacy.filters.StripTocLinks","ephox.powerpaste.legacy.filters.StripNoAttributeA","ephox.powerpaste.legacy.filters.InferListTags","ephox.powerpaste.legacy.filters.StripOPTags","ephox.powerpaste.legacy.filters.StripFormattingAttributes","ephox.powerpaste.legacy.filters.StripEmptyStyleAttributes","ephox.powerpaste.legacy.filters.StripEmptyInlineElements","ephox.powerpaste.legacy.filters.StripNamespaceDeclarations","ephox.powerpaste.legacy.filters.StripClassAttributes","ephox.powerpaste.legacy.filters.StripMetaAndLinkElements","ephox.powerpaste.legacy.filters.StripVMLAttributes","ephox.powerpaste.legacy.tinymce.Clipboard","ephox.powerpaste.legacy.tinymce.Settings","ephox.powerpaste.legacy.tinymce.Util","ephox.powerpaste.legacy.tinymce.BrowserFilters","ephox.powerpaste.tinymce.ModernPowerDrop","ephox.powerpaste.tinymce.ModernTinyDialog","ephox.powerpaste.tinymce.ModernPowerPaste","ephox.powerpaste.tinymce.ErrorDialog","ephox.powerpaste.tinymce.LegacyPowerPaste","ephox.powerpaste.tinymce.LegacyTinyDialog","ephox.powerpaste.tinymce.UndoRewriter","ephox.powerpaste.tinymce.TinyPowerPaste","ephox.powerpaste.imageupload.UploaderFactory","ephox.powerpaste.imageupload.TinyUploader","ephox.powerpaste.imageupload.EphoxUploader","ephox.powerpaste.imageupload.UploadError","global!document","global!tinymce","ephox.compass.Arr","ephox.peanut.Fun","ephox.perhaps.Option","ephox.salmon.api.Ephemera","ephox.sugar.api.Element","ephox.sugar.api.Elements","ephox.sugar.api.InsertAll","ephox.sugar.api.SelectorFilter","ephox.salmon.api.BlobCache","ephox.salmon.api.ImageTracker","ephox.salmon.api.UploadUtils","ephox.salmon.api.Uploaders","ephox.sugar.api.Attr","global!setTimeout","ephox.hermes.api.ImageAsset","ephox.hermes.api.ImageExtract","ephox.cement.api.Cement","ephox.sugar.api.Insert","ephox.sugar.api.Remove","ephox.sugar.api.SelectorExists","ephox.sugar.api.SelectorFind","ephox.porkbun.Event","ephox.porkbun.Events","ephox.sugar.api.DomEvent","global!Array","global!String","ephox.salmon.style.Styles","ephox.classify.Type","ephox.compass.Obj","global!Error","global!console","ephox.sugar.api.Traverse","ephox.sugar.api.PredicateFilter","ephox.sugar.api.Selectors","ephox.sugar.impl.ClosestOrAncestor","ephox.numerosity.api.URL","ephox.scullion.Struct","ephox.highway.Merger","ephox.scullion.ADT","ephox.perhaps.Result","ephox.salmon.ui.UploadUi","ephox.salmon.services.UploadCommon","ephox.salmon.services.UploadDirect","ephox.salmon.services.UploadFunction","ephox.hermes.utils.ImageExtract","ephox.cement.flash.Flash","ephox.cement.smartpaste.MergeSettings","ephox.cement.smartpaste.PasteBroker","ephox.limbo.api.RtfImage","ephox.plumber.tap.function.BlockTap","ephox.porkbun.SourceEvent","ephox.sloth.api.Paste","ephox.sugar.impl.FilteredEvent","ephox.flour.style.Resolver","ephox.scullion.Immutable","ephox.scullion.Immutable2","ephox.scullion.MixedBag","ephox.sugar.alien.Recurse","ephox.sugar.api.Compare","ephox.sugar.api.Body","ephox.bud.NodeTypes","ephox.numerosity.core.Global","ephox.sugar.api.Class","ephox.numerosity.api.FormData","ephox.violin.Strings","ephox.jax.plain.Ajax","ephox.numerosity.api.JSON","ephox.yuri.api.Resolver","ephox.epithet.Id","ephox.fred.PlatformDetection","ephox.numerosity.api.FileReader","ephox.bowerbird.api.Rtf","ephox.cement.flash.Correlation","ephox.cement.flash.FlashDialog","ephox.sugar.api.Css","ephox.sugar.api.Node","ephox.sugar.api.PredicateFind","ephox.sugar.api.Replication","ephox.cement.style.Styles","ephox.cement.smartpaste.Inspection","ephox.cement.smartpaste.PasteHandlers","ephox.perhaps.Options","global!RegExp","ephox.plumber.tap.control.BlockControl","ephox.plumber.tap.wrap.Tapped","ephox.scullion.BagUtils","global!Object","ephox.peanut.Thunk","ephox.epithet.Resolve","ephox.sugar.alien.Toggler","ephox
|
||
jsc*/
|
||
ephox.bolt.module.api.define("global!document", [], function () { return document; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.util.NodeUtil',
|
||
[
|
||
'global!document'
|
||
],
|
||
function(document){
|
||
var nodeToString = function ( node ) {
|
||
var tmpNode = document.createElement( "div" );
|
||
tmpNode.appendChild( node.cloneNode( true ) );
|
||
var str = tmpNode.innerHTML;
|
||
tmpNode = node = null; // prevent memory leaks in IE
|
||
return str;
|
||
};
|
||
|
||
return {
|
||
nodeToString: nodeToString
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!tinymce", [], function () { return tinymce; });
|
||
(function (define, require, demand) {
|
||
define('ephox.powerpaste.i18n.I18n',
|
||
|
||
[
|
||
'global!tinymce'
|
||
],
|
||
|
||
function(tinymce) {
|
||
var missingFlash = function() {
|
||
return "Your browser security settings may be preventing images from being imported.";
|
||
};
|
||
|
||
var flashClipboard = function() {
|
||
return tinymce.Env.mac && tinymce.Env.webkit ? missingFlash() + " <a href=\"https://support.ephox.com/entries/59328357-Safari-6-1-and-7-Flash-Sandboxing\" style=\"text-decoration: underline\">More information on paste for Safari</a>" :
|
||
missingFlash();
|
||
};
|
||
|
||
var english = {
|
||
"cement.dialog.paste.title": "Paste Formatting Options",
|
||
"cement.dialog.paste.instructions": "Choose to keep or remove formatting in the pasted content.",
|
||
"cement.dialog.paste.merge": "Keep Formatting",
|
||
"cement.dialog.paste.clean": "Remove Formatting",
|
||
"cement.dialog.flash.title": "Local Image Import",
|
||
"cement.dialog.flash.trigger-paste": "Trigger paste again from the keyboard to paste content with images.",
|
||
"cement.dialog.flash.missing": "Adobe Flash is required to import images from Microsoft Office. Install the <a href=\"http://get.adobe.com/flashplayer/\" target=\"_blank\">Adobe Flash Player</a>.",
|
||
"cement.dialog.flash.press-escape": "Press <span class=\"ephox-polish-help-kbd\">ESC</span> to ignore local images and continue editing.",
|
||
"loading.wait" : "Please wait...",
|
||
"flash.clipboard.no.rtf": flashClipboard(),
|
||
"safari.imagepaste": "Safari does not support direct paste of images. <a href=\"https://support.ephox.com/entries/88543243-Safari-Direct-paste-of-images-does-not-work\" style=\"text-decoration: underline\">More information on image pasting for Safari</a>",
|
||
"error.code.images.not.found": "The images service was not found: (",
|
||
"error.imageupload": "Image failed to upload: (",
|
||
"error.full.stop": ").",
|
||
"errors.local.images.disallowed": "Local image paste has been disabled. Local images have been removed from pasted content."
|
||
};
|
||
|
||
var getEnglishText = function(key) {
|
||
return english[key];
|
||
};
|
||
|
||
var translate = function (key) {
|
||
//This function acts as a shim between tiny's translation engine, which uses raw strings
|
||
//and cement's, which works off string pointers
|
||
return tinymce.translate(getEnglishText(key));
|
||
};
|
||
|
||
return {
|
||
translate: translate
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.alien.Once',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
// Maybe belongs in peanut.Fun?
|
||
return function (f) {
|
||
var called = false;
|
||
return function () {
|
||
if (!called) {
|
||
called = true;
|
||
f.apply(null, arguments);
|
||
}
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!Array", [], function () { return Array; });
|
||
ephox.bolt.module.api.define("global!String", [], function () { return String; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.compass.Arr',
|
||
|
||
[
|
||
'global!Array',
|
||
'global!String'
|
||
],
|
||
|
||
function (Array, String) {
|
||
var eqC = function(x) {
|
||
return function(y) {
|
||
return x === y;
|
||
};
|
||
};
|
||
|
||
var isTrue = eqC(true);
|
||
|
||
var contains = function(xs, x) {
|
||
return exists(xs, eqC(x));
|
||
};
|
||
|
||
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) {
|
||
var r = [];
|
||
for (var i = 0; i < xs.length; i++) {
|
||
var x = xs[i];
|
||
r.push(f(x, i, xs));
|
||
}
|
||
return r;
|
||
};
|
||
|
||
var each = function(xs, f) {
|
||
for (var i = 0; i < xs.length; i++) {
|
||
var x = xs[i];
|
||
f(x, i, xs);
|
||
}
|
||
};
|
||
|
||
var partition = function(xs, pred) {
|
||
var pass = [];
|
||
var fail = [];
|
||
for (var i = 0; i < xs.length; 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; i < xs.length; 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 = [];
|
||
|
||
each(xs, function (x) {
|
||
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 indexOf = function(xs, x) {
|
||
if (arguments.length !== 2)
|
||
throw 'Expected 2 arguments to indexOf';
|
||
return findIndex(xs, eqC(x));
|
||
};
|
||
|
||
var foldr = function (xs, f, acc) {
|
||
return foldl(reverse(xs), f, acc);
|
||
};
|
||
|
||
var foldl = function (xs, f, acc) {
|
||
each(xs, function (x) {
|
||
acc = f(acc, x);
|
||
});
|
||
return acc;
|
||
};
|
||
|
||
var find = function(xs, pred) {
|
||
if (arguments.length !== 2)
|
||
throw 'Expected 2 arguments to find';
|
||
for (var i = 0; i < xs.length; i++) {
|
||
var x = xs[i];
|
||
if (pred(x, i, xs)) {
|
||
return x;
|
||
}
|
||
}
|
||
return undefined;
|
||
};
|
||
|
||
var findOr = function (xs, f, default_) {
|
||
var r = find(xs, f);
|
||
return r !== undefined ? r : default_;
|
||
};
|
||
|
||
var findOrDie = function (xs, f, message) {
|
||
var r = find(xs, f);
|
||
if (r === undefined)
|
||
throw message || 'Could not find element in array: ' + String(xs);
|
||
return r;
|
||
};
|
||
|
||
var findIndex = function (xs, pred) {
|
||
var fn = pred || isTrue;
|
||
|
||
for (var i = 0; i < xs.length; ++i)
|
||
if (fn(xs[i]) === true)
|
||
return i;
|
||
|
||
return -1;
|
||
};
|
||
|
||
var flatten = function (xs) {
|
||
var r = [];
|
||
for (var i = 0; i < xs.length; ++i)
|
||
r = r.concat(xs[i]);
|
||
return r;
|
||
};
|
||
|
||
var bind = function (xs, f) {
|
||
var output = map(xs, f);
|
||
return flatten(output);
|
||
};
|
||
|
||
var forall = function (xs, pred) {
|
||
var fn = pred || isTrue;
|
||
for (var i = 0; i < xs.length; ++i)
|
||
if (fn(xs[i], i) !== true)
|
||
return false;
|
||
return true;
|
||
};
|
||
|
||
var exists = function (xs, pred) {
|
||
var fn = pred || isTrue;
|
||
for (var i = 0; i < xs.length; ++i)
|
||
if (fn(xs[i]) === true)
|
||
return true;
|
||
return false;
|
||
};
|
||
|
||
var equal = function (a1, a2) {
|
||
return a1.length === a2.length && forall(a1, function (x, i) {
|
||
return x === a2[i];
|
||
});
|
||
};
|
||
|
||
var reverse = function (xs) {
|
||
var r = Array.prototype.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 = {};
|
||
each(xs, function(x, i) {
|
||
r[String(x)] = f(x, i);
|
||
});
|
||
return r;
|
||
};
|
||
|
||
return {
|
||
map: map,
|
||
each: each,
|
||
partition: partition,
|
||
filter: filter,
|
||
groupBy: groupBy,
|
||
indexOf: indexOf,
|
||
foldr: foldr,
|
||
foldl: foldl,
|
||
find: find,
|
||
findIndex: findIndex,
|
||
findOr: findOr,
|
||
findOrDie: findOrDie,
|
||
flatten: flatten,
|
||
bind: bind,
|
||
forall: forall,
|
||
exists: exists,
|
||
contains: contains,
|
||
equal: equal,
|
||
reverse: reverse,
|
||
chunk: chunk,
|
||
difference: difference,
|
||
mapToObject: mapToObject
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.peanut.Fun',
|
||
|
||
[
|
||
'global!Array'
|
||
],
|
||
|
||
function (Array) {
|
||
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;
|
||
};
|
||
|
||
var curry = function (f) {
|
||
var slice = Array.prototype.slice;
|
||
var args = slice.call(arguments, 1);
|
||
return function () {
|
||
var all = args.concat(slice.call(arguments, 0));
|
||
return f.apply(null, all);
|
||
};
|
||
};
|
||
|
||
var not = function (f) {
|
||
return function () {
|
||
return !f.apply(null, arguments);
|
||
};
|
||
};
|
||
|
||
var die = function (msg) {
|
||
return function () {
|
||
throw msg;
|
||
};
|
||
};
|
||
|
||
var apply = function (f) {
|
||
return f();
|
||
};
|
||
|
||
return {
|
||
noop: noop,
|
||
compose: compose,
|
||
constant: constant,
|
||
identity: identity,
|
||
tripleEquals: tripleEquals,
|
||
curry: curry,
|
||
not: not,
|
||
die: die,
|
||
apply: apply
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.perhaps.Option',
|
||
|
||
[
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Fun) {
|
||
|
||
/** some :: a -> Option a */
|
||
var some = function (value) {
|
||
return option(function (n, s) {
|
||
return s(value);
|
||
});
|
||
};
|
||
|
||
/** none :: () -> Option a */
|
||
var none = function () {
|
||
return option(function (n, s) {
|
||
return n();
|
||
});
|
||
};
|
||
|
||
/** from :: undefined|null|a -> Option a */
|
||
var from = function (value) {
|
||
return value === null || value === undefined ? none() : some(value);
|
||
};
|
||
|
||
/** option :: (() -> t, a -> t) -> Option a */
|
||
var option = function (fold) {
|
||
|
||
/** is :: this Option a -> a -> Boolean */
|
||
var is = function (v) {
|
||
return fold(Fun.constant(false), function (o) {
|
||
return o === v;
|
||
});
|
||
};
|
||
|
||
/** isSome :: this Option a -> () -> Boolean */
|
||
var isSome = function () {
|
||
return fold(Fun.constant(false), Fun.constant(true));
|
||
};
|
||
|
||
/** isNone :: this Option a -> () -> Boolean */
|
||
var isNone = Fun.not(isSome);
|
||
|
||
/** getOr :: this Option a -> a -> a */
|
||
var getOr = function (value) {
|
||
return fold(Fun.constant(value), Fun.identity);
|
||
};
|
||
|
||
/** getOrThunk :: this Option a -> (() -> a) -> a */
|
||
var getOrThunk = function (f) {
|
||
return fold(f, Fun.identity);
|
||
};
|
||
|
||
/** getOrDie :: this Option a -> String -> a */
|
||
var getOrDie = function (msg) {
|
||
return fold(Fun.die(msg || 'error: getOrDie called on none.'), Fun.identity);
|
||
};
|
||
|
||
/** or :: this Option a -> Option a -> Option a
|
||
* if some: return self
|
||
* if none: return opt
|
||
*/
|
||
var or = function (opt) {
|
||
return fold(Fun.constant(opt), some);
|
||
};
|
||
|
||
/** orThunk :: this Option a -> (() -> Option a) -> Option a
|
||
* Same as "or", but uses a thunk instead of a value
|
||
*/
|
||
var orThunk = function (f) {
|
||
return fold(f, some);
|
||
};
|
||
|
||
/** map :: this Option a -> (a -> b) -> Option b
|
||
* "fmap" operation on the Option Functor.
|
||
*/
|
||
var map = function (f) {
|
||
return bind(function (value) {
|
||
return some(f(value));
|
||
});
|
||
};
|
||
|
||
/** ap :: this Option a -> Option (a -> b) -> Option b)
|
||
* "apply" operation on the Option Apply/Applicative.
|
||
* Equivalent to <*> in Haskell/PureScript.
|
||
*/
|
||
var ap = function(ofab) {
|
||
return fold(none, function(a) {
|
||
return ofab.fold(none, function(fab) {
|
||
return some(fab(a));
|
||
});
|
||
});
|
||
};
|
||
|
||
/** each :: this Option a -> (a -> b) -> Option b */
|
||
var each = 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.
|
||
*/
|
||
var bind = function (f) {
|
||
return fold(none, f);
|
||
};
|
||
|
||
/** flatten :: {this Option (Option a))} -> () -> Option a
|
||
* "flatten"/"join" operation on the Option Monad.
|
||
*/
|
||
var flatten = function () {
|
||
return fold(none, Fun.identity);
|
||
};
|
||
|
||
/** exists :: this Option a -> (a -> Boolean) -> Boolean */
|
||
var exists = function (f) {
|
||
return fold(Fun.constant(false), f);
|
||
};
|
||
|
||
/** forall :: this Option a -> (a -> Boolean) -> Boolean */
|
||
var forall = function (f) {
|
||
return fold(Fun.constant(true), f);
|
||
};
|
||
|
||
/** filter :: this Option a -> (a -> Boolean) -> Option a */
|
||
var filter = function (f) {
|
||
return fold(none, function (v) {
|
||
return f(v) ? some(v) : none();
|
||
});
|
||
};
|
||
|
||
/** equals :: this Option a -> Option a -> Boolean */
|
||
var equals = function (o) {
|
||
return fold(o.isNone, o.is);
|
||
};
|
||
|
||
/** equals_ :: this Option a -> (Option a, a -> Boolean) -> Boolean */
|
||
var equals_ = function (o, elementEq) {
|
||
return fold(o.isNone, function(x) {
|
||
return o.fold(
|
||
Fun.constant(false),
|
||
Fun.curry(elementEq, x)
|
||
);
|
||
});
|
||
};
|
||
|
||
/** toArray :: this Option a -> () -> [a] */
|
||
var toArray = function () {
|
||
return fold(Fun.constant([]), function (val) {
|
||
return [ val ];
|
||
});
|
||
};
|
||
|
||
var toString = function() {
|
||
return fold(Fun.constant("none()"), function(a) { return "some(" + a + ")"; });
|
||
};
|
||
|
||
return {
|
||
is: is,
|
||
isSome: isSome,
|
||
isNone: isNone,
|
||
getOr: getOr,
|
||
getOrThunk: getOrThunk,
|
||
getOrDie: getOrDie,
|
||
or: or,
|
||
orThunk: orThunk,
|
||
fold: fold,
|
||
map: map,
|
||
each: each,
|
||
bind: bind,
|
||
ap: ap,
|
||
flatten: flatten,
|
||
exists: exists,
|
||
forall: forall,
|
||
equals: equals,
|
||
equals_: equals_,
|
||
filter: filter,
|
||
toArray: toArray,
|
||
toString: toString
|
||
};
|
||
};
|
||
|
||
/** equals :: (Option a, Option a) -> Boolean */
|
||
var equals = function(a, b) {
|
||
return a.equals(b);
|
||
};
|
||
|
||
/** equals_ :: (Option a, Option a, (a -> a) -> Boolean */
|
||
var equals_ = function(a, b, elementEq) {
|
||
return a.equals_(b, elementEq);
|
||
};
|
||
|
||
return {
|
||
some: some,
|
||
none: none,
|
||
from: from,
|
||
equals: equals,
|
||
equals_: equals_
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.imageupload.TinyUploader',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Fun) {
|
||
return function (editor) {
|
||
|
||
var uploadImages = function() {
|
||
editor.uploadImages();
|
||
};
|
||
|
||
var prepareImages = function (assets) {
|
||
Arr.each(assets, function (a) {
|
||
a.fold(function (id, blob, objurl, data) {
|
||
Arr.each(editor.dom.select('img[src="' + objurl + '"]'), function (img) {
|
||
editor.dom.setAttrib(img, 'src', data.result);
|
||
});
|
||
}, Fun.noop);
|
||
});
|
||
};
|
||
|
||
var getLocalURL = function (id, blob, objurl, data) {
|
||
return data.result;
|
||
};
|
||
|
||
return {
|
||
uploadImages: uploadImages,
|
||
prepareImages: prepareImages,
|
||
getLocalURL: getLocalURL
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.ErrorDialog',
|
||
[
|
||
|
||
],
|
||
function() {
|
||
return {
|
||
showDialog: function(editor, errorText){
|
||
|
||
var close = function() {
|
||
win.close();
|
||
};
|
||
|
||
var controls = [{
|
||
text: 'Ok',
|
||
onclick: close
|
||
}];
|
||
|
||
var winSettings = {
|
||
title: "Error",
|
||
spacing: 10,
|
||
padding: 10,
|
||
items: [{
|
||
type: 'container',
|
||
html: errorText
|
||
}],
|
||
buttons: controls
|
||
};
|
||
|
||
//We could have done something similar through the use of windowmanager.alert
|
||
//But it appears that alert doesn't allow us to use html.
|
||
//So we create a custom dialog, again, and use .open instead.
|
||
win = editor.windowManager.open(winSettings);
|
||
}
|
||
};
|
||
});
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.imageupload.UploadError',
|
||
|
||
[
|
||
'ephox.powerpaste.alien.Once',
|
||
'ephox.powerpaste.i18n.I18n',
|
||
'ephox.powerpaste.tinymce.ErrorDialog'
|
||
],
|
||
|
||
function (Once, I18n, ErrorDialog) {
|
||
return function (editor, url) {
|
||
var serviceNotFound = function () { return I18n.translate('error.code.images.not.found') + url + I18n.translate('error.full.stop');};
|
||
|
||
var genericError = function () { return I18n.translate('error.imageupload') + url + I18n.translate('error.full.stop');};
|
||
|
||
var showDialog = function (err) {
|
||
var status = err.status();
|
||
|
||
// TODO: status === 0 seems to consistently be a CORS failure. Might be nice to have a better message.
|
||
var notFound = status === 0 || (status >= 400 || status < 500);
|
||
|
||
// TODO: Services give us more details than this. The response should include details (TBIO-1256).
|
||
// 500+ falls through to generic error for now.
|
||
var error = notFound ? serviceNotFound : genericError;
|
||
ErrorDialog.showDialog(editor, error());
|
||
};
|
||
|
||
var instance = function () {
|
||
// this ensures we only show one banner per upload request, even on multiple failures
|
||
return Once(showDialog);
|
||
};
|
||
|
||
return {
|
||
instance: instance
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.flour.style.Resolver',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
var create = function (projectNamespace) {
|
||
var namespace = cssNamespace(projectNamespace);
|
||
|
||
var resolve = function (cssClasses) {
|
||
var classes = cssClasses.split(' ');
|
||
|
||
var resolved = Arr.map(classes, function (cls) {
|
||
return cssClass(namespace, cls);
|
||
});
|
||
|
||
return resolved.join(' ');
|
||
};
|
||
|
||
return {
|
||
resolve: resolve
|
||
};
|
||
};
|
||
|
||
// JavaScript namespaces are of the form "ephox.project"
|
||
// CSS namespaces are of the form "ephox-project"
|
||
var cssNamespace = function (namespace) {
|
||
return namespace.replace(/\./g, '-');
|
||
};
|
||
|
||
// CSS namespaced classes are of the form "css-namespace-class"
|
||
var cssClass = function (namespace, cls) {
|
||
return namespace + '-' + cls;
|
||
};
|
||
|
||
return {
|
||
create: create,
|
||
cssNamespace: cssNamespace,
|
||
cssClass: cssClass
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.style.Styles',
|
||
|
||
[
|
||
'ephox.flour.style.Resolver'
|
||
],
|
||
|
||
function (Resolver) {
|
||
var styles = Resolver.create('ephox-salmon');
|
||
|
||
return {
|
||
resolve: styles.resolve
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.classify.Type',
|
||
|
||
[
|
||
'global!Array',
|
||
'global!String'
|
||
],
|
||
|
||
function (Array, String) {
|
||
var typeOf = function(x) {
|
||
if (x === null) return 'null';
|
||
var t = typeof x;
|
||
if (t === 'object' && Array.prototype.isPrototypeOf(x)) return 'array';
|
||
if (t === 'object' && String.prototype.isPrototypeOf(x)) return 'string';
|
||
return t;
|
||
};
|
||
|
||
var isType = function (type) {
|
||
return function (value) {
|
||
return typeOf(value) === type;
|
||
};
|
||
};
|
||
|
||
return {
|
||
isString: isType('string'),
|
||
isObject: isType('object'),
|
||
isArray: isType('array'),
|
||
isNull: isType('null'),
|
||
isBoolean: isType('boolean'),
|
||
isUndefined: isType('undefined'),
|
||
isFunction: isType('function'),
|
||
isNumber: isType('number')
|
||
};
|
||
}
|
||
);
|
||
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.compass.Obj',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var each = function (obj, f) {
|
||
for (var i in obj) {
|
||
if (obj.hasOwnProperty(i)) {
|
||
var x = obj[i];
|
||
f(x, i, obj);
|
||
}
|
||
}
|
||
};
|
||
|
||
var objectMap = function (obj, f) {
|
||
return tupleMap(obj, function (x, i, obj) {
|
||
return {
|
||
k: i,
|
||
v: f(x, i, obj)
|
||
};
|
||
});
|
||
};
|
||
|
||
var tupleMap = function (obj, f) {
|
||
var r = {};
|
||
each(obj, function (x, i) {
|
||
var tuple = f(x, i, obj);
|
||
r[tuple.k] = tuple.v;
|
||
});
|
||
return r;
|
||
};
|
||
|
||
var bifilter = function (obj, pred) {
|
||
var t = {};
|
||
var f = {};
|
||
each(obj, function(x, i) {
|
||
var branch = pred(x, i) ? t : f;
|
||
branch[i] = x;
|
||
});
|
||
return {
|
||
t: t,
|
||
f: f
|
||
};
|
||
};
|
||
|
||
var mapToArray = function (obj, f) {
|
||
var r = [];
|
||
each(obj, function(value, name) {
|
||
r.push(f(value, name));
|
||
});
|
||
return r;
|
||
};
|
||
|
||
var find = function (obj, pred) {
|
||
for (var i in obj) {
|
||
if (obj.hasOwnProperty(i)) {
|
||
var x = obj[i];
|
||
if (pred(x, i, obj))
|
||
return x;
|
||
}
|
||
}
|
||
return undefined;
|
||
};
|
||
|
||
var keys = function (obj) {
|
||
return mapToArray(obj, function (v, k) {
|
||
return k;
|
||
});
|
||
};
|
||
|
||
var values = function (obj) {
|
||
return mapToArray(obj, function (v, k) {
|
||
return v;
|
||
});
|
||
};
|
||
|
||
var size = function (obj) {
|
||
return values(obj).length;
|
||
};
|
||
|
||
return {
|
||
bifilter: bifilter,
|
||
each: each,
|
||
map: objectMap,
|
||
mapToArray: mapToArray,
|
||
tupleMap: tupleMap,
|
||
find: find,
|
||
keys: keys,
|
||
values: values,
|
||
size: size
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!Error", [], function () { return Error; });
|
||
ephox.bolt.module.api.define("global!console", [], function () { if (typeof console === "undefined") console = { log: function () {} }; return console; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Attr',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'global!Error',
|
||
'global!console'
|
||
],
|
||
|
||
/*
|
||
* Direct attribute manipulation has been around since IE8, but
|
||
* was apparently unstable until IE10.
|
||
*/
|
||
function (Type, Arr, Obj, Error, console) {
|
||
var rawSet = function (dom, key, value) {
|
||
/*
|
||
* JQuery coerced everything to a string, and silently did nothing on text node/null/undefined.
|
||
*
|
||
* We fail on those invalid cases, only allowing numbers and booleans.
|
||
*/
|
||
if (Type.isString(value) || Type.isBoolean(value) || Type.isNumber(value)) {
|
||
dom.setAttribute(key, value + '');
|
||
} else {
|
||
console.error('Invalid call to Attr.set. Key ', key, ':: Value ', value, ':: Element ', dom);
|
||
throw new Error('Attribute value was not simple');
|
||
}
|
||
};
|
||
|
||
var set = function (element, key, value) {
|
||
rawSet(element.dom(), key, value);
|
||
};
|
||
|
||
var setAll = function (element, attrs) {
|
||
var dom = element.dom();
|
||
Obj.each(attrs, function (v, k) {
|
||
rawSet(dom, k, v);
|
||
});
|
||
};
|
||
|
||
var get = function (element, key) {
|
||
var v = element.dom().getAttribute(key);
|
||
|
||
// undefined is the more appropriate value for JS, and this matches JQuery
|
||
return v === null ? undefined : v;
|
||
};
|
||
|
||
var has = function (element, key) {
|
||
var dom = element.dom();
|
||
|
||
// return false for non-element nodes, no point in throwing an error
|
||
return dom && dom.hasAttribute ? dom.hasAttribute(key) : false;
|
||
};
|
||
|
||
var remove = function (element, key) {
|
||
element.dom().removeAttribute(key);
|
||
};
|
||
|
||
var hasNone = function (element) {
|
||
var attrs = element.dom().attributes;
|
||
return attrs === undefined || attrs === null || attrs.length === 0;
|
||
};
|
||
|
||
var clone = function (element) {
|
||
return Arr.foldl(element.dom().attributes, function (acc, attr) {
|
||
acc[attr.name] = attr.value;
|
||
return acc;
|
||
}, {});
|
||
};
|
||
|
||
return {
|
||
clone: clone,
|
||
set: set,
|
||
setAll: setAll,
|
||
get: get,
|
||
has: has,
|
||
remove: remove,
|
||
hasNone: hasNone
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.Immutable2',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Obj, Fun) {
|
||
|
||
var $a = function(args) {
|
||
return Array.prototype.slice.call(args);
|
||
};
|
||
|
||
var product = function(fields, eqs) {
|
||
|
||
var nu = function(/* values */) {
|
||
var values = $a(arguments);
|
||
if (fields.length !== values.length)
|
||
throw 'Wrong number of arguments to struct. Expected "[' + fields.length + ']", got ' + values.length + ' arguments';
|
||
|
||
var struct = {};
|
||
Arr.each(fields, function (name, i) {
|
||
struct[name] = Fun.constant(values[i]);
|
||
});
|
||
return struct;
|
||
};
|
||
|
||
var eq = function(a, b) {
|
||
for (var i = 0; i < fields.length; i++) {
|
||
var qqq = (eqs && eqs[i]) || Fun.tripleEquals;
|
||
var x = fields[i];
|
||
if (!qqq(a[x](), b[x]())) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
|
||
var evaluate = function(o) {
|
||
return Obj.map(o, function(f) {
|
||
return f();
|
||
});
|
||
};
|
||
|
||
return {
|
||
nu: nu,
|
||
eq: eq,
|
||
evaluate: evaluate
|
||
};
|
||
};
|
||
|
||
return {
|
||
product: product
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.Immutable',
|
||
|
||
[
|
||
'ephox.scullion.Immutable2'
|
||
],
|
||
|
||
function (Immutable2) {
|
||
return function (/* fields */) {
|
||
return Immutable2.product(arguments).nu;
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.BagUtils',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Type, Arr) {
|
||
var sort = function (arr) {
|
||
return arr.slice(0).sort();
|
||
};
|
||
|
||
var reqMessage = function (required, keys) {
|
||
throw 'All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.';
|
||
};
|
||
|
||
var unsuppMessage = function (unsupported) {
|
||
throw 'Unsupported keys for object: ' + sort(unsupported).join(', ');
|
||
};
|
||
|
||
var validateStrArr = function (label, array) {
|
||
if (!Type.isArray(array)) throw 'The ' + label + ' fields must be an array. Was: ' + array + '.';
|
||
Arr.each(array, function (a) {
|
||
if (!Type.isString(a)) throw 'The value ' + a + ' in the ' + label + ' fields was not a string.';
|
||
});
|
||
};
|
||
|
||
var invalidTypeMessage = function (incorrect, type) {
|
||
throw 'All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.';
|
||
};
|
||
|
||
var checkDupes = function (everything) {
|
||
var sorted = sort(everything);
|
||
var dupe = Arr.find(sorted, function (s, i) {
|
||
return i < sorted.length -1 && s === sorted[i + 1];
|
||
});
|
||
|
||
if (dupe !== undefined && dupe !== null) throw 'The field: ' + dupe + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].';
|
||
};
|
||
|
||
return {
|
||
sort: sort,
|
||
reqMessage: reqMessage,
|
||
unsuppMessage: unsuppMessage,
|
||
validateStrArr: validateStrArr,
|
||
invalidTypeMessage: invalidTypeMessage,
|
||
checkDupes: checkDupes
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!Object", [], function () { return Object; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.MixedBag',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.scullion.BagUtils',
|
||
'global!Object'
|
||
],
|
||
|
||
function (Arr, Obj, Fun, Option, BagUtils, Object) {
|
||
|
||
return function (required, optional) {
|
||
var everything = required.concat(optional);
|
||
if (everything.length === 0) throw 'You must specify at least one required or optional field.';
|
||
|
||
BagUtils.validateStrArr('required', required);
|
||
BagUtils.validateStrArr('optional', optional);
|
||
|
||
BagUtils.checkDupes(everything);
|
||
|
||
return function (obj) {
|
||
var keys = Obj.keys(obj);
|
||
|
||
// Ensure all required keys are present.
|
||
var allReqd = Arr.forall(required, function (req) {
|
||
return Arr.contains(keys, req);
|
||
});
|
||
|
||
if (! allReqd) BagUtils.reqMessage(required, keys);
|
||
|
||
var unsupported = Arr.filter(keys, function (key) {
|
||
return !Arr.contains(everything, key);
|
||
});
|
||
|
||
if (unsupported.length > 0) BagUtils.unsuppMessage(unsupported);
|
||
|
||
var r = {};
|
||
Arr.each(required, function (req) {
|
||
r[req] = Fun.constant(obj[req]);
|
||
});
|
||
|
||
Arr.each(optional, function (opt) {
|
||
r[opt] = Fun.constant(Object.prototype.hasOwnProperty.call(obj, opt) ? Option.some(obj[opt]): Option.none());
|
||
});
|
||
|
||
return r;
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.Struct',
|
||
|
||
[
|
||
'ephox.scullion.Immutable',
|
||
'ephox.scullion.Immutable2',
|
||
'ephox.scullion.MixedBag'
|
||
],
|
||
|
||
function (Immutable, Immutable2, MixedBag) {
|
||
return {
|
||
immutable: Immutable,
|
||
immutable2: Immutable2,
|
||
immutableBag: MixedBag
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.alien.Recurse',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
/**
|
||
* Applies f repeatedly until it completes (by returning Option.none()).
|
||
*
|
||
* Normally would just use recursion, but JavaScript lacks tail call optimisation.
|
||
*
|
||
* This is what recursion looks like when manually unravelled :)
|
||
*/
|
||
var toArray = function (target, f) {
|
||
var r = [];
|
||
|
||
var recurse = function (e) {
|
||
r.push(e);
|
||
return f(e);
|
||
};
|
||
|
||
var cur = f(target);
|
||
do {
|
||
cur = cur.bind(recurse);
|
||
} while (cur.isSome());
|
||
|
||
return r;
|
||
};
|
||
|
||
return {
|
||
toArray: toArray
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bud.NodeTypes',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
return {
|
||
ATTRIBUTE: 2,
|
||
CDATA_SECTION: 4,
|
||
COMMENT: 8,
|
||
DOCUMENT: 9,
|
||
DOCUMENT_TYPE: 10,
|
||
DOCUMENT_FRAGMENT: 11,
|
||
ELEMENT: 1,
|
||
TEXT: 3,
|
||
PROCESSING_INSTRUCTION: 7,
|
||
ENTITY_REFERENCE: 5,
|
||
ENTITY: 6,
|
||
NOTATION: 12
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Element',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'global!Error',
|
||
'global!console',
|
||
'global!document'
|
||
],
|
||
|
||
function (Fun, Error, console, document) {
|
||
var fromHtml = function (html, scope) {
|
||
var doc = scope || document;
|
||
var div = doc.createElement('div');
|
||
div.innerHTML = html;
|
||
if (!div.hasChildNodes() || div.childNodes.length > 1) {
|
||
console.error('HTML does not have a single root node', html);
|
||
throw 'HTML must have a single root node';
|
||
}
|
||
return fromDom(div.childNodes[0]);
|
||
};
|
||
|
||
var fromTag = function (tag, scope) {
|
||
var doc = scope || document;
|
||
var node = doc.createElement(tag);
|
||
return fromDom(node);
|
||
};
|
||
|
||
var fromText = function (text, scope) {
|
||
var doc = scope || document;
|
||
var node = doc.createTextNode(text);
|
||
return fromDom(node);
|
||
};
|
||
|
||
var fromDom = function (node) {
|
||
if (node === null || node === undefined) throw new Error('Node cannot be not null or undefined');
|
||
return {
|
||
dom: Fun.constant(node)
|
||
};
|
||
};
|
||
|
||
return {
|
||
fromHtml: fromHtml,
|
||
fromTag: fromTag,
|
||
fromText: fromText,
|
||
fromDom: fromDom
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Selectors',
|
||
|
||
[
|
||
'ephox.bud.NodeTypes',
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Element',
|
||
'global!Error',
|
||
'global!document'
|
||
],
|
||
|
||
function (NodeTypes, Arr, Option, Element, Error, document) {
|
||
/*
|
||
* There's a lot of code here; the aim is to allow the browser to optimise constant comparisons,
|
||
* instead of doing object lookup feature detection on every call
|
||
*/
|
||
var STANDARD = 0;
|
||
var MSSTANDARD = 1;
|
||
var WEBKITSTANDARD = 2;
|
||
var FIREFOXSTANDARD = 3;
|
||
|
||
var selectorType = (function () {
|
||
var test = document.createElement('span');
|
||
// As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function.
|
||
// Still check for the others, but do it last.
|
||
return test.matches !== undefined ? STANDARD :
|
||
test.msMatchesSelector !== undefined ? MSSTANDARD :
|
||
test.webkitMatchesSelector !== undefined ? WEBKITSTANDARD :
|
||
test.mozMatchesSelector !== undefined ? FIREFOXSTANDARD :
|
||
-1;
|
||
})();
|
||
|
||
|
||
var ELEMENT = NodeTypes.ELEMENT;
|
||
var DOCUMENT = NodeTypes.DOCUMENT;
|
||
|
||
var is = function (element, selector) {
|
||
var elem = element.dom();
|
||
if (elem.nodeType !== ELEMENT) return false; // documents have querySelector but not matches
|
||
|
||
// As of Chrome 34 / Safari 7.1 / FireFox 34, everyone except IE has the unprefixed function.
|
||
// Still check for the others, but do it last.
|
||
else if (selectorType === STANDARD) return elem.matches(selector);
|
||
else if (selectorType === MSSTANDARD) return elem.msMatchesSelector(selector);
|
||
else if (selectorType === WEBKITSTANDARD) return elem.webkitMatchesSelector(selector);
|
||
else if (selectorType === FIREFOXSTANDARD) return elem.mozMatchesSelector(selector);
|
||
else throw new Error('Browser lacks native selectors'); // unfortunately we can't throw this on startup :(
|
||
};
|
||
|
||
var bypassSelector = function (dom) {
|
||
// Only elements and documents support querySelector
|
||
return dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT ||
|
||
// IE fix for complex queries on empty nodes: http://jsfiddle.net/spyder/fv9ptr5L/
|
||
dom.childElementCount === 0;
|
||
};
|
||
|
||
var all = function (selector, scope) {
|
||
var base = scope === undefined ? document : scope.dom();
|
||
return bypassSelector(base) ? [] : Arr.map(base.querySelectorAll(selector), Element.fromDom);
|
||
};
|
||
|
||
var one = function (selector, scope) {
|
||
var base = scope === undefined ? document : scope.dom();
|
||
return bypassSelector(base) ? Option.none() : Option.from(base.querySelector(selector)).map(Element.fromDom);
|
||
};
|
||
|
||
return {
|
||
all: all,
|
||
is: is,
|
||
one: one
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Compare',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Selectors'
|
||
],
|
||
|
||
function (Arr, Fun, Selectors) {
|
||
var eq = function (e1, e2) {
|
||
return e1.dom() === e2.dom();
|
||
};
|
||
|
||
var member = function (element, elements) {
|
||
return Arr.exists(elements, Fun.curry(eq, element));
|
||
};
|
||
|
||
return {
|
||
eq: eq,
|
||
member: member,
|
||
|
||
// Only used by DomUniverse. Remove (or should Selectors.is move here?)
|
||
is: Selectors.is
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Traverse',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.alien.Recurse',
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Type, Arr, Fun, Option, Struct, Recurse, Compare, Element) {
|
||
// The document associated with the current element
|
||
var owner = function (element) {
|
||
return Element.fromDom(element.dom().ownerDocument);
|
||
};
|
||
|
||
var documentElement = function (element) {
|
||
// TODO: Avoid unnecessary wrap/unwrap here
|
||
var doc = owner(element);
|
||
return Element.fromDom(doc.dom().documentElement);
|
||
};
|
||
|
||
// The window element associated with the element
|
||
var defaultView = function (element) {
|
||
var el = element.dom();
|
||
var defaultView = el.ownerDocument.defaultView;
|
||
return Element.fromDom(defaultView);
|
||
};
|
||
|
||
var parent = function (element) {
|
||
var dom = element.dom();
|
||
return Option.from(dom.parentNode).map(Element.fromDom);
|
||
};
|
||
|
||
var findIndex = function (element) {
|
||
return parent(element).bind(function (p) {
|
||
// TODO: Refactor out children so we can avoid the constant unwrapping
|
||
var kin = children(p);
|
||
var index = Arr.findIndex(kin, function (elem) {
|
||
return Compare.eq(element, elem);
|
||
});
|
||
|
||
return index > -1 ? Option.some(index) : Option.none();
|
||
});
|
||
};
|
||
|
||
var parents = function (element, isRoot) {
|
||
var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false);
|
||
return internalParents(element, stop);
|
||
};
|
||
|
||
var internalParents = function (element, stop) {
|
||
return parent(element).fold(function () {
|
||
return [];
|
||
}, function (v) {
|
||
var ret = [ v ];
|
||
return stop(v) ? ret : ret.concat(internalParents(v, stop));
|
||
});
|
||
};
|
||
|
||
var siblings = function (element) {
|
||
// TODO: Refactor out children so we can just not add self instead of filtering afterwards
|
||
var filterSelf = function (elements) {
|
||
return Arr.filter(elements, function (x) {
|
||
return !Compare.eq(element, x);
|
||
});
|
||
};
|
||
|
||
return parent(element).map(children).map(filterSelf).getOr([]);
|
||
};
|
||
|
||
var offsetParent = function (element) {
|
||
var dom = element.dom();
|
||
return Option.from(dom.offsetParent).map(Element.fromDom);
|
||
};
|
||
|
||
var prevSibling = function (element) {
|
||
var dom = element.dom();
|
||
return Option.from(dom.previousSibling).map(Element.fromDom);
|
||
};
|
||
|
||
var nextSibling = function (element) {
|
||
var dom = element.dom();
|
||
return Option.from(dom.nextSibling).map(Element.fromDom);
|
||
};
|
||
|
||
var prevSiblings = function (element) {
|
||
// This one needs to be reversed, so they're still in DOM order
|
||
return Arr.reverse(Recurse.toArray(element, prevSibling));
|
||
};
|
||
|
||
var nextSiblings = function (element) {
|
||
return Recurse.toArray(element, nextSibling);
|
||
};
|
||
|
||
var children = function (element) {
|
||
var dom = element.dom();
|
||
return Arr.map(dom.childNodes, Element.fromDom);
|
||
};
|
||
|
||
var child = function (element, index) {
|
||
var children = element.dom().childNodes;
|
||
return Option.from(children[index]).map(Element.fromDom);
|
||
};
|
||
|
||
var firstChild = function (element) {
|
||
return child(element, 0);
|
||
};
|
||
|
||
var lastChild = function (element) {
|
||
return child(element, element.dom().childNodes.length - 1);
|
||
};
|
||
|
||
var spot = Struct.immutable('element', 'offset');
|
||
var leaf = function (element, offset) {
|
||
var cs = children(element);
|
||
return cs.length > 0 && offset < cs.length ? spot(cs[offset], 0) : spot(element, offset);
|
||
};
|
||
|
||
return {
|
||
owner: owner,
|
||
defaultView: defaultView,
|
||
documentElement: documentElement,
|
||
parent: parent,
|
||
findIndex: findIndex,
|
||
parents: parents,
|
||
siblings: siblings,
|
||
prevSibling: prevSibling,
|
||
offsetParent: offsetParent,
|
||
prevSiblings: prevSiblings,
|
||
nextSibling: nextSibling,
|
||
nextSiblings: nextSiblings,
|
||
children: children,
|
||
child: child,
|
||
firstChild: firstChild,
|
||
lastChild: lastChild,
|
||
leaf: leaf
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Insert',
|
||
|
||
[
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Traverse) {
|
||
var before = function (marker, element) {
|
||
var parent = Traverse.parent(marker);
|
||
parent.each(function (v) {
|
||
v.dom().insertBefore(element.dom(), marker.dom());
|
||
});
|
||
};
|
||
|
||
var after = function (marker, element) {
|
||
var sibling = Traverse.nextSibling(marker);
|
||
sibling.fold(function () {
|
||
var parent = Traverse.parent(marker);
|
||
parent.each(function (v) {
|
||
append(v, element);
|
||
});
|
||
}, function (v) {
|
||
before(v, element);
|
||
});
|
||
};
|
||
|
||
var prepend = function (parent, element) {
|
||
var firstChild = Traverse.firstChild(parent);
|
||
firstChild.fold(function () {
|
||
append(parent, element);
|
||
}, function (v) {
|
||
parent.dom().insertBefore(element.dom(), v.dom());
|
||
});
|
||
};
|
||
|
||
var append = function (parent, element) {
|
||
parent.dom().appendChild(element.dom());
|
||
};
|
||
|
||
var appendAt = function (parent, element, index) {
|
||
Traverse.child(parent, index).fold(function () {
|
||
append(parent, element);
|
||
}, function (v) {
|
||
before(v, element);
|
||
});
|
||
};
|
||
|
||
var wrap = function (element, wrapper) {
|
||
before(element, wrapper);
|
||
append(wrapper, element);
|
||
};
|
||
|
||
return {
|
||
before: before,
|
||
after: after,
|
||
prepend: prepend,
|
||
append: append,
|
||
appendAt: appendAt,
|
||
wrap: wrap
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.InsertAll',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Insert'
|
||
],
|
||
|
||
function (Arr, Insert) {
|
||
var before = function (marker, elements) {
|
||
Arr.each(elements, function (x) {
|
||
Insert.before(marker, x);
|
||
});
|
||
};
|
||
|
||
var after = function (marker, elements) {
|
||
Arr.each(elements, function (x, i) {
|
||
var e = i === 0 ? marker : elements[i - 1];
|
||
Insert.after(e, x);
|
||
});
|
||
};
|
||
|
||
var prepend = function (parent, elements) {
|
||
Arr.each(elements.slice().reverse(), function (x) {
|
||
Insert.prepend(parent, x);
|
||
});
|
||
};
|
||
|
||
var append = function (parent, elements) {
|
||
Arr.each(elements, function (x) {
|
||
Insert.append(parent, x);
|
||
});
|
||
};
|
||
|
||
return {
|
||
before: before,
|
||
after: after,
|
||
prepend: prepend,
|
||
append: append
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Remove',
|
||
|
||
[
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (InsertAll, Traverse) {
|
||
var empty = function (element) {
|
||
// requires IE 9
|
||
element.dom().textContent = '';
|
||
};
|
||
|
||
var remove = function (element) {
|
||
var dom = element.dom();
|
||
if (dom.parentNode !== null)
|
||
dom.parentNode.removeChild(dom);
|
||
};
|
||
|
||
var unwrap = function (wrapper) {
|
||
var children = Traverse.children(wrapper);
|
||
if (children.length > 0)
|
||
InsertAll.before(wrapper, children);
|
||
remove(wrapper);
|
||
};
|
||
|
||
return {
|
||
empty: empty,
|
||
remove: remove,
|
||
unwrap: unwrap
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.peanut.Thunk',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
|
||
var cached = function (f) {
|
||
var called = false;
|
||
var r;
|
||
return function() {
|
||
if (!called) {
|
||
called = true;
|
||
r = f.apply(null, arguments);
|
||
}
|
||
return r;
|
||
};
|
||
};
|
||
|
||
return {
|
||
cached: cached
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Node',
|
||
|
||
[
|
||
'ephox.bud.NodeTypes'
|
||
],
|
||
|
||
function (NodeTypes) {
|
||
var name = function (element) {
|
||
var r = element.dom().nodeName;
|
||
return r.toLowerCase();
|
||
};
|
||
|
||
var type = function (element) {
|
||
return element.dom().nodeType;
|
||
};
|
||
|
||
var value = function (element) {
|
||
return element.dom().nodeValue;
|
||
};
|
||
|
||
var isType = function (t) {
|
||
return function (element) {
|
||
return type(element) === t;
|
||
};
|
||
};
|
||
|
||
var isComment = function (element) {
|
||
return type(element) === NodeTypes.COMMENT || name(element) === '#comment';
|
||
};
|
||
|
||
var isElement = isType(NodeTypes.ELEMENT);
|
||
var isText = isType(NodeTypes.TEXT);
|
||
var isDocument = isType(NodeTypes.DOCUMENT);
|
||
|
||
return {
|
||
name: name,
|
||
type: type,
|
||
value: value,
|
||
isElement: isElement,
|
||
isText: isText,
|
||
isDocument: isDocument,
|
||
isComment: isComment
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Body',
|
||
|
||
[
|
||
'ephox.peanut.Thunk',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Node',
|
||
'global!document'
|
||
],
|
||
|
||
function (Thunk, Element, Node, document) {
|
||
|
||
// Node.contains() is very, very, very good performance
|
||
// http://jsperf.com/closest-vs-contains/5
|
||
var inBody = function (element) {
|
||
// Technically this is only required on IE, where contains() returns false for text nodes.
|
||
// But it's cheap enough to run everywhere and Sugar doesn't have platform detection (yet).
|
||
var dom = Node.isText(element) ? element.dom().parentNode : element.dom();
|
||
|
||
// use ownerDocument.body to ensure this works inside iframes.
|
||
// Normally contains is bad because an element "contains" itself, but here we want that.
|
||
return dom !== undefined && dom !== null && dom.ownerDocument.body.contains(dom);
|
||
};
|
||
|
||
var body = Thunk.cached(function () {
|
||
var body = document.body;
|
||
if (body === null || body === undefined) throw 'Body is not available yet';
|
||
return Element.fromDom(body);
|
||
});
|
||
|
||
return {
|
||
body: body,
|
||
inBody: inBody
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.PredicateFilter',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Body',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Arr, Body, Traverse) {
|
||
// maybe TraverseWith, similar to traverse but with a predicate?
|
||
|
||
var all = function (predicate) {
|
||
return descendants(Body.body(), predicate);
|
||
};
|
||
|
||
var ancestors = function (scope, predicate, isRoot) {
|
||
return Arr.filter(Traverse.parents(scope, isRoot), predicate);
|
||
};
|
||
|
||
var siblings = function (scope, predicate) {
|
||
return Arr.filter(Traverse.siblings(scope), predicate);
|
||
};
|
||
|
||
var children = function (scope, predicate) {
|
||
return Arr.filter(Traverse.children(scope), predicate);
|
||
};
|
||
|
||
var descendants = function (scope, predicate) {
|
||
var result = [];
|
||
|
||
// Recurse.toArray() might help here
|
||
Arr.each(Traverse.children(scope), function (x) {
|
||
if (predicate(x)) {
|
||
result = result.concat([ x ]);
|
||
}
|
||
result = result.concat(descendants(x, predicate));
|
||
});
|
||
return result;
|
||
};
|
||
|
||
return {
|
||
all: all,
|
||
ancestors: ancestors,
|
||
siblings: siblings,
|
||
children: children,
|
||
descendants: descendants
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.SelectorFilter',
|
||
|
||
[
|
||
'ephox.sugar.api.PredicateFilter',
|
||
'ephox.sugar.api.Selectors'
|
||
],
|
||
|
||
function (PredicateFilter, Selectors) {
|
||
var all = function (selector) {
|
||
return Selectors.all(selector);
|
||
};
|
||
|
||
// For all of the following:
|
||
//
|
||
// jQuery does siblings of firstChild. IE9+ supports scope.dom().children (similar to Traverse.children but elements only).
|
||
// Traverse should also do this (but probably not by default).
|
||
//
|
||
|
||
var ancestors = function (scope, selector, isRoot) {
|
||
// It may surprise you to learn this is exactly what JQuery does
|
||
// TODO: Avoid all this wrapping and unwrapping
|
||
return PredicateFilter.ancestors(scope, function (e) {
|
||
return Selectors.is(e, selector);
|
||
}, isRoot);
|
||
};
|
||
|
||
var siblings = function (scope, selector) {
|
||
// It may surprise you to learn this is exactly what JQuery does
|
||
// TODO: Avoid all the wrapping and unwrapping
|
||
return PredicateFilter.siblings(scope, function (e) {
|
||
return Selectors.is(e, selector);
|
||
});
|
||
};
|
||
|
||
var children = function (scope, selector) {
|
||
// It may surprise you to learn this is exactly what JQuery does
|
||
// TODO: Avoid all the wrapping and unwrapping
|
||
return PredicateFilter.children(scope, function (e) {
|
||
return Selectors.is(e, selector);
|
||
});
|
||
};
|
||
|
||
var descendants = function (scope, selector) {
|
||
return Selectors.all(selector, scope);
|
||
};
|
||
|
||
return {
|
||
all: all,
|
||
ancestors: ancestors,
|
||
siblings: siblings,
|
||
children: children,
|
||
descendants: descendants
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.impl.ClosestOrAncestor',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Type, Option) {
|
||
return function (is, ancestor, scope, a, isRoot) {
|
||
return is(scope, a) ?
|
||
Option.some(scope) :
|
||
Type.isFunction(isRoot) && isRoot(scope) ?
|
||
Option.none() :
|
||
ancestor(scope, a, isRoot);
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.SelectorFind',
|
||
|
||
[
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'ephox.sugar.api.Selectors',
|
||
'ephox.sugar.impl.ClosestOrAncestor'
|
||
],
|
||
|
||
function (Option, SelectorFilter, Selectors, ClosestOrAncestor) {
|
||
// It's ridiculous to spend all that time finding everything and then just get the first.
|
||
// Two suggestions:
|
||
// * An internal SelectorFilter module that doesn't Element.fromDom() everything
|
||
// * Re-implement using Selectors.one() instead of Selectors.all()
|
||
|
||
var first = function (selector) {
|
||
return Option.from(SelectorFilter.all(selector)[0]);
|
||
};
|
||
|
||
var ancestor = function (scope, selector, isRoot) {
|
||
return Option.from(SelectorFilter.ancestors(scope, selector, isRoot)[0]);
|
||
};
|
||
|
||
var sibling = function (scope, selector) {
|
||
return Option.from(SelectorFilter.siblings(scope, selector)[0]);
|
||
};
|
||
|
||
var child = function (scope, selector) {
|
||
return Option.from(SelectorFilter.children(scope, selector)[0]);
|
||
};
|
||
|
||
var descendant = function (scope, selector) {
|
||
return Option.from(SelectorFilter.descendants(scope, selector)[0]);
|
||
};
|
||
|
||
var closest = function (scope, selector, isRoot) {
|
||
return ClosestOrAncestor(Selectors.is, ancestor, scope, selector, isRoot);
|
||
};
|
||
|
||
return {
|
||
first: first,
|
||
ancestor: ancestor,
|
||
sibling: sibling,
|
||
child: child,
|
||
descendant: descendant,
|
||
closest: closest
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.api.Ephemera',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.salmon.style.Styles',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFind'
|
||
],
|
||
|
||
function (Fun, Styles, Attr, Remove, SelectorFind) {
|
||
var uploadContainer = Styles.resolve('upload-image-container');
|
||
var blobId = 'data-' + Styles.resolve('image-blob');
|
||
|
||
var cleanup = function (element) {
|
||
SelectorFind.child(element, 'img').each(cleanImg);
|
||
Remove.unwrap(element);
|
||
};
|
||
|
||
var cleanImg = function (element) {
|
||
Attr.remove(element, 'class');
|
||
};
|
||
|
||
return {
|
||
uploadContainer: Fun.constant(uploadContainer),
|
||
blobId: Fun.constant(blobId),
|
||
cleanup: cleanup
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Elements',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Traverse',
|
||
'global!document'
|
||
],
|
||
|
||
function (Arr, Element, Traverse, document) {
|
||
var fromHtml = function (html, scope) {
|
||
var doc = scope || document;
|
||
var div = doc.createElement('div');
|
||
div.innerHTML = html;
|
||
return Traverse.children(Element.fromDom(div));
|
||
};
|
||
|
||
var fromTags = function (tags, scope) {
|
||
return Arr.map(tags, function (x) {
|
||
return Element.fromTag(x, scope);
|
||
});
|
||
};
|
||
|
||
var fromText = function (texts, scope) {
|
||
return Arr.map(texts, function (x) {
|
||
return Element.fromText(x, scope);
|
||
});
|
||
};
|
||
|
||
var fromDom = function (nodes) {
|
||
return Arr.map(nodes, Element.fromDom);
|
||
};
|
||
|
||
return {
|
||
fromHtml: fromHtml,
|
||
fromTags: fromTags,
|
||
fromText: fromText,
|
||
fromDom: fromDom
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define('ephox.powerpaste.tinymce.UndoRewriter',
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.salmon.api.Ephemera',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Elements',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.SelectorFilter'
|
||
],
|
||
function(Arr, UiEphemera, Element, Elements, InsertAll, SelectorFilter) {
|
||
var unwrapHistory = function(editor) {
|
||
//Start undomanager hack
|
||
for (var i = 0; i < editor.undoManager.data.length; i ++) {
|
||
//get the content of every undomanager back stack level
|
||
var content = editor.undoManager.data[i].content;
|
||
var temp = Element.fromTag('div');
|
||
InsertAll.append(temp, Elements.fromHtml(content));
|
||
//Find uploaded image containers
|
||
var uploadContainers = SelectorFilter.descendants(temp, '.' + UiEphemera.uploadContainer());
|
||
//Strip the containers
|
||
Arr.each(uploadContainers, UiEphemera.cleanup);
|
||
editor.undoManager.data[i].content = temp.dom().innerHTML;
|
||
}
|
||
};
|
||
|
||
var resrcHistory = function(editor, b, result) {
|
||
for (var i = 0; i < editor.undoManager.data.length; i ++) {
|
||
editor.undoManager.data[i].content = editor.undoManager.data[i].content.split(b.objurl()).join(result.location);
|
||
}
|
||
};
|
||
|
||
return {
|
||
unwrapHistory: unwrapHistory,
|
||
resrcHistory: resrcHistory
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.epithet.Global',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
return Function('return this;')();
|
||
}
|
||
);
|
||
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.epithet.Resolve',
|
||
|
||
[
|
||
'ephox.epithet.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
var path = function (parts, scope) {
|
||
var o = scope || Global;
|
||
for (var i = 0; i < parts.length && o !== undefined && o !== null; ++i)
|
||
o = o[parts[i]];
|
||
return o;
|
||
};
|
||
|
||
var resolve = function (p, scope) {
|
||
var parts = p.split('.');
|
||
return path(parts, scope);
|
||
};
|
||
|
||
return {
|
||
path: path,
|
||
resolve: resolve
|
||
};
|
||
}
|
||
);
|
||
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.core.Global',
|
||
|
||
[
|
||
'ephox.epithet.Resolve'
|
||
],
|
||
|
||
function (Resolve) {
|
||
var unsafe = function (name, scope) {
|
||
return Resolve.resolve(name, scope);
|
||
};
|
||
|
||
var getOrDie = function (name, scope) {
|
||
var actual = unsafe(name, scope);
|
||
|
||
// In theory, TBIO should refuse to load below IE10. But we'll enforce it here too.
|
||
if (actual === undefined) throw name + ' not available on this browser';
|
||
return actual;
|
||
};
|
||
|
||
return {
|
||
getOrDie: getOrDie
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.URL',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE10 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL
|
||
*
|
||
* Also Safari 6.1+
|
||
* Safari 6.0 has 'webkitURL' instead, but doesn't support flexbox so we
|
||
* aren't supporting it anyway
|
||
*/
|
||
var url = function () {
|
||
return Global.getOrDie('URL');
|
||
};
|
||
|
||
var createObjectURL = function (blob) {
|
||
return url().createObjectURL(blob);
|
||
};
|
||
|
||
var revokeObjectURL = function (u) {
|
||
url().revokeObjectURL(u);
|
||
};
|
||
|
||
return {
|
||
createObjectURL: createObjectURL,
|
||
revokeObjectURL: revokeObjectURL
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.api.BlobCache',
|
||
|
||
[
|
||
'ephox.compass.Obj',
|
||
'ephox.numerosity.api.URL',
|
||
'ephox.perhaps.Option',
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Obj, URL, Option, Struct) {
|
||
var blobInfo = Struct.immutable('id', 'blob', 'objurl', 'data');
|
||
|
||
return function () {
|
||
var blobCache = {};
|
||
|
||
var add = function (id, blob, objurl, data) {
|
||
var info = blobInfo(id, blob, objurl, data);
|
||
blobCache[id] = info;
|
||
return info;
|
||
};
|
||
|
||
var get = function (id) {
|
||
return Option.from(blobCache[id]);
|
||
};
|
||
|
||
var release = function (info) {
|
||
URL.revokeObjectURL(info.objurl());
|
||
};
|
||
|
||
var lookupByData = function (data) {
|
||
return Option.from(Obj.find(blobCache, function (c) {
|
||
return c.data().result === data;
|
||
}));
|
||
};
|
||
|
||
var remove = function (id) {
|
||
var o = blobCache[id];
|
||
delete blobCache[id];
|
||
if (o !== undefined) release(o);
|
||
};
|
||
|
||
var destroy = function () {
|
||
Obj.each(blobCache, release);
|
||
blobCache = {};
|
||
};
|
||
|
||
return {
|
||
add: add,
|
||
get: get,
|
||
remove: remove,
|
||
lookupByData: lookupByData,
|
||
destroy: destroy
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.porkbun.Event',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.scullion.Struct'
|
||
],
|
||
function (Arr, Struct) {
|
||
|
||
/** :: ([String]) -> Event */
|
||
return function (fields) {
|
||
var struct = Struct.immutable.apply(null, fields);
|
||
|
||
var handlers = [];
|
||
|
||
var bind = function (handler) {
|
||
if (handler === undefined) {
|
||
throw 'Event bind error: undefined handler';
|
||
}
|
||
handlers.push(handler);
|
||
};
|
||
|
||
var unbind = function(handler) {
|
||
var index = Arr.indexOf(handlers, handler);
|
||
if (index !== -1) {
|
||
handlers.splice(index, 1);
|
||
}
|
||
};
|
||
|
||
var trigger = function (/* values */) {
|
||
// scullion does Array prototype slice, we don't need to as well
|
||
var event = struct.apply(null, arguments);
|
||
Arr.each(handlers, function (handler) {
|
||
handler(event);
|
||
});
|
||
};
|
||
|
||
return {
|
||
bind: bind,
|
||
unbind: unbind,
|
||
trigger: trigger
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.porkbun.Events',
|
||
|
||
[
|
||
'ephox.compass.Obj'
|
||
],
|
||
|
||
function (Obj) {
|
||
|
||
/** :: {name : Event} -> Events */
|
||
var create = function (typeDefs) {
|
||
var registry = Obj.map(typeDefs, function (event) {
|
||
return {
|
||
bind: event.bind,
|
||
unbind: event.unbind
|
||
};
|
||
});
|
||
|
||
var trigger = Obj.map(typeDefs, function (event) {
|
||
return event.trigger;
|
||
});
|
||
|
||
return {
|
||
registry: registry,
|
||
trigger: trigger
|
||
};
|
||
};
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.api.ImageTracker',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.salmon.style.Styles',
|
||
'ephox.salmon.api.Ephemera',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.SelectorFilter'
|
||
],
|
||
|
||
function (Arr, Styles, Ephemera, Event, Events, Attr, SelectorFilter) {
|
||
var uploadAttr = 'data-' + Styles.resolve('image-upload');
|
||
|
||
var findById = function (body, id) {
|
||
return SelectorFilter.descendants(body, 'img[' + uploadAttr + '="' + id + '"]');
|
||
};
|
||
|
||
var findAll = function (body) {
|
||
// Find all images that are registered in the blob tracker, but aren't uploading
|
||
return SelectorFilter.descendants(body, 'img:not([' + uploadAttr + '])[' + Ephemera.blobId() + ']');
|
||
};
|
||
|
||
return function () {
|
||
var imgStack = [];
|
||
var response = [];
|
||
|
||
var events = Events.create({
|
||
complete: Event(['response'])
|
||
});
|
||
|
||
var register = function (img, id) {
|
||
Attr.set(img, uploadAttr, id);
|
||
imgStack.push(id);
|
||
};
|
||
|
||
var deregister = function (id) {
|
||
imgStack = Arr.filter(imgStack, function (val, index) {
|
||
return val !== id;
|
||
});
|
||
if (inProgress() === false) finished();
|
||
};
|
||
|
||
var result = function (bool, element) {
|
||
response.push({
|
||
'success': bool,
|
||
'element': element.dom()
|
||
});
|
||
};
|
||
|
||
var report = function (id, images, success) {
|
||
Arr.each(images, function (img) {
|
||
Attr.remove(img, uploadAttr);
|
||
result(success, img);
|
||
});
|
||
deregister(id);
|
||
};
|
||
|
||
var finished = function () {
|
||
events.trigger.complete(response);
|
||
response = [];
|
||
};
|
||
|
||
var inProgress = function () {
|
||
return imgStack.length > 0;
|
||
};
|
||
|
||
var isActive = function (id) {
|
||
return Arr.contains(imgStack, id);
|
||
};
|
||
|
||
return {
|
||
findById: findById,
|
||
findAll: findAll,
|
||
register: register,
|
||
report: report,
|
||
inProgress: inProgress,
|
||
isActive: isActive,
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.highway.Merger',
|
||
|
||
[
|
||
'ephox.classify.Type'
|
||
],
|
||
|
||
function (Type) {
|
||
|
||
var shallow = function (old, nu) {
|
||
return nu;
|
||
};
|
||
|
||
var deep = function (old, nu) {
|
||
var bothObjects = Type.isObject(old) && Type.isObject(nu);
|
||
return bothObjects ? deepMerge(old, nu) : nu;
|
||
};
|
||
|
||
var baseMerge = function (merger) {
|
||
return function() {
|
||
var objects = Array.prototype.slice.call(arguments, 0);
|
||
if (objects.length === 0) throw "Can't merge zero objects";
|
||
|
||
var ret = {};
|
||
for (var i = 0; i < objects.length; i++) {
|
||
var curObject = objects[i];
|
||
// FIX Merger 14/02/2012 Replace with the functional iterators / maps
|
||
for (var key in curObject) if (Object.prototype.hasOwnProperty.call(curObject, key)) {
|
||
ret[key] = merger(ret[key], curObject[key]);
|
||
}
|
||
}
|
||
return ret;
|
||
};
|
||
};
|
||
|
||
var deepMerge = baseMerge(deep);
|
||
var merge = baseMerge(shallow);
|
||
|
||
return {
|
||
deepMerge: deepMerge,
|
||
merge: merge
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.ADT',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.peanut.Fun',
|
||
'global!Array'
|
||
],
|
||
|
||
function (Type, Arr, Obj, Fun, Array) {
|
||
/*
|
||
* Generates a church encoded ADT. No, I'm not going to explain what that is here.
|
||
*
|
||
* The aim of this file is to replace the extreme ADT syntax we have been using
|
||
* (50 lines of code for a simple variant with 4 cases). Specifying the ADT
|
||
* can now be done in one line per case, and proper validation is included.
|
||
*
|
||
* For syntax and use, look at the test code.
|
||
*/
|
||
var generate = function (cases) {
|
||
// validation
|
||
if (!Type.isArray(cases)) {
|
||
throw 'cases must be an array';
|
||
}
|
||
if (cases.length === 0) {
|
||
throw 'there must be at least one case';
|
||
}
|
||
// adt is mutated to add the individual cases
|
||
var adt = {};
|
||
Arr.each(cases, function (acase, count) {
|
||
var keys = Obj.keys(acase);
|
||
|
||
// validation
|
||
if (keys.length !== 1) {
|
||
throw 'one and only one name per case';
|
||
}
|
||
|
||
var key = keys[0];
|
||
var value = acase[key];
|
||
|
||
// validation
|
||
if (adt[key] !== undefined) {
|
||
throw 'duplicate key detected:' + key;
|
||
} else if (key === 'cata') {
|
||
throw 'cannot have a case named cata (sorry)';
|
||
} else if (!Type.isArray(value)) {
|
||
// this implicitly checks if acase is an object
|
||
throw 'case arguments must be an array';
|
||
}
|
||
//
|
||
// constructor for key
|
||
//
|
||
adt[key] = function () {
|
||
var args = Array.prototype.slice.call(arguments);
|
||
// validation
|
||
if (args.length !== value.length) {
|
||
throw 'Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + args.length;
|
||
}
|
||
|
||
|
||
//
|
||
// the fold function for key
|
||
//
|
||
return {
|
||
fold: function (/* arguments */) {
|
||
// runtime validation
|
||
if (arguments.length !== cases.length) {
|
||
throw 'Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + arguments.length;
|
||
}
|
||
var target = arguments[count];
|
||
return target.apply(null, args);
|
||
}
|
||
};
|
||
};
|
||
});
|
||
|
||
return adt;
|
||
};
|
||
return {
|
||
generate: generate
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.hermes.api.ImageAsset',
|
||
|
||
[
|
||
'ephox.highway.Merger',
|
||
'ephox.scullion.ADT'
|
||
],
|
||
|
||
function (Merger, ADT) {
|
||
/*
|
||
* An arbitrary common data structure for handling both local image files
|
||
* and images from web urls.
|
||
*/
|
||
var adt = ADT.generate([
|
||
{ 'blob': // Local image. W3C blob object (or File).
|
||
[ // NOTE File is just a subclass of Blob
|
||
'id', // unique ID
|
||
'blob', // the entire blob object
|
||
'objurl', // an object URL - THIS MUST BE RELEASED WHEN DONE
|
||
'data' // FileReader instance - already complete - loaded using readAsDataURL().
|
||
// we're storing this rather than result in the hope it will
|
||
// keep the string native rather than convert to JS
|
||
]
|
||
},
|
||
{ 'url': ['id', 'url', 'raw'] } // Remote image. JS image object/element loaded via url
|
||
|
||
]);
|
||
|
||
var cata = function (subject, onFile, onImage) {
|
||
return subject.fold(onFile, onImage);
|
||
};
|
||
|
||
return Merger.merge(adt, {
|
||
cata: cata
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.perhaps.Result',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Fun, Option) {
|
||
var value = function (r) {
|
||
return result(function (e, v) {
|
||
return v(r);
|
||
});
|
||
};
|
||
|
||
var error = function (message) {
|
||
return result(function (e, v) {
|
||
return e(message);
|
||
});
|
||
};
|
||
|
||
var result = function (fold) {
|
||
|
||
var is = function (v) {
|
||
return fold(Fun.constant(false), function (o) {
|
||
return o === v;
|
||
});
|
||
};
|
||
|
||
var isValue = function () {
|
||
return fold(Fun.constant(false), Fun.constant(true));
|
||
};
|
||
|
||
var isError = Fun.not(isValue);
|
||
|
||
var getOr = function (a) {
|
||
return fold(Fun.constant(a), Fun.identity);
|
||
};
|
||
|
||
var getOrThunk = function (f) {
|
||
return fold(f, Fun.identity);
|
||
};
|
||
|
||
var getOrDie = function () {
|
||
return fold(function (m) {
|
||
Fun.die(m)();
|
||
}, Fun.identity);
|
||
};
|
||
|
||
var or = function (opt) {
|
||
return fold(Fun.constant(opt), value);
|
||
};
|
||
|
||
var orThunk = function (f) {
|
||
return fold(f, value);
|
||
};
|
||
|
||
var map = function (f) {
|
||
return bind(function (a) {
|
||
return value(f(a));
|
||
});
|
||
};
|
||
|
||
var each = map;
|
||
|
||
var bind = function (f) {
|
||
return fold(error, f);
|
||
};
|
||
|
||
var exists = function (f) {
|
||
return fold(Fun.constant(false), f);
|
||
};
|
||
|
||
var forall = function (f) {
|
||
return fold(Fun.constant(true), f);
|
||
};
|
||
|
||
var toOption = function () {
|
||
return fold(Option.none, Option.some);
|
||
};
|
||
|
||
return {
|
||
is: is,
|
||
isValue: isValue,
|
||
isError: isError,
|
||
getOr: getOr,
|
||
getOrThunk: getOrThunk,
|
||
getOrDie: getOrDie,
|
||
or: or,
|
||
orThunk: orThunk,
|
||
fold: fold,
|
||
map: map,
|
||
each: each,
|
||
bind: bind,
|
||
exists: exists,
|
||
forall: forall,
|
||
toOption: toOption
|
||
};
|
||
};
|
||
|
||
return {
|
||
value: value,
|
||
error: error
|
||
};
|
||
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.alien.Toggler',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
return function (turnOff, turnOn, initial) {
|
||
var active = initial || false;
|
||
|
||
var on = function () {
|
||
turnOn();
|
||
active = true;
|
||
};
|
||
|
||
var off = function () {
|
||
turnOff();
|
||
active = false;
|
||
};
|
||
|
||
var toggle = function () {
|
||
var f = active ? off : on;
|
||
f();
|
||
};
|
||
|
||
var isOn = function () {
|
||
return active;
|
||
};
|
||
|
||
return {
|
||
on: on,
|
||
off: off,
|
||
toggle: toggle,
|
||
isOn: isOn
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Class',
|
||
|
||
[
|
||
'ephox.sugar.alien.Toggler',
|
||
'ephox.sugar.api.Attr'
|
||
],
|
||
|
||
function (Toggler, Attr) {
|
||
/*
|
||
* ClassList is IE10 minimum:
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
|
||
*
|
||
* Note that IE doesn't support the second argument to toggle (at all).
|
||
* If it did, the toggler could be better.
|
||
*/
|
||
|
||
var add = function (element, clazz) {
|
||
element.dom().classList.add(clazz);
|
||
};
|
||
|
||
var remove = function (element, clazz) {
|
||
var classList = element.dom().classList;
|
||
classList.remove(clazz);
|
||
|
||
// classList is a "live list", so this is up to date already
|
||
if (classList.length === 0) {
|
||
// No more classes left, remove the class attribute as well
|
||
Attr.remove(element, 'class');
|
||
}
|
||
};
|
||
|
||
var toggle = function (element, clazz) {
|
||
return element.dom().classList.toggle(clazz);
|
||
};
|
||
|
||
var toggler = function (element, clazz) {
|
||
var classList = element.dom().classList;
|
||
var off = function () {
|
||
classList.remove(clazz);
|
||
};
|
||
var on = function () {
|
||
classList.add(clazz);
|
||
};
|
||
return Toggler(off, on, has(element, clazz));
|
||
};
|
||
|
||
var has = function (element, clazz) {
|
||
var classList = element.dom().classList;
|
||
// Cereal has a nasty habit of calling this with a text node >.<
|
||
return classList !== undefined && classList.contains(clazz);
|
||
};
|
||
|
||
// set deleted, risks bad performance. Be deterministic.
|
||
|
||
return {
|
||
add: add,
|
||
remove: remove,
|
||
toggle: toggle,
|
||
toggler: toggler,
|
||
has: has
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.ui.UploadUi',
|
||
|
||
[
|
||
'ephox.salmon.api.Ephemera',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Ephemera, Class, Element, Insert, InsertAll, Remove, SelectorFind, Traverse) {
|
||
var removeUi = function (image) {
|
||
SelectorFind.ancestor(image, '.' + Ephemera.uploadContainer()).each(function (wrapper) {
|
||
var children = Traverse.children(wrapper);
|
||
InsertAll.before(wrapper, children);
|
||
Remove.remove(wrapper);
|
||
});
|
||
};
|
||
|
||
var addUi = function (image) {
|
||
var block = Element.fromTag('div');
|
||
Class.add(block, Ephemera.uploadContainer());
|
||
Insert.before(image, block);
|
||
Insert.append(block, image);
|
||
};
|
||
|
||
return {
|
||
removeUi: removeUi,
|
||
addUi: addUi
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.api.UploadUtils',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.hermes.api.ImageAsset',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.perhaps.Result',
|
||
'ephox.salmon.api.Ephemera',
|
||
'ephox.salmon.ui.UploadUi',
|
||
'ephox.scullion.ADT',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'global!console'
|
||
],
|
||
|
||
function (Arr, ImageAsset, Fun, Option, Result, UiEphemera, UploadUi, Adt, Struct, Attr, SelectorFind, console) {
|
||
var imageBlob = Struct.immutable('image', 'blobInfo');
|
||
|
||
var uploadResult = Adt.generate([
|
||
{ 'failure': [ 'error' ] },
|
||
{ 'success': [ 'result', 'images', 'blob' ] }
|
||
]);
|
||
|
||
/* Register any not already active id in the image tracker and return Some if it isn't already active */
|
||
var prepareForUpload = function (imageTracker, id, image) {
|
||
// If this ID is already active, don't actually trigger the upload a second time
|
||
var alreadyActive = imageTracker.isActive(id);
|
||
|
||
// Register the id so ImageTracker can find it
|
||
imageTracker.register(image, id);
|
||
|
||
// Add the spinner wrapper.
|
||
UploadUi.addUi(image);
|
||
|
||
// separate the actual upload call so we don't have the img element in closure
|
||
return !alreadyActive ? Option.some(id) : Option.none();
|
||
};
|
||
|
||
/* With each uploaded image, remove the uploading UI, update its src, and remove from the blob cache.
|
||
* Return the blob info identified by the id
|
||
*/
|
||
var updateImages = function (blobCache, images, id, result) {
|
||
Arr.each(images, function (img) {
|
||
Attr.set(img, 'src', result.location);
|
||
Attr.remove(img, UiEphemera.blobId());
|
||
});
|
||
|
||
return removeFromCache(blobCache, id, images);
|
||
};
|
||
|
||
/* Upload a particular image, finding it afterwards and updating its source */
|
||
var handleUpload = function (uploadManager, imageTracker, blobCache, container, id, blob, callback) {
|
||
var internalError = function () {
|
||
console.error('Internal error with blob cache', id);
|
||
// anything over 500 is a generic error
|
||
callback(uploadResult.failure({status: Fun.constant(666)}));
|
||
};
|
||
|
||
uploadManager.upload(blob, id, function (response) {
|
||
var freshImgs = imageTracker.findById(container, id);
|
||
|
||
// remove the image UI no matter what happened
|
||
Arr.each(freshImgs, UploadUi.removeUi);
|
||
|
||
response.fold(function (err) {
|
||
callback(uploadResult.failure(err));
|
||
}, function (result) {
|
||
updateImages(blobCache, freshImgs, id, result).fold(internalError, function (blobInfo) {
|
||
callback(uploadResult.success(result, freshImgs, blobInfo));
|
||
});
|
||
});
|
||
|
||
imageTracker.report(id, freshImgs, response.isValue());
|
||
});
|
||
};
|
||
|
||
var addToCache = function (blobCache, id, blob, objurl, data, image) {
|
||
var blobInfo = blobCache.lookupByData(data.result).getOrThunk(function () { return blobCache.add(id, blob, objurl, data); });
|
||
Attr.set(image, UiEphemera.blobId(), blobInfo.id());
|
||
return Result.value(imageBlob(image, blobInfo));
|
||
};
|
||
|
||
|
||
var findInCache = function (blobCache, image) {
|
||
var id = Attr.get(image, UiEphemera.blobId());
|
||
return blobCache.get(id).fold(function () {
|
||
return Result.error(id);
|
||
}, function (blobInfo) {
|
||
return Result.value(imageBlob(image, blobInfo));
|
||
});
|
||
};
|
||
|
||
var removeFromCache = function (blobCache, id, images) {
|
||
return blobCache.get(id).fold(function () {
|
||
return Result.error('Internal error with blob cache');
|
||
}, function (blobInfo) {
|
||
blobCache.remove(id);
|
||
return Result.value(blobInfo);
|
||
});
|
||
};
|
||
|
||
/* Find all of the assets in the container, and return the (blobInfo, img) pairs */
|
||
var registerAssets = function (blobCache, container, assets) {
|
||
return Arr.bind(assets, function (asset) {
|
||
return ImageAsset.cata(asset, function (id, blob, objurl, data) {
|
||
var freshImg = SelectorFind.descendant(container, 'img[src="' + objurl + '"]');
|
||
return freshImg.fold(function () {
|
||
return [ Result.error('Image that was just inserted could not be found: ' + objurl) ];
|
||
}, function (img) {
|
||
return [ addToCache(blobCache, id, blob, objurl, data, img) ];
|
||
});
|
||
}, Fun.constant([]));
|
||
});
|
||
};
|
||
|
||
var findBlobs = function(imageTracker, blobCache, container) {
|
||
var images = imageTracker.findAll(container);
|
||
if (imageTracker.inProgress()) return [];
|
||
return Arr.map(images, function (image) {
|
||
return findInCache(blobCache, image);
|
||
});
|
||
};
|
||
|
||
return {
|
||
prepareForUpload: prepareForUpload,
|
||
handleUpload: handleUpload,
|
||
registerAssets: registerAssets,
|
||
findBlobs: findBlobs
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.FormData',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE10 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||
*/
|
||
return function () {
|
||
var f = Global.getOrDie('FormData');
|
||
return new f();
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!Math", [], function () { return Math; });
|
||
ephox.bolt.module.api.define("global!isFinite", [], function () { return isFinite; });
|
||
ephox.bolt.module.api.define("global!isNaN", [], function () { return isNaN; });
|
||
ephox.bolt.module.api.define("global!parseFloat", [], function () { return parseFloat; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.violin.util.Validate',
|
||
|
||
[
|
||
'global!Math',
|
||
'global!isFinite',
|
||
'global!isNaN',
|
||
'global!parseFloat'
|
||
],
|
||
|
||
function(Math, isFinite, isNaN, parseFloat) {
|
||
var vType = function(expectedType) {
|
||
return function(name, value) {
|
||
var t = typeof value;
|
||
if (t !== expectedType) throw name + ' was not a ' + expectedType + '. Was: ' + value + ' (' + t + ')';
|
||
};
|
||
};
|
||
|
||
var vString = vType('string');
|
||
|
||
var vChar = function(name, value) {
|
||
vString(name, value);
|
||
var length = value.length;
|
||
if (length !== 1) throw name + ' was not a single char. Was: ' + value;
|
||
};
|
||
|
||
var vNumber = vType('number');
|
||
|
||
var vInt = function(name, value) {
|
||
vNumber(name, value);
|
||
if (value !== Math.abs(value)) throw name + ' was not an integer. Was: ' + value;
|
||
};
|
||
|
||
var pNum = function(value) {
|
||
return !isNaN(parseFloat(value)) && isFinite(value);
|
||
};
|
||
|
||
var vNat = function(name, value) {
|
||
vInt(name, value);
|
||
if (value < 0) throw name + ' was not a natural number. Was: ' + value;
|
||
};
|
||
|
||
return {
|
||
vString: vString,
|
||
vChar: vChar,
|
||
vInt: vInt,
|
||
vNat: vNat,
|
||
pNum: pNum
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
/**
|
||
* "violin" - stringed instrument, or rather, an instrument for dealing with strings.
|
||
*/
|
||
define(
|
||
"ephox.violin.Strings",
|
||
|
||
[
|
||
'ephox.violin.util.Validate'
|
||
],
|
||
|
||
function (Validate) {
|
||
//common method
|
||
var checkRange = function(str, substr, start) {
|
||
if (substr === "") return true;
|
||
if (str.length < substr.length) return false;
|
||
var x = str.substr(start, start + substr.length);
|
||
return x === substr;
|
||
};
|
||
|
||
/** Given a string and object, perform template-replacements on the string, as specified by the object.
|
||
* Any template fields of the form ${name} are replaced by the string or number specified as obj["name"]
|
||
* Based on Douglas Crockford's 'supplant' method for template-replace of strings. Uses different template format.
|
||
*/
|
||
var supplant = function(str, obj) {
|
||
var isStringOrNumber = function(a) {
|
||
var t = typeof a;
|
||
return t === "string" || t === "number";
|
||
};
|
||
|
||
return str.replace(/\${([^{}]*)}/g,
|
||
function (a, b) {
|
||
var value = obj[b];
|
||
return isStringOrNumber(value) ? value : a;
|
||
}
|
||
);
|
||
};
|
||
|
||
var ignoringCase = function(fn) {
|
||
var map = function(a, fn) {
|
||
var r = [];
|
||
for (var i = 0; i < a.length; i++) r.push(fn(a[i]));
|
||
return r;
|
||
};
|
||
|
||
return function() {
|
||
var args = map(arguments, function(x) {
|
||
return typeof x === "string" ? x.toLowerCase() : x;
|
||
});
|
||
return fn.apply(this, args);
|
||
};
|
||
};
|
||
|
||
/** Does 'str' start with 'prefix'?
|
||
* Note: all strings start with the empty string.
|
||
* More formally, for all strings x, startsWith(x, "").
|
||
* This is so that for all strings x and y, startsWith(y + x, y)
|
||
*/
|
||
var startsWith = function(str, prefix) {
|
||
return checkRange(str, prefix, 0);
|
||
};
|
||
|
||
var startsWithIgnoringCase = /* str, prefix */ ignoringCase(startsWith);
|
||
|
||
/** Does 'str' end with 'suffix'?
|
||
* Note: all strings end with the empty string.
|
||
* More formally, for all strings x, endsWith(x, "").
|
||
* This is so that for all strings x and y, endsWith(x + y, y)
|
||
*/
|
||
var endsWith = function(str, suffix) {
|
||
return checkRange(str, suffix, str.length - suffix.length);
|
||
};
|
||
|
||
var endsWithIgnoringCase = /* str, suffix */ ignoringCase(endsWith);
|
||
|
||
/** Return the first 'count' letters from 'str'.
|
||
* e.g. first("abcde", 2) === "ab"
|
||
*/
|
||
var first = function(str, count) {
|
||
return str.substr(0, count);
|
||
};
|
||
|
||
/** Return the last 'count' letters from 'str'.
|
||
* e.g. last("abcde", 2) === "de"
|
||
*/
|
||
var last = function(str, count) {
|
||
return str.substr(str.length - count, str.length);
|
||
};
|
||
|
||
var removeAppendage = function(checkFn, chopFn) {
|
||
return function(str, appendage) {
|
||
return checkFn(str, appendage) ? chopFn(str, str.length - appendage.length) : str;
|
||
};
|
||
};
|
||
|
||
var removeLeading = /* str, prefix */ removeAppendage(startsWith, last);
|
||
var removeTrailing = /* str, suffix */ removeAppendage(endsWith, first);
|
||
|
||
var append = function(a, b) {
|
||
return a + b;
|
||
};
|
||
|
||
var prepend = function(a, b) {
|
||
return b + a;
|
||
};
|
||
|
||
var ensureAppendage = function(checkFn, concatter) {
|
||
return function(str, appendage) {
|
||
return checkFn(str, appendage) ? str : concatter(str, appendage);
|
||
};
|
||
};
|
||
|
||
var ensureLeading = /* str, prefix */ ensureAppendage(startsWith, prepend);
|
||
var ensureTrailing = /* str, suffix */ ensureAppendage(endsWith, append);
|
||
|
||
/** removes all leading and trailing spaces */
|
||
var trim = function(str) {
|
||
return str.replace(/^\s+|\s+$/g, '');
|
||
};
|
||
|
||
var lTrim = function(str) {
|
||
return str.replace(/^\s+/g, '');
|
||
};
|
||
|
||
var rTrim = function(str) {
|
||
return str.replace(/\s+$/g, '');
|
||
};
|
||
|
||
/** Does 'str' contain 'substr'?
|
||
* Note: all strings contain the empty string.
|
||
*/
|
||
var contains = function(str, substr) {
|
||
return str.indexOf(substr) != -1;
|
||
};
|
||
|
||
var containsIgnoringCase = /* str, substr */ ignoringCase(contains);
|
||
|
||
var htmlEncodeDoubleQuotes = function(str) {
|
||
return str.replace(/\"/gm, """);
|
||
};
|
||
|
||
var equals = function(a, b) {
|
||
return a === b;
|
||
};
|
||
var equalsIgnoringCase = /* a, b */ ignoringCase(equals);
|
||
|
||
var head = function(str) {
|
||
if (str === "") throw "head on empty string";
|
||
return str.substr(0, 1);
|
||
};
|
||
|
||
var toe = function(str) {
|
||
if (str === "") throw "toe on empty string";
|
||
return str.substr(str.length - 1, str.length);
|
||
};
|
||
|
||
var tail = function(str) {
|
||
if (str === "") throw "tail on empty string";
|
||
return str.substr(1, str.length - 1);
|
||
};
|
||
|
||
var torso = function(str) {
|
||
if (str === "") throw "torso on empty string";
|
||
return str.substr(0, str.length - 1);
|
||
};
|
||
|
||
var capitalize = function(str) {
|
||
if (str === "") return str;
|
||
return head(str).toUpperCase() + tail(str);
|
||
};
|
||
|
||
var repeat = function(str, num) {
|
||
Validate.vString('str', str);
|
||
Validate.vNat('num', num);
|
||
var r = '';
|
||
for (var i = 0; i < num; i++) {
|
||
r += str;
|
||
}
|
||
return r;
|
||
};
|
||
|
||
var pad = function(combiner) {
|
||
return function(str, c, width) {
|
||
Validate.vString('str', str);
|
||
Validate.vChar('c', c);
|
||
Validate.vNat('width', width);
|
||
var l = str.length;
|
||
return l >= width ? str : combiner(str, repeat(c, width - l));
|
||
};
|
||
};
|
||
|
||
var padLeft = pad(function(s, padding) { return padding + s; });
|
||
var padRight = pad(function(s, padding) { return s + padding; });
|
||
|
||
return {
|
||
supplant: supplant,
|
||
startsWith: startsWith,
|
||
startsWithIgnoringCase: startsWithIgnoringCase,
|
||
endsWith: endsWith,
|
||
endsWithIgnoringCase: endsWithIgnoringCase,
|
||
first: first,
|
||
last: last,
|
||
removeLeading: removeLeading,
|
||
removeTrailing: removeTrailing,
|
||
ensureLeading: ensureLeading,
|
||
ensureTrailing: ensureTrailing,
|
||
trim: trim,
|
||
lTrim: lTrim,
|
||
rTrim: rTrim,
|
||
contains: contains,
|
||
containsIgnoringCase: containsIgnoringCase,
|
||
htmlEncodeDoubleQuotes: htmlEncodeDoubleQuotes,
|
||
equals: equals,
|
||
equalsIgnoringCase: equalsIgnoringCase,
|
||
head: head,
|
||
repead: repeat,
|
||
padLeft: padLeft,
|
||
padRight: padRight,
|
||
toe: toe,
|
||
tail: tail,
|
||
torso: torso,
|
||
capitalize: capitalize
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.services.UploadCommon',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.numerosity.api.FormData',
|
||
'ephox.scullion.Struct',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Type, Arr, FormData, Struct, Strings) {
|
||
var failureObject = Struct.immutable('message', 'status', 'contents');
|
||
|
||
var known = [ 'jpg', 'png', 'gif', 'jpeg' ]; // I welcome more suggestions
|
||
|
||
var buildFilename = function (file, identifier) {
|
||
if (Type.isString(file.type) && Strings.startsWith(file.type, 'image/')) {
|
||
var filetype = file.type.substr('image/'.length);
|
||
|
||
// If it's a known extension, use it, otherwise just don't provide an extension
|
||
return Arr.contains(known, filetype) ? identifier + '.' + filetype : identifier;
|
||
} else {
|
||
// things that aren't image/xxx can just have the default filename with no extension
|
||
return identifier;
|
||
}
|
||
};
|
||
|
||
var getFilename = function (file, identifier) {
|
||
// file.name is the default, but if it's a blob the default name is 'blob'
|
||
// TBIO-3151: On IE11 internet sites the filename ends in '.tmp' and we don't want to upload that.
|
||
var useFilename = Type.isString(file.name) && !Strings.endsWith(file.name, '.tmp');
|
||
return useFilename ? file.name : buildFilename(file, identifier);
|
||
};
|
||
|
||
var buildExtra = function (fieldName, file, filename) {
|
||
var formData = FormData();
|
||
formData.append(fieldName, file, filename);
|
||
|
||
return {
|
||
data: formData,
|
||
// override Jax, which sets this to application/json (triggering pre-flight)
|
||
contentType: false,
|
||
// stop JQuery processing the data
|
||
processData: false
|
||
};
|
||
};
|
||
|
||
return {
|
||
failureObject: failureObject,
|
||
getFilename: getFilename,
|
||
buildExtra: buildExtra
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.XMLHttpRequest',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE8 and above per
|
||
* https://developer.mozilla.org/en/docs/XMLHttpRequest
|
||
*/
|
||
return function () {
|
||
var f = Global.getOrDie('XMLHttpRequest');
|
||
return new f();
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.jax.base.Ajax',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Obj',
|
||
'ephox.highway.Merger',
|
||
'ephox.numerosity.api.XMLHttpRequest',
|
||
'ephox.perhaps.Result',
|
||
'ephox.violin.Strings',
|
||
'global!console'
|
||
],
|
||
|
||
function (Type, Obj, Merger, XMLHttpRequest, Result, Strings, console) {
|
||
var accepts = {
|
||
'*': '*/*',
|
||
text: 'text/plain',
|
||
html: 'text/html',
|
||
xml: 'application/xml, text/xml',
|
||
json: 'application/json, text/javascript'
|
||
};
|
||
|
||
/*
|
||
*
|
||
* This could be done better, but that would involve an API change. Or move some of it to numerosity.
|
||
* Start by replicating JQuery API, and unravel later.
|
||
*
|
||
*/
|
||
|
||
var ajax = function (url, success, error, extra) {
|
||
var base = {
|
||
url: url,
|
||
contentType: 'application/json',
|
||
processData: false,
|
||
type: 'GET'
|
||
};
|
||
|
||
var options = Merger.merge(base, extra);
|
||
// This would be nice, but IE doesn't support responseType 'json' - might be an excuse to bring in platform detection
|
||
/*
|
||
if (Type.isString(options.responseType))
|
||
request.responseType = options.responseType;
|
||
*/
|
||
|
||
var request = XMLHttpRequest();
|
||
|
||
request.open(options.type.toUpperCase(), options.url, true); // enforced async! enforced type as String!
|
||
|
||
if (Type.isString(options.contentType)) { // set to string here, but overridden for form posting by TBIO-1255
|
||
request.setRequestHeader('Content-Type', options.contentType);
|
||
}
|
||
|
||
// I'm not 100% sure why JQuery does this, but eh why not
|
||
var odt = options.dataType;
|
||
var a = Type.isString(odt) && odt !== '*' ?
|
||
accepts[odt] + ', ' + accepts['*'] + '; q=0.01' :
|
||
accepts['*'];
|
||
|
||
request.setRequestHeader('Accept', a);
|
||
|
||
if (options.xhrFields !== undefined && options.xhrFields.withCredentials === true) {
|
||
request.withCredentials = true; // IE10 minimum
|
||
}
|
||
|
||
// Do this last, so the extra headers can override the above
|
||
if (Type.isObject(options.headers)) Obj.each(options.headers, function (v, k) {
|
||
if (!Type.isString(k) && !Type.isString(v)) console.error('Request header data was not a string: ', k ,' -> ', v);
|
||
else request.setRequestHeader(k, v);
|
||
});
|
||
|
||
var onSuccess = function (data, status, jqxhr) {
|
||
success(data);
|
||
};
|
||
|
||
var onError = function (jqxhr) {
|
||
error('Could not load url "' + url + '": ' + jqxhr.status + ' ' + jqxhr.statusText, jqxhr.status, jqxhr.responseText);
|
||
};
|
||
|
||
var parseJson = function (jqxhr) {
|
||
// If we do this inside the try block, an error in the success callback will be caught
|
||
// by the "response was not JSON" catch block.
|
||
try {
|
||
return Result.value(JSON.parse(jqxhr.response));
|
||
} catch (e) {
|
||
return Result.error({
|
||
status: jqxhr.status,
|
||
statusText: 'Response was not JSON',
|
||
responseText: jqxhr.responseText
|
||
});
|
||
}
|
||
};
|
||
|
||
var validateData = function (request) {
|
||
var data = options.dataType === 'json' ? parseJson(request) : Result.value(request.response);
|
||
data.fold(onError, function (xhrData) {
|
||
onSuccess(xhrData, request.statusText, request);
|
||
});
|
||
};
|
||
|
||
var onLoad = function () {
|
||
if (request.status === 0) {
|
||
// Local files and Cors errors return status 0.
|
||
// The only way we can decifer a local request is request url starts with 'file:' and allow local files to succeed.
|
||
if (Strings.startsWith(options.url, 'file:')) validateData(request);
|
||
else error('Unknown HTTP error (possible cross-domain request)', request.status, request.responseText);
|
||
|
||
} else if ( request.status < 100 || request.status >= 400) {
|
||
// Technically only network errors trigger onerror; HTTP errors trigger onload.
|
||
// In practice only IE does this. But we need to handle it.
|
||
onError(request);
|
||
} else {
|
||
validateData(request);
|
||
}
|
||
};
|
||
|
||
request.onerror = onError;
|
||
request.onload = onLoad;
|
||
// I suspect refactoring this at all will break stuff
|
||
if (options.data === undefined) {
|
||
request.send();
|
||
} else {
|
||
request.send(options.data);
|
||
}
|
||
};
|
||
|
||
return {
|
||
ajax: ajax
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.JSON',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE8 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
|
||
*/
|
||
var json = function () {
|
||
return Global.getOrDie('JSON');
|
||
};
|
||
|
||
var parse = function (obj) {
|
||
return json().parse(obj);
|
||
};
|
||
|
||
var stringify = function (obj) {
|
||
return json().stringify(obj);
|
||
};
|
||
|
||
return {
|
||
parse: parse,
|
||
stringify: stringify
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.jax.plain.Ajax',
|
||
|
||
[
|
||
'ephox.highway.Merger',
|
||
'ephox.jax.base.Ajax',
|
||
'ephox.numerosity.api.JSON'
|
||
],
|
||
|
||
function (Merger, Ajax, JSON) {
|
||
var get = function (url, success, error, extra) {
|
||
Ajax.ajax(url, success, error, Merger.merge({
|
||
dataType: 'text',
|
||
type: 'GET'
|
||
}, extra));
|
||
};
|
||
|
||
var post = function (url, data, success, error, extra) {
|
||
Ajax.ajax(url, success, error, Merger.merge({
|
||
dataType: 'text',
|
||
data: JSON.stringify(data),
|
||
type: 'POST'
|
||
}, extra));
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
post: post
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
// An implementation of the algorithm specified in section 5.3 of RFC 3986.
|
||
// See http://tools.ietf.org/html/rfc3986#section-5.3
|
||
define(
|
||
'ephox.yuri.resolve.Recompose',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var recompose = function (transformed) {
|
||
var result = '';
|
||
|
||
if (transformed.protocol !== '') {
|
||
result += transformed.protocol;
|
||
result += ':';
|
||
}
|
||
|
||
if (transformed.authority !== '') {
|
||
result += '//';
|
||
result += transformed.authority;
|
||
}
|
||
|
||
result += transformed.path;
|
||
|
||
if (transformed.query !== '') {
|
||
result += '?';
|
||
result += transformed.query;
|
||
}
|
||
|
||
if (transformed.anchor !== '') {
|
||
result += '#';
|
||
result += transformed.anchor;
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
return {
|
||
recompose: recompose
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
// Based on parseUri 1.2.2
|
||
// (c) Steven Levithan <stevenlevithan.com>
|
||
// MIT License
|
||
// http://blog.stevenlevithan.com/archives/parseuri
|
||
//
|
||
// Forked by Ephox on 2011-02-07. Source modified.
|
||
define(
|
||
'ephox.yuri.api.Parser',
|
||
|
||
[
|
||
'ephox.highway.Merger'
|
||
],
|
||
|
||
function (Merger) {
|
||
var defaultOptions = {
|
||
strictMode: false,
|
||
key: [ 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' ],
|
||
q: {
|
||
name: 'queryKey',
|
||
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
||
},
|
||
parser: {
|
||
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
||
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||
}
|
||
};
|
||
|
||
var parseUri = function (str, options) {
|
||
var o = options,
|
||
m = o.parser[o.strictMode ? 'strict' : 'loose'].exec(str),
|
||
uri = {},
|
||
i = 14;
|
||
|
||
while (i--) uri[o.key[i]] = m[i] || '';
|
||
|
||
uri[o.q.name] = {};
|
||
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
|
||
if ($1) uri[o.q.name][$1] = $2;
|
||
});
|
||
|
||
return uri;
|
||
};
|
||
|
||
var parse = function (str, options) {
|
||
var augmentedOptions = Merger.merge(defaultOptions, options);
|
||
return parseUri(str, augmentedOptions);
|
||
};
|
||
|
||
return {
|
||
parse: parse
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
// An implementation of the algorithm specified in section 5.2.4 of RFC 3986.
|
||
// See http://tools.ietf.org/html/rfc3986#section-5.2.4
|
||
define(
|
||
'ephox.yuri.normalize.Dots',
|
||
|
||
[
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Strings) {
|
||
var removeLastSegment = function (path) {
|
||
return Strings.removeTrailing(path, lastSegment(path));
|
||
};
|
||
|
||
var firstSegment = function (path) {
|
||
return path.match(/(^\/?.*?)(\/|$)/)[1];
|
||
};
|
||
|
||
var lastSegment = function (path) {
|
||
return path.substring(path.lastIndexOf('/'));
|
||
};
|
||
|
||
var remove = function (path) {
|
||
// 1.
|
||
var input = path;
|
||
var output = '';
|
||
|
||
// 2.
|
||
while (input !== '') {
|
||
// 2. A
|
||
if (Strings.startsWith(input, '../')) {
|
||
input = Strings.removeLeading(input, '../');
|
||
} else if (Strings.startsWith(input, './')) {
|
||
input = Strings.removeLeading(input, './');
|
||
// 2. B
|
||
} else if (Strings.startsWith(input, '/./')) {
|
||
input = '/' + Strings.removeLeading(input, '/./');
|
||
} else if (input === '/.') {
|
||
input = '/';
|
||
// 2. C
|
||
} else if (Strings.startsWith(input, '/../')) {
|
||
input = '/' + Strings.removeLeading(input, '/../');
|
||
output = removeLastSegment(output);
|
||
} else if (input === '/..') {
|
||
input = '/';
|
||
output = removeLastSegment(output);
|
||
// 2. D
|
||
} else if (input === '.' || input === '..') {
|
||
input = '';
|
||
// 2. E
|
||
} else {
|
||
var first = firstSegment(input);
|
||
input = Strings.removeLeading(input, first);
|
||
output += first;
|
||
}
|
||
}
|
||
|
||
// 3.
|
||
return output;
|
||
};
|
||
|
||
return {
|
||
remove: remove
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
// An implementation of the algorithm specified in section 5.2.3 of RFC 3986.
|
||
// See http://tools.ietf.org/html/rfc3986#section-5.2.3
|
||
define(
|
||
'ephox.yuri.resolve.Merge',
|
||
|
||
[
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Strings) {
|
||
var merge = function (base, rel, baseAuthority) {
|
||
if (baseAuthority !== '' && base === '')
|
||
return '/' + rel;
|
||
|
||
var last = base.substring(base.lastIndexOf('/') + 1);
|
||
return Strings.removeTrailing(base, last) + rel;
|
||
};
|
||
|
||
return {
|
||
merge: merge
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
// An implementation of the algorithm specified in section 5.2.2 of RFC 3986.
|
||
// See http://tools.ietf.org/html/rfc3986#section-5.2.2
|
||
define(
|
||
'ephox.yuri.resolve.Transform',
|
||
|
||
[
|
||
'ephox.violin.Strings',
|
||
'ephox.yuri.api.Parser',
|
||
'ephox.yuri.normalize.Dots',
|
||
'ephox.yuri.resolve.Merge'
|
||
],
|
||
|
||
function (Strings, Parser, Dots, Merge) {
|
||
var transform = function (base, relative) {
|
||
var options = { strictMode: true };
|
||
|
||
var b = Parser.parse(base, options);
|
||
var rel = Parser.parse(relative, options);
|
||
|
||
var ret = {};
|
||
|
||
if (rel.protocol !== '') {
|
||
ret.protocol = rel.protocol;
|
||
ret.authority = rel.authority;
|
||
ret.path = Dots.remove(rel.path);
|
||
ret.query = rel.query;
|
||
} else {
|
||
if (rel.authority !== '') {
|
||
ret.authority = rel.authority;
|
||
ret.path = Dots.remove(rel.path);
|
||
ret.query = rel.query;
|
||
} else {
|
||
if (rel.path === '') {
|
||
ret.path = b.path;
|
||
if (rel.query !== '') {
|
||
ret.query = rel.query;
|
||
} else {
|
||
ret.query = b.query;
|
||
}
|
||
} else {
|
||
if (Strings.startsWith(rel.path, '/')) {
|
||
ret.path = Dots.remove(rel.path);
|
||
} else {
|
||
ret.path = Merge.merge(b.path, rel.path, base.authority);
|
||
ret.path = Dots.remove(ret.path);
|
||
}
|
||
ret.query = rel.query;
|
||
}
|
||
ret.authority = b.authority;
|
||
}
|
||
ret.protocol = b.protocol;
|
||
}
|
||
|
||
ret.anchor = rel.anchor;
|
||
|
||
return ret;
|
||
};
|
||
|
||
return {
|
||
transform: transform
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.yuri.api.Resolver',
|
||
|
||
[
|
||
'ephox.yuri.resolve.Recompose',
|
||
'ephox.yuri.resolve.Transform'
|
||
],
|
||
|
||
function (Recompose, Transform) {
|
||
var resolve = function (base, relative) {
|
||
var transformed = Transform.transform(base, relative);
|
||
return Recompose.recompose(transformed);
|
||
};
|
||
|
||
return {
|
||
resolve: resolve
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.services.UploadDirect',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.highway.Merger',
|
||
'ephox.jax.plain.Ajax',
|
||
'ephox.numerosity.api.JSON',
|
||
'ephox.perhaps.Result',
|
||
'ephox.salmon.services.UploadCommon',
|
||
'ephox.violin.Strings',
|
||
'ephox.yuri.api.Resolver'
|
||
],
|
||
|
||
function (Type, Merger, Ajax, JSON, Result, UploadCommon, Strings, Resolver) {
|
||
return function (settings) {
|
||
|
||
/*
|
||
* If a base is not provided by the config, use the directory where the
|
||
* POST acceptor lives
|
||
*/
|
||
var resolveBase = function () {
|
||
var fullBase = settings.url;
|
||
// The base is the string up to the last slash, unless that slash is at the start of the string.
|
||
var lastSlash = fullBase.lastIndexOf('/');
|
||
var base = lastSlash > 0 ? fullBase.substr(0, lastSlash) : fullBase;
|
||
|
||
var _responseBase = settings.basePath === undefined ? base : settings.basePath;
|
||
return Strings.endsWith(_responseBase, '/') ? _responseBase : _responseBase + '/';
|
||
};
|
||
|
||
// pre-calculate, don't need to do this on every upload
|
||
var responseBase = resolveBase();
|
||
|
||
|
||
/*
|
||
* ELJ upload handler result location consists of:
|
||
*
|
||
* - If the response looks like a URL, use that as the filename
|
||
* - Otherwise use the source filename
|
||
* - take the filename, and resolve it relative to the base provided by the config
|
||
*/
|
||
var calculateLocation = function (response, filename) {
|
||
var splits = response.split(/\s+/);
|
||
|
||
// If the response text is a simple string with no whitespace, it's a URL
|
||
var serverFilename = (splits.length === 1 && splits[0] !== '') ? splits[0] : filename;
|
||
|
||
// Resolve the settings base url to the response
|
||
return Resolver.resolve(responseBase, serverFilename);
|
||
};
|
||
|
||
// ELJ style direct uploader, form field name 'image'
|
||
var upload = function (blobInfo, identifier, callback) {
|
||
var file = blobInfo.blob();
|
||
var failure = function (message, status, contents) {
|
||
callback(Result.error(UploadCommon.failureObject(message, status, contents)));
|
||
};
|
||
|
||
var filename = UploadCommon.getFilename(file, identifier);
|
||
|
||
var useCreds = settings.credentials !== true ? {} : {
|
||
xhrFields: {
|
||
withCredentials: true
|
||
}
|
||
};
|
||
|
||
var extra = Merger.merge(useCreds, UploadCommon.buildExtra('image', file, filename));
|
||
|
||
var success = function (_response) {
|
||
var response;
|
||
// This is difficult to refactor; response is either a json parse failure, a json object, or a string.
|
||
try {
|
||
var json = JSON.parse(_response);
|
||
// we have JSON, make sure it's valid
|
||
if (!Type.isString(json.location)) {
|
||
failure('JSON response did not contain a string location', 500, _response);
|
||
return;
|
||
} else {
|
||
// we now support adding a base URL ELJ style, so we have to unwrap the JSON into a string
|
||
response = json.location;
|
||
}
|
||
} catch (e) {
|
||
// not JSON, assume it's an ELJ style url response
|
||
response = _response;
|
||
}
|
||
|
||
var loc = calculateLocation(response, filename);
|
||
|
||
callback(Result.value({
|
||
// convert ELJ style response to TBIO expected response
|
||
location: loc
|
||
}));
|
||
};
|
||
|
||
Ajax.post(settings.url, {}, success, failure, extra);
|
||
};
|
||
|
||
return {
|
||
upload: upload
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!setTimeout", [], function () { return setTimeout; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.services.UploadFunction',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.perhaps.Result',
|
||
'ephox.salmon.services.UploadCommon',
|
||
'ephox.scullion.Struct',
|
||
'global!console',
|
||
'global!setTimeout'
|
||
],
|
||
|
||
function (Type, Result, UploadCommon, Struct, console, setTimeout) {
|
||
var imageObjectApi = Struct.immutable('id', 'filename', 'blob', 'base64');
|
||
|
||
// Customer handler function
|
||
return function (handler) {
|
||
|
||
var upload = function (blobInfo, identifier, callback) {
|
||
var failure = function (message) {
|
||
// SimpleError handles the variety of callback message types, so just pass it straight through
|
||
callback(Result.error(message));
|
||
};
|
||
|
||
var success = function (result) {
|
||
if (!Type.isString(result)) {
|
||
console.error('Image upload result was not a string');
|
||
failure('');
|
||
} else {
|
||
callback(Result.value({
|
||
// convert to TBIO expected response
|
||
location: result
|
||
}));
|
||
}
|
||
};
|
||
|
||
var filename = UploadCommon.getFilename(blobInfo.blob(), identifier);
|
||
|
||
var api = imageObjectApi(identifier, filename, blobInfo.blob(), blobInfo.data().result);
|
||
|
||
// wrap the custom handler in a setTimeout so that if it throws an error, that doesn't break the core editor
|
||
setTimeout(function () {
|
||
handler(api, success, failure);
|
||
}, 0);
|
||
};
|
||
|
||
return {
|
||
upload: upload
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.salmon.api.Uploaders',
|
||
|
||
[
|
||
'ephox.salmon.services.UploadCommon',
|
||
'ephox.salmon.services.UploadDirect',
|
||
'ephox.salmon.services.UploadFunction'
|
||
],
|
||
|
||
function (UploadCommon, UploadDirect, UploadFunction) {
|
||
var direct = function (settings) {
|
||
return UploadDirect(settings);
|
||
};
|
||
|
||
var custom = function (handler) {
|
||
return UploadFunction(handler);
|
||
};
|
||
|
||
var failureObject = function (message, status, contents) {
|
||
return UploadCommon.failureObject(message, status, contents);
|
||
};
|
||
|
||
var getFilename = function (file, identifier) {
|
||
return UploadCommon.getFilename(file, identifier);
|
||
};
|
||
|
||
var buildExtra = function (fieldName, file, filename) {
|
||
return UploadCommon.buildExtra(fieldName, file, filename);
|
||
};
|
||
|
||
return {
|
||
direct: direct,
|
||
custom: custom,
|
||
failureObject: failureObject,
|
||
getFilename: getFilename,
|
||
buildExtra: buildExtra
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.imageupload.EphoxUploader',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.powerpaste.imageupload.TinyUploader',
|
||
'ephox.powerpaste.imageupload.UploadError',
|
||
'ephox.powerpaste.tinymce.UndoRewriter',
|
||
'ephox.salmon.api.BlobCache',
|
||
'ephox.salmon.api.ImageTracker',
|
||
'ephox.salmon.api.UploadUtils',
|
||
'ephox.salmon.api.Uploaders',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Arr, Fun, Option, TinyUploader, UploadError, UndoRewriter, BlobCache, ImageTracker, UploadUtils, Uploaders, Attr, Element) {
|
||
var enabled = function (editor, settings) {
|
||
// Use polish dependencies.
|
||
var blobCache = BlobCache();
|
||
var imageTracker = ImageTracker();
|
||
var errorHandler = UploadError();
|
||
var errors = UploadError(editor, settings.url);
|
||
|
||
// UploadDirect will need an error handler that is tiny specific.
|
||
var uploadManager = Uploaders.direct(settings);
|
||
|
||
var getBody = function () {
|
||
return Element.fromDom(editor.getBody());
|
||
};
|
||
|
||
var updateImage = function (result, images, b) {
|
||
Arr.each(images, function (img) {
|
||
Attr.set(img, 'data-mce-src', result.location);
|
||
});
|
||
|
||
// undo hack.
|
||
UndoRewriter.resrcHistory(editor, b, result);
|
||
};
|
||
|
||
imageTracker.events.complete.bind(function (event) {
|
||
//Other undo hack.
|
||
UndoRewriter.unwrapHistory(editor);
|
||
});
|
||
|
||
var uploadImage = function(id, blob, showError) {
|
||
UploadUtils.handleUpload(uploadManager, imageTracker, blobCache, getBody(), id, blob, function (upResult) {
|
||
upResult.fold(function (err) {
|
||
// show error dialog
|
||
showError(err);
|
||
}, updateImage);
|
||
});
|
||
};
|
||
|
||
var prepareForUpload = function (info, showError) {
|
||
UploadUtils.prepareForUpload(imageTracker, info.blobInfo().id(), info.image()).each(function (id) {
|
||
uploadImage(id, info.blobInfo(), showError);
|
||
});
|
||
};
|
||
|
||
var uploadAssets = function (assets) {
|
||
var showError = errors.instance();
|
||
var candidates = UploadUtils.registerAssets(blobCache, getBody(), assets);
|
||
Arr.each(candidates, function (candidate) {
|
||
candidate.fold(function (err) {
|
||
// a blob we do not know about.
|
||
console.error(err);
|
||
}, function(info) {
|
||
prepareForUpload(info, showError);
|
||
});
|
||
});
|
||
};
|
||
|
||
// Need to fill this in.
|
||
var reconstitute = function () {
|
||
var showError = errors.instance();
|
||
var imageBlobs = UploadUtils.findBlobs(imageTracker, blobCache, getBody());
|
||
Arr.each(imageBlobs, function (imageBlob) {
|
||
imageBlob.fold(function (id) {
|
||
// Report the failure.
|
||
imageTracker.report(id, Option.none(), false);
|
||
}, function(info) {
|
||
prepareForUpload(info, showError);
|
||
});
|
||
});
|
||
};
|
||
|
||
var uploadImages = function (assets) {
|
||
reconstitute();
|
||
|
||
uploadAssets(assets);
|
||
};
|
||
|
||
var getLocalURL = function (id, blob, objurl, data) {
|
||
return objurl;
|
||
};
|
||
|
||
return {
|
||
uploadImages: uploadImages,
|
||
prepareImages: Fun.noop, //Images are already in Ephox uploader's required format (blobs)
|
||
getLocalURL: getLocalURL
|
||
};
|
||
};
|
||
|
||
|
||
var disabled = function(editor) {
|
||
|
||
var tinyUploader = TinyUploader(editor);
|
||
|
||
return {
|
||
uploadImages: Fun.noop,
|
||
prepareImages: tinyUploader.prepareImages, //Convert images back to base64 so they aren't unusable
|
||
getLocalURL: tinyUploader.getLocalURL //As above
|
||
};
|
||
};
|
||
|
||
return function (editor, settings) {
|
||
return settings ? enabled(editor, settings) : disabled(editor);
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.imageupload.UploaderFactory',
|
||
|
||
[
|
||
'ephox.powerpaste.imageupload.EphoxUploader',
|
||
'ephox.powerpaste.imageupload.TinyUploader'
|
||
],
|
||
function (EphoxUploader, TinyUploader) {
|
||
return function (editor) {
|
||
//We'll only need to use ephox's uploader if Tiny's 4.0-4.1 and we have an images_upload_url setting
|
||
var ephoxUploadSettings = !editor.uploadImages && editor.settings.images_upload_url ?
|
||
{
|
||
url: editor.settings.images_upload_url,
|
||
basePath: editor.settings.images_upload_base_path,
|
||
credentials: editor.settings.images_upload_credentials
|
||
} : null;
|
||
return !editor.uploadImages ? EphoxUploader(editor, ephoxUploadSettings) : TinyUploader(editor);
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.tinymce.Util',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var bind = function(func, t) {
|
||
return function() {
|
||
return func.apply(t, arguments);
|
||
};
|
||
};
|
||
|
||
// Useful utilities that may exist in libraries but aren't as common.
|
||
// Currently we're providing our own implementation for these but want to track them.
|
||
var ephoxGetComputedStyle = function(node) {
|
||
if (node.ownerDocument.defaultView) {
|
||
return node.ownerDocument.defaultView.getComputedStyle(node, null);
|
||
}
|
||
return node.currentStyle || {};
|
||
};
|
||
|
||
var log = function(msg) {
|
||
if (typeof(console) !== 'undefined' && console.log) {
|
||
console.log(msg);
|
||
}
|
||
};
|
||
|
||
var compose = function(funs) {
|
||
var args = Array.prototype.slice.call(funs).reverse();
|
||
return function(input) {
|
||
var r = input;
|
||
for (var i = 0; i < args.length; i++) {
|
||
var fun = args[i];
|
||
r = fun(r);
|
||
}
|
||
return r;
|
||
};
|
||
};
|
||
|
||
var extend = function(obj) {
|
||
tinymce.each(Array.prototype.slice.call(arguments, 1), function(element){
|
||
for (var prop in element) {
|
||
obj[prop] = element[prop];
|
||
}
|
||
});
|
||
return obj;
|
||
};
|
||
|
||
return {
|
||
each: tinymce.each,
|
||
trim: tinymce.trim,
|
||
bind: bind,
|
||
extend: extend,
|
||
ephoxGetComputedStyle: ephoxGetComputedStyle,
|
||
log: log,
|
||
compose: compose
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
/**
|
||
* Source code in this file has been taken under a commercial license from tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js
|
||
* Copyright 2009, Moxiecode Systems AB
|
||
*/
|
||
define(
|
||
'ephox.powerpaste.legacy.tinymce.Clipboard',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Util) {
|
||
var each = tinymce.each;
|
||
// Note that there is only ever one instance of this module - it's not created specifically for each editor.
|
||
|
||
// Private function that does the actual work of grabbing the clipboard content.
|
||
// This can then be shared between the onpaste and onKeyDown listeners.
|
||
var grabContent = function(ed, callback, e) {
|
||
var n, or, rng, sel = ed.selection, dom = ed.dom, body = ed.getBody(), posY;
|
||
|
||
// Check if browser supports direct plaintext access
|
||
if (e.clipboardData && e.clipboardData.getData('text/html')) {
|
||
e.preventDefault();
|
||
var data = e.clipboardData.getData('text/html');
|
||
var matched = data.match(/<html[\s\S]+<\/html>/i);
|
||
// some browsers such as firefox don't wrap the content in a html tag
|
||
var content = matched === null ? data : matched[0];
|
||
return callback(content);
|
||
}
|
||
|
||
if (dom.get('_mcePaste'))
|
||
return;
|
||
|
||
// Create container to paste into
|
||
n = dom.add(body, 'div', {id : '_mcePaste', 'class' : 'mcePaste'}, '\uFEFF<br _mce_bogus="1">');
|
||
|
||
// If contentEditable mode we need to find out the position of the closest element
|
||
if (body != ed.getDoc().body)
|
||
posY = dom.getPos(ed.selection.getStart(), body).y;
|
||
else
|
||
posY = body.scrollTop;
|
||
|
||
// Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles
|
||
dom.setStyles(n, {
|
||
position : 'absolute',
|
||
left : -10000,
|
||
top : posY,
|
||
width : 1,
|
||
height : 1,
|
||
overflow : 'hidden'
|
||
});
|
||
|
||
if (tinymce.isIE) {
|
||
// Select the container
|
||
rng = dom.doc.body.createTextRange();
|
||
rng.moveToElementText(n);
|
||
rng.execCommand('Paste');
|
||
|
||
// Remove container
|
||
dom.remove(n);
|
||
|
||
// Check if the contents was changed, if it wasn't then clipboard extraction failed probably due
|
||
// to IE security settings so we pass the junk though better than nothing right
|
||
if (n.innerHTML === '\uFEFF') {
|
||
ed.execCommand('mcePasteWord');
|
||
e.preventDefault();
|
||
return;
|
||
}
|
||
|
||
// Process contents
|
||
callback(n.innerHTML);
|
||
|
||
// Block the real paste event
|
||
return tinymce.dom.Event.cancel(e);
|
||
} else {
|
||
var block = function(e) {
|
||
e.preventDefault();
|
||
};
|
||
|
||
// Block mousedown and click to prevent selection change
|
||
dom.bind(ed.getDoc(), 'mousedown', block);
|
||
dom.bind(ed.getDoc(), 'keydown', block);
|
||
|
||
// If pasting inside the same element and the contents is only one block
|
||
// remove the block and keep the text since Firefox will copy parts of pre and h1-h6 as a pre element
|
||
if (tinymce.isGecko) {
|
||
rng = ed.selection.getRng(true);
|
||
if (rng.startContainer == rng.endContainer && rng.startContainer.nodeType == 3) {
|
||
nodes = dom.select('p,h1,h2,h3,h4,h5,h6,pre', n);
|
||
|
||
if (nodes.length == 1)
|
||
dom.remove(nodes.reverse(), true);
|
||
}
|
||
}
|
||
|
||
or = ed.selection.getRng();
|
||
|
||
// Move caret into hidden div
|
||
n = n.firstChild;
|
||
rng = ed.getDoc().createRange();
|
||
rng.setStart(n, 0);
|
||
rng.setEnd(n, 1);
|
||
sel.setRng(rng);
|
||
|
||
// Wait a while and grab the pasted contents
|
||
window.setTimeout(function() {
|
||
var h = '', nl = dom.select('div.mcePaste');
|
||
|
||
// WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string
|
||
Util.each(nl, function(n) {
|
||
var child = n.firstChild;
|
||
|
||
// WebKit inserts a DIV container with lots of odd styles
|
||
if (child && child.nodeName == 'DIV' && child.style.marginTop && child.style.backgroundColor) {
|
||
dom.remove(child, 1);
|
||
}
|
||
|
||
// WebKit duplicates the divs so we need to remove them
|
||
Util.each(dom.select('div.mcePaste', n), function(n) {
|
||
dom.remove(n, 1);
|
||
});
|
||
|
||
// Remove apply style spans
|
||
Util.each(dom.select('span.Apple-style-span', n), function(n) {
|
||
dom.remove(n, 1);
|
||
});
|
||
|
||
// Remove bogus br elements
|
||
Util.each(dom.select('br[_mce_bogus]', n), function(n) {
|
||
dom.remove(n);
|
||
});
|
||
|
||
h += n.innerHTML;
|
||
});
|
||
|
||
// Remove the nodes
|
||
Util.each(nl, function(n) {
|
||
dom.remove(n);
|
||
});
|
||
|
||
// Restore the old selection
|
||
if (or)
|
||
sel.setRng(or);
|
||
|
||
callback(h);
|
||
|
||
// Unblock events ones we got the contents
|
||
dom.unbind(ed.getDoc(), 'mousedown', block);
|
||
dom.unbind(ed.getDoc(), 'keydown', block);
|
||
}, 0);
|
||
}
|
||
};
|
||
|
||
/** Creates the function to attach to the onpaste event so that the pasted content can be intercepted.
|
||
*
|
||
* The returned function should capture the pasted content and pass it as the argument to the provided callback function.
|
||
*
|
||
* @param ed the editor this function is for.
|
||
* @param callback the function to call with the clipboard content as the argument
|
||
*/
|
||
var getOnPasteFunction = function(ed, callback) {
|
||
return function(e) {
|
||
grabContent(ed, callback, e);
|
||
};
|
||
};
|
||
|
||
/** Creates the function to attach to the onKeyDown event so that the pasted content can be intercepted. If no onKeyDown function is required in the current browser
|
||
* this should return null.
|
||
*
|
||
* The returned function should capture the pasted content and pass it as the argument to the provided callback function.
|
||
*
|
||
* @param ed the editor this function is for.
|
||
* @param callback the function to call with the clipboard content as the argument
|
||
*/
|
||
var getOnKeyDownFunction = function(ed, callback) {
|
||
return function(e) {
|
||
// Is it's Opera or older FF use key handler
|
||
if (tinymce.isOpera || navigator.userAgent.indexOf('Firefox/2') > 0) {
|
||
if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
|
||
grabContent(ed, callback, e);
|
||
}
|
||
};
|
||
};
|
||
|
||
return {
|
||
getOnPasteFunction: getOnPasteFunction,
|
||
getOnKeyDownFunction: getOnKeyDownFunction
|
||
};
|
||
});
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.Insert',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
var insert = function(fragment, editor) {
|
||
var document = editor.getDoc(), marker, markerId = "ephoxInsertMarker", selection = editor.selection, dom = editor.dom;
|
||
selection.setContent('<span id="' + markerId + '"> </span>');
|
||
marker = dom.get(markerId);
|
||
var leadingText = document.createDocumentFragment();
|
||
while (fragment.firstChild && !dom.isBlock(fragment.firstChild)) {
|
||
leadingText.appendChild(fragment.firstChild);
|
||
}
|
||
var trailingText = document.createDocumentFragment();
|
||
while (fragment.lastChild && !dom.isBlock(fragment.lastChild)) {
|
||
trailingText.appendChild(fragment.lastChild);
|
||
}
|
||
|
||
marker.parentNode.insertBefore(leadingText, marker);
|
||
dom.insertAfter(trailingText, marker);
|
||
|
||
if (fragment.firstChild) {
|
||
if (dom.isBlock(fragment.firstChild)) {
|
||
while (!dom.isBlock(marker.parentNode) && marker.parentNode !== dom.getRoot()) {
|
||
marker = dom.split(marker.parentNode, marker);
|
||
}
|
||
if (!dom.is(marker.parentNode, 'td,th') && marker.parentNode !== dom.getRoot()) {
|
||
marker = dom.split(marker.parentNode, marker);
|
||
}
|
||
}
|
||
|
||
dom.replace(fragment, marker);
|
||
} else {
|
||
dom.remove(marker);
|
||
}
|
||
};
|
||
|
||
return {
|
||
insert: insert
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.tinymce.Settings',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Util) {
|
||
var settings_clean = {
|
||
strip_class_attributes: 'all',
|
||
retain_style_properties: 'none'
|
||
};
|
||
|
||
var settings_inline = {
|
||
strip_class_attributes: 'none',
|
||
retain_style_properties: 'valid'
|
||
};
|
||
|
||
var getImportSettings = function(pasteType, defaultSettings) {
|
||
if (pasteType && typeof(pasteType) != 'string') {
|
||
return pasteType;
|
||
}
|
||
|
||
switch (pasteType) {
|
||
case 'clean': return settings_clean;
|
||
case 'merge': return settings_inline;
|
||
default: return defaultSettings;
|
||
}
|
||
};
|
||
|
||
var getSettingsFor = function(pasteWordAs, pasteHtmlAs, base64Images) {
|
||
var s = getImportSettings(pasteWordAs, pasteHtmlAs);
|
||
s = Util.extend(s, {base_64_images: base64Images});
|
||
return s;
|
||
};
|
||
|
||
var create = function(pasteWordAs, pasteHtmlAs, base64Images) {
|
||
var wordSettings = getSettingsFor(pasteWordAs, settings_clean, base64Images);
|
||
var htmlSettings = getSettingsFor(pasteHtmlAs, settings_inline, base64Images);
|
||
|
||
var activeSettings = htmlSettings;
|
||
|
||
var setWordContent = function(wordContent) {
|
||
activeSettings = wordContent ? wordSettings : htmlSettings;
|
||
};
|
||
|
||
var get = function(name) {
|
||
return activeSettings[name];
|
||
};
|
||
return {
|
||
setWordContent: setWordContent,
|
||
get: get
|
||
};
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Attributes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Util) {
|
||
var isAttrSpecified = function(attr) {
|
||
return attr.specified !== false || (attr.nodeName === 'name' && attr.value !== '');
|
||
};
|
||
|
||
var combineFilters = function(filter1, filter2) {
|
||
if (!filter1 || !filter2) {
|
||
return filter1 || filter2;
|
||
}
|
||
return function(name, value) {
|
||
return filter2(name, filter1(name, value));
|
||
};
|
||
};
|
||
|
||
var manager = function(node) {
|
||
var attributeCount = 0;
|
||
var attributes;
|
||
var getCachedAttributes = function() {
|
||
return attributes;
|
||
};
|
||
|
||
var getAttributeMutableFunction;
|
||
var getAttributes = function() {
|
||
return getAttributeMutableFunction();
|
||
};
|
||
|
||
getAttributeMutableFunction = function() {
|
||
attributes = {};
|
||
attributeCount = 0;
|
||
// Load from native.
|
||
Util.each(node.attributes, function(attr) {
|
||
var name = attr.nodeName, value = attr.value;
|
||
if (isAttrSpecified(attr)) {
|
||
if (value !== null && value !== undefined) {
|
||
attributes[name] = value;
|
||
attributeCount++;
|
||
}
|
||
}
|
||
});
|
||
if (attributes.style === undefined && node.style.cssText) {
|
||
attributes.style = node.style.cssText;
|
||
attributeCount++;
|
||
}
|
||
getAttributeMutableFunction = getCachedAttributes;
|
||
return attributes;
|
||
};
|
||
|
||
var getAttributeCount = function() {
|
||
getAttributeMutableFunction();
|
||
return attributeCount;
|
||
};
|
||
|
||
var unappliedFilter;
|
||
var unfilteredGetAttributes;
|
||
|
||
var filter = function(f) {
|
||
if (!unappliedFilter) {
|
||
unfilteredGetAttributes = getAttributeMutableFunction;
|
||
}
|
||
|
||
unappliedFilter = combineFilters(unappliedFilter, f);
|
||
// Defer applying the filter until we absolutely have to.
|
||
getAttributeMutableFunction = function() {
|
||
getAttributeMutableFunction = unfilteredGetAttributes;
|
||
eachAttribute(function(name, value) {
|
||
var newValue = unappliedFilter(name, value);
|
||
if (newValue === null) {
|
||
node.removeAttribute(name);
|
||
delete attributes[name];
|
||
attributeCount--;
|
||
} else if (newValue !== value) {
|
||
if (name === 'class') {
|
||
node.className = newValue;
|
||
} else {
|
||
node.setAttribute(name, newValue);
|
||
}
|
||
attributes[name] = newValue;
|
||
}
|
||
});
|
||
getAttributeMutableFunction = getCachedAttributes;
|
||
return attributes;
|
||
};
|
||
};
|
||
|
||
var get = function(name) {
|
||
return getAttributeMutableFunction()[name];
|
||
};
|
||
|
||
var eachAttribute = function(callback) {
|
||
Util.each(getAttributeMutableFunction(), function(value, name) {
|
||
callback(name, value);
|
||
});
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
each: eachAttribute,
|
||
filter: filter,
|
||
getAttributes: getAttributes,
|
||
getAttributeCount: getAttributeCount
|
||
};
|
||
};
|
||
return {
|
||
manager: manager
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Attributes',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Attributes, Util, TokenTypes) {
|
||
var START_ELEMENT_TYPE = 'startElement';
|
||
var END_ELEMENT_TYPE = 'endElement';
|
||
var TEXT_TYPE = 'text';
|
||
var COMMENT_TYPE = 'comment';
|
||
|
||
var attributeManager = Attributes.manager;
|
||
|
||
/**
|
||
* Converts a style name from the CSS version (e.g. text-align) to the
|
||
* DOM property equivalent (textAlign).
|
||
*
|
||
* @param name the style name to convert.
|
||
* @returns the style name in DOM form.
|
||
*/
|
||
var convertStyleName = function(name) {
|
||
return name.replace(/-(.)/g, function(regex, capture) {
|
||
return capture.toUpperCase();
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Converts a style name from the DOM version (e.g. textAlign) to the
|
||
* CSS equivalent (text-align). This is the reverse of convertStyleName.
|
||
*
|
||
* @param name the style name to convert.
|
||
* @returns the style name in CSS form.
|
||
*/
|
||
var convertStyleNameBack = function(name) {
|
||
return name.replace(/([A-Z])/g, function(regex, capture) {
|
||
return '-' + capture.toLowerCase();
|
||
});
|
||
};
|
||
|
||
// This is shared across all instances because if we ever see an mso- style in the normal iteration
|
||
// we can be certain they are included and don't need the extra check.
|
||
var seenMsoStyle = false;
|
||
var eachNativeStyle = function(node, syntheticStyles, callback) {
|
||
var i, len = node.style.length, name, value, styles;
|
||
styles = syntheticStyles || node.getAttribute("style");
|
||
if (styles === undefined || styles === null || !styles.split) {
|
||
styles = node.style.cssText;
|
||
}
|
||
Util.each(styles.split(';'), function(declaration) {
|
||
var idx = declaration.indexOf(':');
|
||
if (idx > 0) {
|
||
name = Util.trim(declaration.substring(0, idx));
|
||
if (name.toUpperCase() === name) {
|
||
name = name.toLowerCase();
|
||
}
|
||
name = convertStyleNameBack(name);
|
||
value = Util.trim(declaration.substring(idx + 1));
|
||
if (!seenMsoStyle) seenMsoStyle = name.indexOf('mso-') === 0;
|
||
callback(name, value);
|
||
}
|
||
});
|
||
if (!seenMsoStyle) {
|
||
// IE9 preserves unknown styles but provides no way to iterate them.
|
||
// To deal with that, we look for the specific custom styles we care about.
|
||
value = node.style['mso-list'];
|
||
if (value) {
|
||
callback('mso-list', value);
|
||
}
|
||
}
|
||
};
|
||
|
||
var token = function(node, endNode, syntheticStyles) {
|
||
var tokenType;
|
||
var tagName;
|
||
var tokenText;
|
||
var attributeStore;
|
||
var tokenStyles;
|
||
switch (node.nodeType) {
|
||
case 1:
|
||
if (endNode) {
|
||
tokenType = END_ELEMENT_TYPE;
|
||
} else {
|
||
tokenType = START_ELEMENT_TYPE;
|
||
attributeStore = attributeManager(node);
|
||
|
||
tokenStyles = {};
|
||
eachNativeStyle(node, syntheticStyles, function(name, value) {
|
||
tokenStyles[name] = value;
|
||
});
|
||
}
|
||
if (node.scopeName !== "HTML" && node.scopeName && node.tagName && node.tagName.indexOf(':') <= 0) {
|
||
tagName = (node.scopeName + ":" + node.tagName).toUpperCase();
|
||
} else {
|
||
tagName = node.tagName;
|
||
}
|
||
|
||
break;
|
||
case 3:
|
||
tokenType = TEXT_TYPE;
|
||
tokenText = node.nodeValue;
|
||
break;
|
||
case 8:
|
||
tokenType = COMMENT_TYPE;
|
||
tokenText = node.nodeValue;
|
||
break;
|
||
default:
|
||
Util.log("WARNING: Unsupported node type encountered: " + node.nodeType);
|
||
break;
|
||
}
|
||
|
||
var getNode = function() {
|
||
// Make sure all filters are applied.
|
||
if (attributeStore) attributeStore.getAttributes();
|
||
return node;
|
||
};
|
||
|
||
var tag = function() {
|
||
return tagName;
|
||
};
|
||
|
||
var type = function() {
|
||
return tokenType;
|
||
};
|
||
|
||
var text = function() {
|
||
return tokenText;
|
||
};
|
||
|
||
var toString = function() {
|
||
return "Type: " + tokenType + ", Tag: " + tagName + " Text: " + tokenText;
|
||
};
|
||
|
||
var getAttribute = function(name) {
|
||
return attributeStore.get(name.toLowerCase());
|
||
};
|
||
|
||
var filterAttributes = function(filter) {
|
||
if (tokenType === START_ELEMENT_TYPE) {
|
||
attributeStore.filter(filter);
|
||
}
|
||
};
|
||
|
||
var filterStyles = function(filter) {
|
||
if (type() === START_ELEMENT_TYPE) {
|
||
var css = "";
|
||
Util.each(tokenStyles, function(value, name) {
|
||
var newValue = filter(name, value);
|
||
if (newValue === null) {
|
||
if (node.style.removeProperty) {
|
||
node.style.removeProperty(convertStyleName(name));
|
||
} else {
|
||
node.style.removeAttribute(convertStyleName(name));
|
||
}
|
||
delete tokenStyles[name];
|
||
} else {
|
||
css += name + ': ' + newValue + '; ';
|
||
tokenStyles[name] = newValue;
|
||
}
|
||
});
|
||
css = css ? css : null;
|
||
filterAttributes(function(name, value) {
|
||
if (name === 'style') {
|
||
return css;
|
||
}
|
||
return value;
|
||
});
|
||
node.style.cssText = css;
|
||
}
|
||
};
|
||
|
||
var getAttributeCount = function() {
|
||
return attributeStore.getAttributeCount();
|
||
};
|
||
|
||
var attributes = function(callback) {
|
||
attributeStore.each(callback);
|
||
};
|
||
|
||
var getStyle = function(name) {
|
||
return tokenStyles[name];
|
||
};
|
||
|
||
var styles = function(callback) {
|
||
Util.each(tokenStyles, function(value, name) {
|
||
callback(name, value);
|
||
});
|
||
};
|
||
|
||
var getComputedStyle = function() {
|
||
return Util.ephoxGetComputedStyle(node);
|
||
};
|
||
|
||
var isWhitespace = function() {
|
||
return tokenType === TEXT_TYPE && /^[\s\u00A0]*$/.test(tokenText);
|
||
};
|
||
|
||
return {
|
||
getNode: getNode,
|
||
tag: tag,
|
||
type: type,
|
||
text: text,
|
||
toString: toString,
|
||
getAttribute: getAttribute,
|
||
filterAttributes: filterAttributes,
|
||
filterStyles: filterStyles,
|
||
getAttributeCount: getAttributeCount,
|
||
attributes: attributes,
|
||
getStyle: getStyle,
|
||
styles: styles,
|
||
getComputedStyle: getComputedStyle,
|
||
isWhitespace: isWhitespace
|
||
};
|
||
};
|
||
|
||
var createStartElement = function(tag, attributes, styles, document) {
|
||
var node = document.createElement(tag), css = "";
|
||
Util.each(attributes, function(value, name) {
|
||
node.setAttribute(name, value);
|
||
});
|
||
Util.each(styles, function(value, name) {
|
||
css += name + ":" + value + ";";
|
||
node.style[convertStyleName(name)] = value;
|
||
});
|
||
return token(node, false, css !== "" ? css : null);
|
||
};
|
||
|
||
var createEndElement = function(tag, document) {
|
||
return token(document.createElement(tag), true);
|
||
};
|
||
|
||
var createComment = function(text, document) {
|
||
return token(document.createComment(text), false);
|
||
};
|
||
|
||
var createText = function(text, document) {
|
||
return token(document.createTextNode(text));
|
||
};
|
||
|
||
var FINISHED = createEndElement('HTML', window.document);
|
||
|
||
return {
|
||
START_ELEMENT_TYPE: START_ELEMENT_TYPE,
|
||
END_ELEMENT_TYPE: END_ELEMENT_TYPE,
|
||
TEXT_TYPE: TEXT_TYPE,
|
||
COMMENT_TYPE: COMMENT_TYPE,
|
||
FINISHED: FINISHED,
|
||
token: token,
|
||
createStartElement: createStartElement,
|
||
createEndElement: createEndElement,
|
||
createComment: createComment,
|
||
createText: createText
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Serializer',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
var create = function(document) {
|
||
var currentNode = document.createDocumentFragment();
|
||
var initialNode = currentNode;
|
||
|
||
var push = function(node) {
|
||
append(node);
|
||
currentNode = node;
|
||
};
|
||
|
||
var pop = function() {
|
||
currentNode = currentNode.parentNode;
|
||
};
|
||
|
||
var append = function(node) {
|
||
currentNode.appendChild(node);
|
||
};
|
||
|
||
var receive = function(token) {
|
||
|
||
var startElement = function(token) {
|
||
var node = token.getNode().cloneNode(false);
|
||
push(node);
|
||
};
|
||
|
||
var text = function(token, serializer) {
|
||
// IE7 will crash if you clone a text node that's a URL.
|
||
// IE8 throws an invalid argument error.
|
||
// So while cloning may be faster, we have to create a new node here.
|
||
var node = document.createTextNode(token.text());
|
||
append(node);
|
||
};
|
||
|
||
switch (token.type()) {
|
||
case Token.START_ELEMENT_TYPE:
|
||
startElement(token);
|
||
break;
|
||
case Token.TEXT_TYPE:
|
||
text(token);
|
||
break;
|
||
case Token.END_ELEMENT_TYPE:
|
||
pop();
|
||
break;
|
||
case Token.COMMENT_TYPE:
|
||
// Ignore.
|
||
break;
|
||
default:
|
||
throw { message: 'Unsupported token type: ' + token.type() };
|
||
}
|
||
};
|
||
|
||
return {
|
||
dom: initialNode,
|
||
receive: receive
|
||
};
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Tokenizer',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
var tokenize = function(html, document) {
|
||
var container;
|
||
document = document || window.document;
|
||
container = document.createElement('div');
|
||
document.body.appendChild(container);
|
||
container.style.position = 'absolute';
|
||
container.style.left = '-10000px';
|
||
container.innerHTML = html;
|
||
|
||
nextNode = container.firstChild || Token.FINISHED;
|
||
|
||
var nodeStack = [];
|
||
endNode = false;
|
||
|
||
var getTokenForNode = function(node, endTag) {
|
||
if (node === Token.FINISHED) {
|
||
return node;
|
||
} else if (node) {
|
||
return Token.token(node, endTag);
|
||
} else {
|
||
return undefined;
|
||
}
|
||
};
|
||
|
||
var next = function() {
|
||
var currentNode = nextNode;
|
||
var currentEndNode = endNode;
|
||
if (!endNode && nextNode.firstChild) {
|
||
nodeStack.push(nextNode);
|
||
nextNode = nextNode.firstChild;
|
||
} else if (!endNode && nextNode.nodeType === 1) {
|
||
// Empty element.
|
||
endNode = true;
|
||
} else if (nextNode.nextSibling) {
|
||
nextNode = nextNode.nextSibling;
|
||
endNode = false;
|
||
} else {
|
||
nextNode = nodeStack.pop();
|
||
endNode = true;
|
||
}
|
||
|
||
if (currentNode !== Token.FINISHED && !nextNode) {
|
||
document.body.removeChild(container);
|
||
nextNode = Token.FINISHED;
|
||
}
|
||
|
||
return getTokenForNode(currentNode, currentEndNode);
|
||
};
|
||
|
||
var hasNext = function() {
|
||
return nextNode !== undefined;
|
||
};
|
||
|
||
return {
|
||
hasNext: hasNext,
|
||
next: next
|
||
};
|
||
};
|
||
|
||
return {
|
||
tokenize: tokenize
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Token, Util) {
|
||
|
||
var createFilter = function(actualReceiver, clientReset) {
|
||
var filter = function(nextFilter, settings, document) {
|
||
var deferred;
|
||
var receivedTokens, emittedTokens, inTransaction = false;
|
||
|
||
var resetState = function() {
|
||
if (clientReset) clientReset(api);
|
||
inTransaction = false;
|
||
receivedTokens = [];
|
||
emittedTokens = [];
|
||
};
|
||
|
||
var emitTokens = function(tokens) {
|
||
Util.each(tokens, function(tok) {
|
||
nextFilter.receive(tok);
|
||
});
|
||
};
|
||
|
||
var emit = function(token) {
|
||
if (inTransaction) {
|
||
emittedTokens.push(token);
|
||
} else {
|
||
nextFilter.receive(token);
|
||
}
|
||
};
|
||
|
||
var receive = function(token) {
|
||
if (clientReset) receivedTokens.push(token);
|
||
actualReceiver(api, token);
|
||
if (token === Token.FINISHED) {
|
||
commit();
|
||
}
|
||
};
|
||
|
||
var startTransaction = function() {
|
||
inTransaction = true;
|
||
};
|
||
|
||
var rollback = function() {
|
||
emitTokens(receivedTokens);
|
||
resetState();
|
||
};
|
||
|
||
var commit = function() {
|
||
emitDeferred();
|
||
emitTokens(emittedTokens);
|
||
resetState();
|
||
};
|
||
|
||
var defer = function(token) {
|
||
deferred = deferred || [];
|
||
deferred.push(token);
|
||
};
|
||
|
||
var hasDeferred = function() {
|
||
return deferred && deferred.length > 0;
|
||
};
|
||
|
||
var emitDeferred = function() {
|
||
Util.each(deferred, function(token) {
|
||
emit(token);
|
||
});
|
||
dropDeferred();
|
||
};
|
||
|
||
var dropDeferred = function() {
|
||
deferred = [];
|
||
};
|
||
|
||
var api = {
|
||
document: document || window.document,
|
||
settings: settings || {},
|
||
emit: emit,
|
||
receive: receive,
|
||
startTransaction: startTransaction,
|
||
rollback: rollback,
|
||
commit: commit,
|
||
defer: defer,
|
||
hasDeferred: hasDeferred,
|
||
emitDeferred: emitDeferred,
|
||
dropDeferred: dropDeferred
|
||
};
|
||
|
||
resetState();
|
||
return api;
|
||
};
|
||
return filter;
|
||
};
|
||
|
||
var createAttributeFilter = function(filter) {
|
||
return createFilter(function(api, token) {
|
||
token.filterAttributes(Util.bind(filter, api));
|
||
api.emit(token);
|
||
});
|
||
};
|
||
|
||
return {
|
||
createFilter: createFilter,
|
||
createAttributeFilter: createAttributeFilter
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.Text',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Token) {
|
||
var lineBreakElements = /^(P|H[1-6]|T[DH]|LI|DIV|BLOCKQUOTE|PRE|ADDRESS|FIELDSET|DD|DT|CENTER)$/;
|
||
var causesLinebreak = function(token) {
|
||
return lineBreakElements.test(token.tag());
|
||
};
|
||
var removeFilter = function() {
|
||
return null;
|
||
};
|
||
|
||
var inP = false;
|
||
|
||
return Filter.createFilter(function(api, token) {
|
||
var ensureInP = function() {
|
||
if (!inP) {
|
||
api.emit(Token.createStartElement('P', {}, {}, api.document));
|
||
inP = true;
|
||
}
|
||
};
|
||
switch (token.type()) {
|
||
case Token.TEXT_TYPE:
|
||
ensureInP();
|
||
api.emit(token);
|
||
break;
|
||
case Token.END_ELEMENT_TYPE:
|
||
if (inP && (causesLinebreak(token) || token === Token.FINISHED)) {
|
||
api.emit(Token.createEndElement('P', api.document));
|
||
inP = false;
|
||
} else if (token.tag() === 'BR') {
|
||
api.emit(token);
|
||
}
|
||
break;
|
||
case Token.START_ELEMENT_TYPE:
|
||
if (token.tag() === 'BR') {
|
||
token.filterAttributes(removeFilter);
|
||
token.filterStyles(removeFilter);
|
||
api.emit(token);
|
||
} else if (token.tag() === 'IMG' && token.getAttribute('alt')) {
|
||
ensureInP();
|
||
api.emit(Token.createText(token.getAttribute('alt'), api.document));
|
||
}
|
||
break;
|
||
}
|
||
if (token === Token.FINISHED) {
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.data.tokens.Helper',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
var checkSupportsCustomStyles = function() {
|
||
// Firefox 4 preserves these styles in the DOM, but strips them when pasting.
|
||
// Since we can't trigger a paste there's no way to detect this situation apart from sniffing.
|
||
if (navigator.userAgent.indexOf('Gecko') > 0 && navigator.userAgent.indexOf('WebKit') < 0) return false;
|
||
var div = document.createElement('div');
|
||
try {
|
||
div.innerHTML = '<p style="mso-list: Ignore;"> </p>';
|
||
} catch (ex) {
|
||
// Can't set innerHTML if we're in XHTML mode so just assume we don't get custom styles.
|
||
return false;
|
||
}
|
||
return Token.token(div.firstChild).getStyle('mso-list') === 'Ignore';
|
||
};
|
||
|
||
var supportsCustomStyles = checkSupportsCustomStyles();
|
||
|
||
var spanOrA = function(token) {
|
||
return token.tag() === 'A' || token.tag() === 'SPAN';
|
||
};
|
||
|
||
var hasMsoListStyle = function(token) {
|
||
var style = token.getStyle('mso-list');
|
||
return style && style !== 'skip';
|
||
};
|
||
|
||
var hasNoAttributes = function(token, allowStyle) {
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
return token.getAttributeCount() === 0 ||
|
||
(allowStyle && token.getAttributeCount() === 1 &&
|
||
(token.getAttribute('style') !== null && token.getAttribute('style') !== undefined));
|
||
} else {
|
||
return token.type() === Token.END_ELEMENT_TYPE;
|
||
}
|
||
};
|
||
|
||
return {
|
||
hasNoAttributes: hasNoAttributes,
|
||
supportsCustomStyles: supportsCustomStyles,
|
||
spanOrA: spanOrA,
|
||
hasMsoListStyle: hasMsoListStyle
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.list.ListTypes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Token, Util) {
|
||
var orderedListTypes = [
|
||
{ regex: /^\(?[dc][\.\)]$/, type: { tag: 'OL', type: 'lower-alpha' } },
|
||
{ regex: /^\(?[DC][\.\)]$/, type: { tag: 'OL', type: 'upper-alpha' } },
|
||
{ regex: /^\(?M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})[\.\)]$/, type: { tag: 'OL', type: 'upper-roman' } },
|
||
{ regex: /^\(?m*(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})[\.\)]$/, type: { tag: 'OL', type: 'lower-roman' } },
|
||
{ regex: /^\(?[0-9]+[\.\)]$/, type: { tag: 'OL' } },
|
||
{ regex: /^([0-9]+\.)*[0-9]+\.?$/, type: { tag: 'OL', variant: 'outline' } },
|
||
{ regex: /^\(?[a-z]+[\.\)]$/, type: { tag: 'OL', type: 'lower-alpha' } },
|
||
{ regex: /^\(?[A-Z]+[\.\)]$/, type: { tag: 'OL', type: 'upper-alpha' } }
|
||
];
|
||
|
||
var ulChars = {
|
||
'\u2022': { tag: 'UL', type: 'disc' },
|
||
'\u00B7': { tag: 'UL', type: 'disc' },
|
||
'\u00A7': { tag: 'UL', type: 'square' }
|
||
};
|
||
|
||
var ulNonSymbolChars = {
|
||
'o': { tag: 'UL', type: 'circle' },
|
||
'-': { tag: 'UL', type: 'disc' },
|
||
'\u25CF': { tag: 'UL', type: 'disc' }
|
||
};
|
||
|
||
var createVariant = function(type, variant) {
|
||
var newType = { tag: type.tag, type: type.type, variant: variant };
|
||
if (type.start){
|
||
newType.start = type.start;
|
||
}
|
||
if (!type.type) delete newType.type;
|
||
return newType;
|
||
};
|
||
|
||
var guessListType = function(bulletInfo, preferredType, originalToken) {
|
||
var listType = null, text, symbolFont, variant;
|
||
if (bulletInfo) {
|
||
text = bulletInfo.text;
|
||
symbolFont = bulletInfo.symbolFont;
|
||
}
|
||
text = Util.trim(text);
|
||
|
||
listType = ulNonSymbolChars[text];
|
||
if (!listType) {
|
||
if (symbolFont) {
|
||
listType = ulChars[text];
|
||
if (!listType) {
|
||
listType = { tag: 'UL', variant: text };
|
||
} else {
|
||
listType = createVariant(listType, text);
|
||
}
|
||
} else {
|
||
|
||
Util.each(orderedListTypes, function(def) {
|
||
if (def.regex.test(text)) {
|
||
if (preferredType && eqListType(def.type, preferredType, true)) {
|
||
listType = def.type;
|
||
listType.start=parseInt(text);
|
||
return false;
|
||
}
|
||
if (!listType) listType = def.type;
|
||
listType.start=parseInt(text);
|
||
}
|
||
});
|
||
if (listType && !listType.variant) {
|
||
if (text.charAt(0) === '(') variant = '()';
|
||
else if (text.charAt(text.length - 1) === ')') variant = ')';
|
||
else variant = '.';
|
||
listType = createVariant(listType, variant);
|
||
}
|
||
}
|
||
} else {
|
||
listType = createVariant(listType, text);
|
||
}
|
||
|
||
if (listType && listType.tag === 'OL' &&
|
||
originalToken && (originalToken.tag() !== 'P' || /^MsoHeading/.test(originalToken.getAttribute('class')))) {
|
||
// Don't convert numbered headings but do convert bulleted headings.
|
||
listType = null;
|
||
}
|
||
|
||
return listType;
|
||
};
|
||
|
||
var eqListType = function(t1, t2, ignoreVariant) {
|
||
return t1 === t2 ||
|
||
(t1 && t2 && t1.tag === t2.tag && t1.type === t2.type &&
|
||
(ignoreVariant || t1.variant === t2.variant));
|
||
};
|
||
|
||
var checkFont = function(token, symbolFont) {
|
||
if (token.type() == Token.START_ELEMENT_TYPE) {
|
||
font = token.getStyle('font-family');
|
||
if (font) {
|
||
symbolFont = (font === 'Wingdings' || font === 'Symbol');
|
||
} else if (/^(P|H[1-6]|DIV)$/.test(token.tag())) {
|
||
symbolFont = false;
|
||
}
|
||
}
|
||
return symbolFont;
|
||
};
|
||
|
||
return {
|
||
guessListType: guessListType,
|
||
eqListType: eqListType,
|
||
checkFont: checkFont
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.list.CommentHeuristics',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.filters.list.ListTypes',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Token, ListTypes, Util) {
|
||
var isListWithoutCommentsOrStyles = function(token, state) {
|
||
var indent, cls, node, symbolFont = false, value, listType;
|
||
var checkFont = function(n) {
|
||
var font = n.style.fontFamily;
|
||
if (font) {
|
||
symbolFont = (font === 'Wingdings' || font === 'Symbol');
|
||
}
|
||
};
|
||
if (token.type() === Token.START_ELEMENT_TYPE && state.openedTag && token.tag() === 'SPAN') {
|
||
node = state.openedTag.getNode();
|
||
checkFont(node);
|
||
if (node.childNodes.length > 1 && node.firstChild.tagName === 'A' && node.firstChild.textContent === '') {
|
||
node = node.childNodes[1];
|
||
}
|
||
while (node.firstChild && (node.firstChild.tagName === 'SPAN' || node.firstChild.tagName === 'A')) {
|
||
node = node.firstChild;
|
||
checkFont(node);
|
||
}
|
||
node = node.firstChild;
|
||
if (node && node.nodeType === 3) {
|
||
value = node.value;
|
||
if (!Util.trim(value)) {
|
||
// This handles the case where there's a SPAN with nbsps before the bullet such as with roman numerals.
|
||
node = node.parentNode.nextSibling;
|
||
value = node ? node.value : '';
|
||
}
|
||
// Real lists have the bullet with NBSPs either side surrounded in a SPAN. If there's anything else, it's not a list.
|
||
if (!node || Util.trim(node.parentNode.textContent) != value) {
|
||
return false;
|
||
}
|
||
listType = ListTypes.guessListType({ text: value, symbolFont: symbolFont }, null, state.originalToken);
|
||
if (listType) {
|
||
// Don't convert numbered headings to lists.
|
||
return node.nextSibling && node.nextSibling.tagName === 'SPAN' && /^[\u00A0\s]/.test(node.nextSibling.firstChild.value) &&
|
||
(state.openedTag.tag() === 'P' || listType.tag === 'UL');
|
||
}
|
||
} else {
|
||
return node && node.tagName === 'IMG';
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
|
||
|
||
var getLeftOffset = function(node, paragraph) {
|
||
var parent, child, offset = 0;
|
||
parent = node.parentNode;
|
||
while (parent !== null && parent !== undefined && parent !== paragraph.parentNode) {
|
||
offset += parent.offsetLeft;
|
||
parent = parent.offsetParent;
|
||
}
|
||
return offset;
|
||
};
|
||
|
||
/** A simplified memoize function which only supports one or two function parameters.
|
||
*
|
||
* @param fn
|
||
* @param param the funtion p
|
||
* @returns
|
||
*/
|
||
var memoize2 = function(fn) {
|
||
var cache = {};
|
||
return function(param1, param2) {
|
||
var result, key = param1 + "," + param2;
|
||
if (cache.hasOwnProperty(key)) {
|
||
return cache[key];
|
||
}
|
||
result = fn.call(null, param1, param2);
|
||
cache[key] = result;
|
||
return result;
|
||
};
|
||
};
|
||
|
||
var findStylesInner = function(selector) {
|
||
var dotIndex = selector.indexOf('.');
|
||
if (dotIndex >= 0 && Util.trim(selector.substring(dotIndex + 1)) === className) {
|
||
match = results[2];
|
||
return false;
|
||
}
|
||
};
|
||
|
||
var findStyles = memoize2(function(css, className) {
|
||
var results, matcher = /([^{]+){([^}]+)}/g, match, el, computedStyle;
|
||
matcher.lastIndex = 0; // Firefox Mac reuses the same regex so we need to reset it.
|
||
while ((results = matcher.exec(css)) !== null && !match) {
|
||
Util.each(results[1].split(','), findStylesInner(selector)
|
||
);
|
||
}
|
||
if (match) {
|
||
el = document.createElement('p');
|
||
el.setAttribute("style", match);
|
||
computedStyle = Util.ephoxGetComputedStyle(el);
|
||
return computedStyle ? "" + computedStyle.marginLeft : false;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
var indentGuesser = function() {
|
||
var listIndentAdjust;
|
||
var listIndentAmount;
|
||
var guessIndentLevel = function(currentToken, token, styles, bulletInfo) {
|
||
var indentAmount, itemIndent, el, level = 1;
|
||
|
||
if (bulletInfo && /^([0-9]+\.)+[0-9]+\.?$/.test(bulletInfo.text)) {
|
||
// Outline list type so we can just count the number of sections.
|
||
return bulletInfo.text.replace(/([0-9]+|\.$)/g, '').length + 1;
|
||
}
|
||
indentAmount = listIndentAmount || parseInt(findStyles(styles, token.getAttribute('class')));
|
||
|
||
itemIndent = getLeftOffset(currentToken.getNode(), token.getNode());
|
||
if (!indentAmount) {
|
||
indentAmount = 48;
|
||
} else {
|
||
// We might get a 0 item indent if the list CSS code wasn't pasted as happens on Windows.
|
||
if (listIndentAdjust) {
|
||
itemIndent += listIndentAdjust;
|
||
} else if (itemIndent === 0) {
|
||
listIndentAdjust = indentAmount;
|
||
itemIndent += indentAmount;
|
||
}
|
||
}
|
||
listIndentAmount = indentAmount = Math.min(itemIndent, indentAmount);
|
||
level = Math.max(1, Math.floor(itemIndent / indentAmount)) || 1;
|
||
return level;
|
||
};
|
||
return {
|
||
guessIndentLevel: guessIndentLevel
|
||
};
|
||
};
|
||
|
||
var styles = function() {
|
||
var inStyle = false;
|
||
var styles = "";
|
||
var check = function(token) {
|
||
if (inStyle && token.type() === Token.TEXT_TYPE) {
|
||
styles += token.text();
|
||
return true;
|
||
} else if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'STYLE') {
|
||
inStyle = true;
|
||
return true;
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === 'STYLE') {
|
||
inStyle = false;
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
return {
|
||
check: check
|
||
};
|
||
};
|
||
|
||
return {
|
||
isListWithoutCommentsOrStyles: isListWithoutCommentsOrStyles,
|
||
indentGuesser: indentGuesser,
|
||
styles: styles
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.list.Emitter',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.filters.list.ListTypes'
|
||
],
|
||
|
||
function (Token, ListTypes) {
|
||
var impliedULatLevel = [ 'disc', 'circle', 'square' ];
|
||
|
||
var removeImpliedListType = function(type, level) {
|
||
if (type.tag === 'UL') {
|
||
if (impliedULatLevel[level - 1] === type.type) {
|
||
type = { tag: 'UL' };
|
||
}
|
||
}
|
||
return type;
|
||
};
|
||
|
||
return function(api, document) {
|
||
var listTypes = [];
|
||
var itemTags = [];
|
||
var currentLevel = 0;
|
||
var currentListType;
|
||
|
||
var openList = function(type, useType) {
|
||
var style = {}, attributes={};
|
||
currentLevel++;
|
||
if (useType) {
|
||
if (type.type) {
|
||
style = { 'list-style-type': type.type };
|
||
}
|
||
}
|
||
if (type.start && type.start>1) {
|
||
attributes={start:type.start};
|
||
}
|
||
listTypes.push(type);
|
||
api.emit(Token.createStartElement(type.tag, attributes, style, document));
|
||
currentListType = type;
|
||
};
|
||
|
||
var closeList = function() {
|
||
api.emit(Token.createEndElement(listTypes.pop().tag, document));
|
||
currentLevel--;
|
||
currentListType = listTypes[listTypes.length - 1];
|
||
};
|
||
|
||
var closeAllLists = function() {
|
||
while (currentLevel > 0) {
|
||
closeItem();
|
||
closeList();
|
||
}
|
||
api.commit();
|
||
};
|
||
|
||
var closeItem = function() {
|
||
var tag = itemTags ? itemTags.pop() : 'P';
|
||
if (tag != 'P') {
|
||
api.emit(Token.createEndElement(tag, document));
|
||
}
|
||
api.emit(Token.createEndElement('LI', document));
|
||
};
|
||
|
||
var openLI = function(paragraphToken, type, skippedPara) {
|
||
var style = {};
|
||
if (!paragraphToken) {
|
||
style['list-style-type'] = 'none';
|
||
} else {
|
||
var leftMargin = paragraphToken.getStyle('margin-left');
|
||
if (leftMargin !== undefined) {
|
||
style['margin-left'] = leftMargin;
|
||
}
|
||
}
|
||
if (currentListType && !ListTypes.eqListType(currentListType, type)) {
|
||
closeList();
|
||
if (skippedPara) {
|
||
api.emit(Token.createStartElement('P', {}, {}, document));
|
||
api.emit(Token.createText('\u00A0', document));
|
||
api.emit(Token.createEndElement('P', document));
|
||
}
|
||
openList(type, true);
|
||
}
|
||
api.emit(Token.createStartElement('LI', {}, style, document));
|
||
if (paragraphToken && paragraphToken.tag() != 'P') {
|
||
itemTags.push(paragraphToken.tag());
|
||
paragraphToken.filterStyles(function() { return null; });
|
||
api.emit(paragraphToken);
|
||
} else {
|
||
itemTags.push('P');
|
||
}
|
||
};
|
||
|
||
var openItem = function(level, paragraphToken, type, skippedPara) {
|
||
var style = {}, token;
|
||
if (!type) return;
|
||
if (!currentLevel) currentLevel = 0;
|
||
while (currentLevel > level) {
|
||
closeItem();
|
||
closeList();
|
||
}
|
||
type = removeImpliedListType(type, level);
|
||
if (currentLevel == level) {
|
||
closeItem();
|
||
openLI(paragraphToken, type, skippedPara);
|
||
} else {
|
||
// If there's a heading item we opened in the list we need to close it before creating the indented list
|
||
if (level > 1 && itemTags.length > 0 && itemTags[itemTags.length - 1] !== 'P') {
|
||
api.emit(Token.createEndElement(itemTags[itemTags.length - 1], document));
|
||
itemTags[itemTags.length - 1] = 'P';
|
||
}
|
||
while (currentLevel < level) {
|
||
openList(type, currentLevel == level - 1);
|
||
openLI(currentLevel == level ? paragraphToken : undefined, type);
|
||
}
|
||
}
|
||
};
|
||
var getCurrentLevel = function() {
|
||
return currentLevel;
|
||
};
|
||
var getCurrentListType = function() {
|
||
return currentListType;
|
||
};
|
||
return {
|
||
openList: openList,
|
||
closelist: closeList,
|
||
closeAllLists: closeAllLists,
|
||
closeItem: closeItem,
|
||
openLI: openLI,
|
||
openItem: openItem,
|
||
getCurrentListType: getCurrentListType,
|
||
getCurrentLevel: getCurrentLevel
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.list.ListStates',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Helper',
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.filters.list.CommentHeuristics',
|
||
'ephox.powerpaste.legacy.filters.list.ListTypes',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Helper, Token, CommentHeuristics, ListTypes, Util) {
|
||
var unexpectedToken = function(api, token) {
|
||
Util.log("Unexpected token in list conversion: " + token.toString());
|
||
api.rollback();
|
||
};
|
||
|
||
var preferredListType = function(currentType, currentLevel, newLevel) {
|
||
if (currentLevel == newLevel) {
|
||
return currentType;
|
||
}
|
||
return null;
|
||
};
|
||
|
||
var afterListState = function(api, state, token) {
|
||
if (token.type() === Token.TEXT_TYPE && Util.trim(token.text()) === '') {
|
||
// Drop whitespace that's potentially between list items.
|
||
api.defer(token);
|
||
} else if (!state.skippedPara && token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'P' && !Helper.hasMsoListStyle(token)) {
|
||
state.openedTag = token;
|
||
api.defer(token);
|
||
state.nextFilter = skipEmptyParaState;
|
||
} else {
|
||
noListState(api, state, token);
|
||
}
|
||
};
|
||
|
||
var skipEmptyParaState = function(api, state, token) {
|
||
if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'SPAN' && state.spanCount.length === 0 &&
|
||
(Helper.supportsCustomStyles || !CommentHeuristics.isListWithoutCommentsOrStyles(token, state)) && !Helper.hasMsoListStyle(token)) {
|
||
api.defer(token);
|
||
state.spanCount.push(token);
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE) {
|
||
if (token.tag() === 'SPAN') {
|
||
api.defer(token);
|
||
state.spanCount.pop();
|
||
} else if (token.tag() === 'P') {
|
||
api.defer(token);
|
||
state.skippedPara = true;
|
||
state.openedTag = null;
|
||
state.nextFilter = afterListState;
|
||
} else {
|
||
// Not an empty paragraph.
|
||
state.nextFilter = noListState;
|
||
state.nextFilter(api, state, token);
|
||
}
|
||
} else if (token.isWhitespace()) {
|
||
api.defer(token);
|
||
} else {
|
||
state.nextFilter = noListState;
|
||
state.nextFilter(api, state, token);
|
||
}
|
||
};
|
||
|
||
var msoListSkipState = function(api, state, token) {
|
||
if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === state.originalToken.tag()) {
|
||
state.nextFilter = afterListState;
|
||
} else if (token === Token.FINISHED) {
|
||
state.emitter.closeAllLists();
|
||
api.emit(token);
|
||
}
|
||
// Else drop.
|
||
};
|
||
|
||
var noListState = function(api, state, token) {
|
||
var closeOutLists = function() {
|
||
state.emitter.closeAllLists();
|
||
api.emitDeferred();
|
||
state.openedTag = null;
|
||
api.emit(token);
|
||
state.nextFilter = noListState;
|
||
};
|
||
if (token.type() === Token.START_ELEMENT_TYPE && Helper.hasMsoListStyle(token) && token.tag() !== 'LI') {
|
||
var msoList = token.getStyle('mso-list');
|
||
if (false && msoList === 'skip') {
|
||
state.nextFilter = msoListSkipState;
|
||
state.originalToken = token;
|
||
} else {
|
||
var lvl = / level([0-9]+)/.exec(token.getStyle('mso-list'));
|
||
|
||
if (lvl && lvl[1]) {
|
||
state.itemLevel = parseInt(lvl[1], 10) + state.styleLevelAdjust;
|
||
// Tokens between lists should be dropped (they're just whitespace anyway)
|
||
// however, tokens before a list should be emitted if we find an mso-list style
|
||
// since this is the very first token of the list.
|
||
if (state.nextFilter === noListState) {
|
||
api.emitDeferred();
|
||
} else {
|
||
api.dropDeferred();
|
||
}
|
||
state.nextFilter = listStartState;
|
||
api.startTransaction();
|
||
state.originalToken = token;
|
||
state.commentMode = false;
|
||
} else {
|
||
closeOutLists();
|
||
}
|
||
}
|
||
} else if (!Helper.supportsCustomStyles &&
|
||
((token.type() === Token.COMMENT_TYPE && token.text() === '[if !supportLists]') ||
|
||
(CommentHeuristics.isListWithoutCommentsOrStyles(token, api)))) {
|
||
if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'SPAN') {
|
||
state.spanCount.push(token);
|
||
}
|
||
state.nextFilter = listStartState;
|
||
api.startTransaction();
|
||
state.originalToken = state.openedTag;
|
||
state.commentMode = true;
|
||
state.openedTag = null;
|
||
api.dropDeferred();
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && Helper.spanOrA(token)) {
|
||
api.defer(token);
|
||
state.spanCount.pop();
|
||
} else if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
// Might be the start of an item, store it and see if we get a comment next.
|
||
if (Helper.spanOrA(token)) {
|
||
api.defer(token);
|
||
state.spanCount.push(token);
|
||
} else {
|
||
if (state.openedTag) {
|
||
state.emitter.closeAllLists();
|
||
api.emitDeferred();
|
||
}
|
||
state.openedTag = token;
|
||
api.defer(token);
|
||
}
|
||
} else {
|
||
closeOutLists();
|
||
}
|
||
};
|
||
|
||
var afterNoBulletListState = function(api, state, token) {
|
||
if (token.type() === Token.END_ELEMENT_TYPE && state.originalToken.tag() === token.tag()) {
|
||
state.nextFilter = afterListState;
|
||
state.styleLevelAdjust = -1;
|
||
}
|
||
api.emit(token);
|
||
};
|
||
|
||
var listStartState = function(api, state, token) {
|
||
if (token.type() == Token.START_ELEMENT_TYPE && token.getStyle('mso-list') === 'Ignore') {
|
||
state.nextFilter = findListTypeState;
|
||
} if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'SPAN') {
|
||
state.spanCount.push(token);
|
||
if (state.commentMode && token.getAttribute("style") === "" || token.getAttribute("style") === null) {
|
||
state.nextFilter = findListTypeState;
|
||
}
|
||
// Otherwise drop.
|
||
} else if (token.tag() === 'A') {
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
state.spanCount.push(token);
|
||
} else {
|
||
state.spanCount.pop();
|
||
}
|
||
} else if (token.type() === Token.TEXT_TYPE) {
|
||
if (state.commentMode) {
|
||
state.nextFilter = findListTypeState;
|
||
state.nextFilter(api, state, token);
|
||
} else {
|
||
// List type without a bullet, we should treat it as a paragraph.
|
||
var start = state.originalToken;
|
||
var spans = state.spanCount;
|
||
state.emitter.closeAllLists();
|
||
api.emit(start);
|
||
Util.each(spans, Util.bind(api.emit, api));
|
||
api.emit(token);
|
||
api.commit();
|
||
state.originalToken = start;
|
||
state.nextFilter = afterNoBulletListState;
|
||
}
|
||
} else if (!state.commentMode && token.type() === Token.COMMENT_TYPE) {
|
||
// Drop. We seem to be getting custom styles and comments.
|
||
} else {
|
||
unexpectedToken(api, token);
|
||
}
|
||
};
|
||
|
||
var findListTypeState = function(api, state, token) {
|
||
if (token.type() === Token.TEXT_TYPE) {
|
||
if (token.isWhitespace()) {
|
||
// Ignore whitespace node, it's padding before the actual list type.
|
||
} else {
|
||
state.nextFilter = beforeSpacerState;
|
||
state.bulletInfo = { text: token.text(), symbolFont: state.symbolFont };
|
||
}
|
||
} else if (Helper.spanOrA(token)) {
|
||
// Drop open and close span tags.
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
state.spanCount.push(token);
|
||
} else {
|
||
state.spanCount.pop();
|
||
}
|
||
} else if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'IMG') {
|
||
// Custom list image type. We can't access the image so use a normal bullet instead.
|
||
// EditLive! may want this to come through as a CSS reference.
|
||
state.nextFilter = beforeSpacerState;
|
||
state.bulletInfo = { text: '\u2202', symbolFont: true };
|
||
} else {
|
||
unexpectedToken(api, token);
|
||
}
|
||
};
|
||
|
||
var beforeSpacerState = function(api, state, token) {
|
||
if (token.type() === Token.START_ELEMENT_TYPE && Helper.spanOrA(token)) {
|
||
state.spanCount.push(token);
|
||
state.nextFilter = spacerState;
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && Helper.spanOrA(token)) {
|
||
state.spanCount.pop();
|
||
state.nextFilter = closeSpansState;
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === 'IMG') {
|
||
// Drop
|
||
} else {
|
||
unexpectedToken(api, token);
|
||
}
|
||
};
|
||
|
||
var spacerState = function(api, state, token) {
|
||
if (token.type() === Token.END_ELEMENT_TYPE) {
|
||
if (Helper.spanOrA(token)) {
|
||
state.spanCount.pop();
|
||
}
|
||
state.nextFilter = closeSpansState;
|
||
}
|
||
// Drop all other tokens.
|
||
};
|
||
|
||
var closeSpansState = function(api, state, token) {
|
||
var moveToItemContentState = function(includeToken) {
|
||
state.nextFilter = itemContentState;
|
||
if (state.commentMode) state.itemLevel = state.indentGuesser.guessIndentLevel(token, state.originalToken, state.styles.styles, state.bulletInfo);
|
||
state.listType = ListTypes.guessListType(state.bulletInfo, preferredListType(state.emitter.getCurrentListType(), state.emitter.getCurrentLevel(), state.itemLevel), state.originalToken);
|
||
if (state.listType) {
|
||
state.emitter.openItem(state.itemLevel, state.originalToken, state.listType, state.skippedPara);
|
||
api.emitDeferred();
|
||
while (state.spanCount.length > 0) {
|
||
api.emit(state.spanCount.shift());
|
||
}
|
||
if (includeToken) {
|
||
api.emit(token);
|
||
}
|
||
} else {
|
||
Util.log("Unknown list type: " + state.bulletInfo.text + " Symbol font? " + state.bulletInfo.symbolFont);
|
||
api.rollback();
|
||
}
|
||
};
|
||
|
||
if (token.type() === Token.TEXT_TYPE || token.type() === Token.START_ELEMENT_TYPE) {
|
||
moveToItemContentState(true);
|
||
} else if (token.type() === Token.COMMENT_TYPE) {
|
||
moveToItemContentState(token.text() !== '[endif]');
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE) {
|
||
if (Helper.spanOrA(token)) {
|
||
state.spanCount.pop();
|
||
}
|
||
} else {
|
||
unexpectedToken(api, token);
|
||
}
|
||
};
|
||
|
||
var itemContentState = function(api, state, token) {
|
||
if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === state.originalToken.tag()) {
|
||
state.nextFilter = afterListState;
|
||
state.skippedPara = false;
|
||
} else {
|
||
api.emit(token);
|
||
}
|
||
};
|
||
|
||
var initial = noListState;
|
||
return {
|
||
initial: initial
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.list.Lists',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Helper',
|
||
'ephox.powerpaste.legacy.data.tokens.Token',
|
||
'ephox.powerpaste.legacy.filters.list.CommentHeuristics',
|
||
'ephox.powerpaste.legacy.filters.list.Emitter',
|
||
'ephox.powerpaste.legacy.filters.list.ListStates',
|
||
'ephox.powerpaste.legacy.filters.list.ListTypes',
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Filter, Helper, Token, CommentHeuristics, Emitter, ListStates, ListTypes, Util) {
|
||
|
||
var activeState = {};
|
||
|
||
var resetActiveState = function(api) {
|
||
//It would be nice if this was creating a fresh object, but listStartState() expects state mutation when api.commit() is called
|
||
activeState.nextFilter = ListStates.initial;
|
||
activeState.itemLevel = 0;
|
||
activeState.originalToken = null;
|
||
activeState.commentMode = false;
|
||
activeState.openedTag = null;
|
||
activeState.symbolFont = false;
|
||
activeState.listType = null;
|
||
activeState.indentGuesser = CommentHeuristics.indentGuesser();
|
||
activeState.emitter = Emitter(api, api.document);
|
||
activeState.styles = CommentHeuristics.styles();
|
||
activeState.spanCount = [];
|
||
activeState.skippedPara = false;
|
||
activeState.styleLevelAdjust = 0;
|
||
activeState.bulletInfo = undefined;
|
||
};
|
||
|
||
resetActiveState({});
|
||
|
||
var resetState = function(api) {
|
||
resetActiveState(api);
|
||
};
|
||
|
||
var receive = function(api, token) {
|
||
if (activeState.styles.check(token)) {
|
||
return;
|
||
}
|
||
activeState.symbolFont = ListTypes.checkFont(token, activeState.symbolFont);
|
||
activeState.nextFilter(api, activeState, token);
|
||
};
|
||
|
||
return Filter.createFilter(receive, resetState);
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
/**
|
||
* Source code in this file has been taken under a commercial license from tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js
|
||
* Copyright 2009, Moxiecode Systems AB
|
||
*/
|
||
define(
|
||
'ephox.powerpaste.legacy.tinymce.BrowserFilters',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.tinymce.Util'
|
||
],
|
||
|
||
function (Util) {
|
||
var trailingSpaceCharacter = function(content) {
|
||
var h = content;
|
||
// Strip a trailing non-breaking, zero-width space which Firefox tends to insert.
|
||
var hasCrazySpace = h.charCodeAt(h.length - 1) === 65279;
|
||
return hasCrazySpace ?
|
||
h.substring(0, h.length - 1) :
|
||
content;
|
||
};
|
||
|
||
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
|
||
var removeBrNextToBlock = function(content) {
|
||
return (/<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/).test(content) ?
|
||
content.replace(/(?:<br> [\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br> [\s\r\n]+|<br>)*/g, '$1') :
|
||
content;
|
||
};
|
||
|
||
// Replace multiple BR elements with uppercase BR to keep them intact when collapseBr runs
|
||
var capitaliseMultipleBr = function(content) {
|
||
return content.replace(/<br><br>/g, '<BR><BR>');
|
||
};
|
||
|
||
// Replace single br elements with space since they are word wrapping
|
||
var removeSingleBr = function(content) {
|
||
return content.replace(/<br>/g, ' ');
|
||
};
|
||
|
||
// Collapse double brs into a single BR
|
||
var collapseBr = function(content) {
|
||
return content.replace(/<BR><BR>/g, '<br>');
|
||
};
|
||
|
||
var baseFilters = [trailingSpaceCharacter];
|
||
|
||
var filters = (tinymce.isIE && document.documentMode >= 9) ?
|
||
[collapseBr, removeSingleBr, capitaliseMultipleBr, removeBrNextToBlock].concat(baseFilters) :
|
||
baseFilters;
|
||
|
||
var allFilters = Util.compose(filters);
|
||
|
||
return {
|
||
all: allFilters,
|
||
textOnly: trailingSpaceCharacter
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.FilterInlineStyles',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
var removeStyles = /^(mso-.*|tab-stops|tab-interval|language|text-underline|text-effect|text-line-through|font-color|horiz-align|list-image-[0-9]+|separator-image|table-border-color-(dark|light)|vert-align|vnd\..*)$/;
|
||
|
||
var filterFunction = function(styleFilter) {
|
||
return function(name, value) {
|
||
var preserve = false;
|
||
switch (styleFilter) {
|
||
case 'all':
|
||
case '*':
|
||
preserve = true;
|
||
break;
|
||
case 'valid':
|
||
preserve = !removeStyles.test(name);
|
||
break;
|
||
case undefined:
|
||
case 'none':
|
||
preserve = name === 'list-style-type';
|
||
break;
|
||
default:
|
||
preserve = (',' + styleFilter + ',').indexOf(',' + name + ',') >= 0;
|
||
break;
|
||
}
|
||
return preserve ? value : null;
|
||
};
|
||
};
|
||
|
||
return Filter.createFilter(function(api, token) {
|
||
var styleFilter = api.settings.get('retain_style_properties');
|
||
token.filterStyles(filterFunction(styleFilter));
|
||
api.emit(token);
|
||
});
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.InferListTags',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Token) {
|
||
return Filter.createFilter(function(api, token) {
|
||
if (api.seenList) {
|
||
// As soon as we see a UL or OL tag we know we're getting complete lists.
|
||
api.emit(token);
|
||
} else if (api.inferring) {
|
||
if (token.tag() === 'LI') {
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
api.inferring++;
|
||
} else {
|
||
api.inferring--;
|
||
if (!api.inferring) {
|
||
api.needsClosing = true;
|
||
}
|
||
}
|
||
}
|
||
api.emit(token);
|
||
} else {
|
||
if (token.tag() === 'OL' || token.tag() === 'UL') {
|
||
api.seenList = true;
|
||
} else if (token.tag() === 'LI') {
|
||
api.inferring = 1;
|
||
if (!api.needsClosing) {
|
||
api.emit(Token.createStartElement('UL', {}, {}, api.document));
|
||
}
|
||
}
|
||
if (api.needsClosing && !api.inferring && !token.isWhitespace()) {
|
||
api.needsClosing = false;
|
||
api.emit(Token.createEndElement('UL', api.document));
|
||
}
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripBookmarks',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
if (name === 'name' || name === 'id') {
|
||
return null;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripClassAttributes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
var classFilter;
|
||
if (name === 'class') {
|
||
classFilter = this.settings.get('strip_class_attributes');
|
||
switch (classFilter) {
|
||
case 'mso':
|
||
return value.indexOf('Mso') === 0 ? null : value;
|
||
case 'none':
|
||
return value;
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripEmptyInlineElements',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Helper',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Helper, Token) {
|
||
var deferred = [];
|
||
var open = [];
|
||
var hasContent = false;
|
||
|
||
var removeMatchingSpan = function(len, pos) {
|
||
var j, token, opened = 1;
|
||
for (j = pos + 1; j < len; j++) {
|
||
token = deferred[j];
|
||
if (token && token.tag() === 'SPAN') {
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
opened++;
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE) {
|
||
opened--;
|
||
if (opened === 0) {
|
||
// Save reallocating a shorter array, just null it out.
|
||
deferred[j] = null;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
var flushDeferred = function(api) {
|
||
if (hasContent) {
|
||
var tok, len = deferred.length, i;
|
||
for (i = 0; i < len; i++) {
|
||
tok = deferred[i];
|
||
if (!tok) continue;
|
||
if (tok.type() === Token.START_ELEMENT_TYPE && tok.tag() === 'SPAN' && Helper.hasNoAttributes(tok)) {
|
||
// Omit token and find the last end span and remove it too.
|
||
removeMatchingSpan(len, i);
|
||
} else {
|
||
api.emit(tok);
|
||
}
|
||
}
|
||
}
|
||
deferred = [];
|
||
open = [];
|
||
hasContent = false;
|
||
};
|
||
|
||
var internalDefer = function(api, token) {
|
||
deferred.push(token);
|
||
open = open || [];
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
open.push(token);
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE) {
|
||
open.pop();
|
||
if (open.length === 0) {
|
||
// Didn't find anything to keep so dump everything.
|
||
flushDeferred(api, token);
|
||
return;
|
||
}
|
||
}
|
||
};
|
||
|
||
return Filter.createFilter(function(api, token) {
|
||
var inlineTags = ',FONT,EM,STRONG,SAMP,ACRONYM,CITE,CODE,DFN,KBD,TT,B,I,U,S,SUB,SUP,INS,DEL,VAR,SPAN,';
|
||
|
||
deferred = deferred || [];
|
||
|
||
var alwaysKeep = function(token) {
|
||
return !(inlineTags.indexOf(',' + token.tag() + ',') >= 0 && Helper.hasNoAttributes(token, true));
|
||
};
|
||
|
||
if (deferred.length === 0) {
|
||
if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
if (alwaysKeep(token)) {
|
||
api.emit(token);
|
||
} else {
|
||
internalDefer(api, token);
|
||
}
|
||
} else {
|
||
api.emit(token);
|
||
}
|
||
} else {
|
||
if (!hasContent) hasContent = alwaysKeep(token);
|
||
internalDefer(api, token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripEmptyStyleAttributes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
if (name === 'style' && value === '') {
|
||
return null;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripLangAttribute',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
return name === 'lang' ? null : value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripImages',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Token) {
|
||
return Filter.createFilter(function(api, token) {
|
||
if (token.tag() === 'IMG') {
|
||
if (token.type() === Token.END_ELEMENT_TYPE && api.skipEnd) {
|
||
api.skipEnd = false;
|
||
return;
|
||
} else if (token.type() === Token.START_ELEMENT_TYPE) {
|
||
if (/^file:/.test(token.getAttribute('src'))) {
|
||
api.skipEnd = true;
|
||
return;
|
||
} else if (api.settings.get('base_64_images') && /^data:image\/.*;base64/.test(token.getAttribute('src'))) {
|
||
api.skipEnd = true;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
api.emit(token);
|
||
});
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripMetaAndLinkElements',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createFilter(function(api, token) {
|
||
if (token.tag() !== 'META' && token.tag() !== 'LINK') {
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripNoAttributeA',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Helper',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Helper, Token) {
|
||
var keepA = function(token) {
|
||
return !Helper.hasNoAttributes(token) && !/^OLE_LINK/.test(token.getAttribute('name'));
|
||
};
|
||
|
||
var stack = [];
|
||
|
||
return Filter.createFilter(function(api, token) {
|
||
var open;
|
||
if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'A') {
|
||
stack.push(token);
|
||
if (keepA(token)) {
|
||
api.defer(token);
|
||
}
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === 'A') {
|
||
open = stack.pop();
|
||
if (keepA(open)) {
|
||
api.defer(token);
|
||
}
|
||
if (stack.length === 0) {
|
||
api.emitDeferred();
|
||
}
|
||
} else if (api.hasDeferred()) {
|
||
api.defer(token);
|
||
} else {
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripScripts',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter',
|
||
'ephox.powerpaste.legacy.data.tokens.Token'
|
||
],
|
||
|
||
function (Filter, Token) {
|
||
var script = false;
|
||
return Filter.createFilter(function(api, token) {
|
||
if (token.tag() === 'SCRIPT') {
|
||
script = token.type() === Token.START_ELEMENT_TYPE;
|
||
} else if (!script) {
|
||
token.filterAttributes(function(name, value) {
|
||
if (/^on/.test(name) || name === 'language') return null;
|
||
return value;
|
||
});
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.wordimport.CommonFilters',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.filters.FilterInlineStyles',
|
||
'ephox.powerpaste.legacy.filters.InferListTags',
|
||
'ephox.powerpaste.legacy.filters.StripBookmarks',
|
||
'ephox.powerpaste.legacy.filters.StripClassAttributes',
|
||
'ephox.powerpaste.legacy.filters.StripEmptyInlineElements',
|
||
'ephox.powerpaste.legacy.filters.StripEmptyStyleAttributes',
|
||
'ephox.powerpaste.legacy.filters.StripLangAttribute',
|
||
'ephox.powerpaste.legacy.filters.StripImages',
|
||
'ephox.powerpaste.legacy.filters.StripMetaAndLinkElements',
|
||
'ephox.powerpaste.legacy.filters.StripNoAttributeA',
|
||
'ephox.powerpaste.legacy.filters.StripScripts'
|
||
],
|
||
|
||
function (FilterInlineStyles, InferListTags, StripBookmarks, StripClassAttributes, StripEmptyInlineElements, StripEmptyStyleAttributes, StripLangAttribute, StripImages, StripMetaAndLinkElements, StripNoAttributeA, StripScripts) {
|
||
return [
|
||
StripScripts,
|
||
StripBookmarks,
|
||
StripImages,
|
||
FilterInlineStyles,
|
||
StripLangAttribute,
|
||
StripEmptyStyleAttributes,
|
||
StripClassAttributes,
|
||
StripNoAttributeA,
|
||
StripEmptyInlineElements,
|
||
StripMetaAndLinkElements,
|
||
InferListTags
|
||
];
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripFormattingAttributes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createFilter(function(api, token) {
|
||
token.filterAttributes(function(name, value) {
|
||
if (name === 'align') return null;
|
||
if ((token.tag() === 'UL' || token.tag() === 'OL') &&
|
||
name === 'type') return null;
|
||
return value;
|
||
});
|
||
api.emit(token);
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripNamespaceDeclarations',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
if (/^xmlns(:|$)/.test(name)) {
|
||
return null;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripOPTags',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createFilter(function(api, token) {
|
||
if (!token.tag || !/^([OVWXP]|U[0-9]+|ST[0-9]+):/.test(token.tag())) {
|
||
api.emit(token);
|
||
}
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripTocLinks',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
if (name === 'href' && (value.indexOf('#_Toc') >= 0 || value.indexOf('#_mso') >= 0)) {
|
||
return null;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.filters.StripVMLAttributes',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Filter'
|
||
],
|
||
|
||
function (Filter) {
|
||
return Filter.createAttributeFilter(function(name, value) {
|
||
if (/^v:/.test(name)) {
|
||
return null;
|
||
}
|
||
return value;
|
||
});
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.wordimport.WordOnlyFilters',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.filters.StripFormattingAttributes',
|
||
'ephox.powerpaste.legacy.filters.StripNamespaceDeclarations',
|
||
'ephox.powerpaste.legacy.filters.StripOPTags',
|
||
'ephox.powerpaste.legacy.filters.StripTocLinks',
|
||
'ephox.powerpaste.legacy.filters.StripVMLAttributes',
|
||
'ephox.powerpaste.legacy.filters.list.Lists'
|
||
],
|
||
|
||
function (StripFormattingAttributes, StripNamespaceDeclarations, StripOPTags, StripTocLinks, StripVMLAttributes, Lists) {
|
||
return [
|
||
StripOPTags,
|
||
Lists,
|
||
StripTocLinks,
|
||
StripVMLAttributes,
|
||
StripNamespaceDeclarations,
|
||
StripFormattingAttributes
|
||
];
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.legacy.wordimport.WordImport',
|
||
|
||
[
|
||
'ephox.powerpaste.legacy.data.tokens.Serializer',
|
||
'ephox.powerpaste.legacy.data.tokens.Tokenizer',
|
||
'ephox.powerpaste.legacy.filters.Text',
|
||
'ephox.powerpaste.legacy.filters.list.Lists',
|
||
'ephox.powerpaste.legacy.tinymce.BrowserFilters',
|
||
'ephox.powerpaste.legacy.wordimport.CommonFilters',
|
||
'ephox.powerpaste.legacy.wordimport.WordOnlyFilters'
|
||
],
|
||
|
||
function (Serializer, Tokenizer, Text, Lists, BrowserFilters, CommonFilters, WordOnlyFilters) {
|
||
var buildPipeline = function(filters, sink, settings, document) {
|
||
var i, filter = sink;
|
||
for (i = filters.length - 1; i >= 0; i--) {
|
||
//This is calling the function defined by Filter.createFilter().
|
||
//The best description I can come up with is "function composition using CPS".
|
||
//Filters are run in reverse order to the loop, which is reversed so the arrays below define the order.
|
||
//And then the sink comes last (which means it's injected on the first pass of the loop).
|
||
filter = filters[i](filter, settings, document);
|
||
}
|
||
return filter;
|
||
};
|
||
|
||
var runPipeline = function(content, settings, document, requiredFilters) {
|
||
var serializer = Serializer.create(document);
|
||
var tokenizer = Tokenizer.tokenize(content, document);
|
||
pipeline = buildPipeline(requiredFilters, serializer, settings, document);
|
||
while (tokenizer.hasNext()) {
|
||
pipeline.receive(tokenizer.next());
|
||
}
|
||
return serializer.dom;
|
||
};
|
||
|
||
/**
|
||
* Accepts a string of content to filter and returns a filtered DOM structure.
|
||
*
|
||
* @param inputContent the content to filter
|
||
* @param settings the settings object
|
||
* @param document the target document that the content will be inserted into
|
||
* @return a DOM fragment with the filtered content.
|
||
*/
|
||
var filter = function(inputContent, settings, document) {
|
||
var content = BrowserFilters.all(inputContent);
|
||
|
||
var detectedAsWordContent = isWordContent(content);
|
||
settings.setWordContent(detectedAsWordContent);
|
||
|
||
var requiredFilters = CommonFilters;
|
||
if (detectedAsWordContent) {
|
||
requiredFilters = WordOnlyFilters.concat(CommonFilters);
|
||
}
|
||
return runPipeline(content, settings, document, requiredFilters);
|
||
};
|
||
|
||
var filterPlainText = function(inputContent, settings, document) {
|
||
var content = BrowserFilters.textOnly(inputContent);
|
||
|
||
return runPipeline(content, settings, document, [Text]);
|
||
};
|
||
|
||
var isWordContent = function(content) {
|
||
return content.indexOf('<o:p>') >= 0 || // IE, Safari, Opera
|
||
content.indexOf('p.MsoNormal, li.MsoNormal, div.MsoNormal') >= 0 || // Firefox Mac
|
||
content.indexOf('MsoListParagraphCxSpFirst') >= 0 || // Windows list only selection
|
||
content.indexOf('<w:WordDocument>') >= 0; // Firefox Windows
|
||
};
|
||
|
||
return {
|
||
filter: filter,
|
||
filterPlainText: filterPlainText,
|
||
isWordContent: isWordContent
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.LegacyTinyDialog',
|
||
[
|
||
'ephox.powerpaste.legacy.data.Insert',
|
||
'ephox.powerpaste.legacy.tinymce.Settings',
|
||
'ephox.powerpaste.legacy.wordimport.WordImport',
|
||
'global!setTimeout'
|
||
],
|
||
function(Insert, Settings, WordImport, setTimeout){
|
||
return function(editor, translate) {
|
||
var showDialog = function(content){
|
||
|
||
var filterAndInsert = function(importSetting) {
|
||
var data = {content: content};
|
||
|
||
// run pre-filters
|
||
editor.fire('PastePreProcess', data);
|
||
|
||
// run main filter, always strip images
|
||
var settings = Settings.create(importSetting || editor.settings.powerpaste_word_import, importSetting || editor.settings.powerpaste_html_import, true);
|
||
var fragment = WordImport.filter(data.content, settings, editor.getDoc());
|
||
|
||
// run post-filters
|
||
editor.fire('PastePostProcess', fragment);
|
||
//Set undo step
|
||
editor.undoManager.transact(function() {
|
||
// insert the DocumentFragment object into the editor
|
||
Insert.insert(fragment, editor);
|
||
});
|
||
};
|
||
|
||
//introduce the prompt option
|
||
var cleanOrMerge = function(setting) {
|
||
return setting === 'clean' || setting === 'merge';
|
||
};
|
||
|
||
var openDialog =function() {
|
||
var win;
|
||
|
||
var clean = function() {
|
||
win.close();
|
||
filterAndInsert('clean');
|
||
};
|
||
var merge = function() {
|
||
win.close();
|
||
filterAndInsert('merge');
|
||
};
|
||
|
||
var controls = [{
|
||
text: translate('cement.dialog.paste.clean'),
|
||
onclick: clean
|
||
},
|
||
{
|
||
text: translate('cement.dialog.paste.merge'),
|
||
onclick: merge
|
||
}];
|
||
|
||
var winSettings = {
|
||
title: translate('cement.dialog.paste.title'),
|
||
spacing: 10,
|
||
padding: 10,
|
||
items: [{
|
||
type: 'container',
|
||
html: translate('cement.dialog.paste.instructions')
|
||
}],
|
||
buttons: controls
|
||
};
|
||
|
||
win = editor.windowManager.open(winSettings);
|
||
|
||
//IE appears to require that we blur the iframe
|
||
setTimeout(function() {
|
||
if (win) {
|
||
win.getEl().focus();
|
||
}
|
||
}, 1);
|
||
};
|
||
|
||
if (WordImport.isWordContent(content) && !cleanOrMerge(editor.settings.powerpaste_word_import)) {
|
||
openDialog();
|
||
} else if (!cleanOrMerge(editor.settings.powerpaste_html_import)) {
|
||
openDialog();
|
||
} else {
|
||
filterAndInsert();
|
||
}
|
||
|
||
};
|
||
|
||
return {
|
||
showDialog: showDialog
|
||
};
|
||
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.LegacyPowerPaste',
|
||
[
|
||
'ephox.powerpaste.i18n.I18n',
|
||
'ephox.powerpaste.legacy.tinymce.Clipboard',
|
||
'ephox.powerpaste.tinymce.LegacyTinyDialog'
|
||
],
|
||
function (I18n, Clipboard, LegacyTinyDialog) {
|
||
return function (editor, settings) {
|
||
var t = this, onPaste, onKeyDown;
|
||
|
||
var tinyDialog = LegacyTinyDialog(editor, I18n.translate);
|
||
|
||
var handlerAdapter = function(handler) {
|
||
return function(e) {
|
||
handler(e);
|
||
};
|
||
};
|
||
|
||
// Register the getClipboardContent function onpaste and with the magical keyboard shortcuts for browsers that don't support that (Opera & FF2).
|
||
onPaste = Clipboard.getOnPasteFunction(editor, tinyDialog.showDialog);
|
||
editor.on('paste', handlerAdapter(onPaste));
|
||
|
||
onKeyDown = Clipboard.getOnKeyDownFunction(editor, tinyDialog.showDialog);
|
||
editor.on('keydown', handlerAdapter(onKeyDown));
|
||
|
||
editor.addCommand('mceInsertClipboardContent', function(ui, data) {
|
||
tinyDialog.showDialog(data.content || data);
|
||
});
|
||
|
||
if (editor.settings.paste_preprocess) {
|
||
editor.on('PastePreProcess', function(e) {
|
||
editor.settings.paste_preprocess.call(t, t, e);
|
||
});
|
||
}
|
||
|
||
};
|
||
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.epithet.Id',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
|
||
/**
|
||
* Generate a unique identifier.
|
||
*
|
||
* The unique portion of the identifier only contains an underscore
|
||
* and digits, so that it may safely be used within HTML attributes.
|
||
*
|
||
* The chance of generating a non-unique identifier has been minimized
|
||
* by combining the current time, a random number and a one-up counter.
|
||
*
|
||
* @param {string} prefix Prepended to the identifier
|
||
* @return {string} Unique identifier
|
||
*/
|
||
var unique = 0;
|
||
|
||
var generate = function (prefix) {
|
||
var date = new Date();
|
||
var time = date.getTime();
|
||
var random = Math.floor(Math.random() * 1000000000);
|
||
|
||
unique++;
|
||
|
||
return prefix + "_" + random + unique + String(time);
|
||
};
|
||
|
||
return {
|
||
generate: generate
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.DeviceType',
|
||
|
||
[
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Fun) {
|
||
return function(osObj, browserObj, userAgent) {
|
||
var isiPad = osObj.isiOS() && ( userAgent.search(/iPad/i) !== -1 );
|
||
var isiPhone = osObj.isiOS() && !isiPad;
|
||
var isAndroid3 = osObj.isAndroid() && ( osObj.version.major === 3 );
|
||
var isAndroid4 = osObj.isAndroid() && ( osObj.version.major === 4 );
|
||
var isTablet = isiPad || isAndroid3 || ( isAndroid4 && userAgent.search(/mobile/i) === -1 );
|
||
var isTouch = ( osObj.isiOS() || osObj.isAndroid() );
|
||
var isPhone = isTouch && !isTablet;
|
||
|
||
return {
|
||
isiPad : Fun.constant(isiPad),
|
||
isiPhone: Fun.constant(isiPhone),
|
||
isTablet: Fun.constant(isTablet),
|
||
isPhone: Fun.constant(isPhone),
|
||
isTouch: Fun.constant(isTouch),
|
||
isAndroid: osObj.isAndroid,
|
||
isiOS: osObj.isiOS
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.Platform',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var create = function(browserName, browserVersion, osName) {
|
||
return {
|
||
browser: {
|
||
current: browserName,
|
||
version: browserVersion
|
||
},
|
||
os: {
|
||
current: osName
|
||
}
|
||
};
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.GetterHelper',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var getter = function(value) {
|
||
return function() {
|
||
return value;
|
||
};
|
||
};
|
||
|
||
// Adapter between settings like IsMSIE and functions like EditLiveJava.browser.isIE()
|
||
var attachGetters = function(scope, current, options) {
|
||
for (var i = 0; i < options.length; i++) {
|
||
scope['is' + options[i].name] = getter(options[i].name === current);
|
||
}
|
||
};
|
||
|
||
return {
|
||
getter: getter,
|
||
attachGetters: attachGetters
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.Result',
|
||
|
||
[
|
||
'ephox.fred.core.GetterHelper'
|
||
],
|
||
|
||
function (GetterHelper) {
|
||
var create = function(options, current, version) {
|
||
//-------------------------------------------------------------------------
|
||
var attachGetters = GetterHelper.attachGetters;
|
||
//-------------------------------------------------------------------------
|
||
|
||
var result = {};
|
||
result.current = current;
|
||
result.version = version;
|
||
attachGetters(result, result.current, options);
|
||
return result;
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.SearchInfo',
|
||
|
||
[
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (V) {
|
||
var contains = V.contains;
|
||
var checkContains = function(x) {
|
||
return function(uastring) {
|
||
return contains(uastring, x);
|
||
};
|
||
};
|
||
|
||
var chromeFrameChecker = function() {
|
||
try {
|
||
var i = new ActiveXObject('ChromeTab.ChromeFrame');
|
||
return !!i;
|
||
} catch(e) {
|
||
return false;
|
||
}
|
||
};
|
||
|
||
var normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/;
|
||
|
||
var create = function(isChromeFrameEnabled) {
|
||
var browsers = [
|
||
{
|
||
name : 'Spartan',
|
||
versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],
|
||
search: function(uastring) {
|
||
var monstrosity = contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit');
|
||
return monstrosity;
|
||
}
|
||
},
|
||
{
|
||
name : 'ChromeFrame',
|
||
versionRegexes: [/.*?chromeframe\/([0-9]+)\.([0-9]+).*/, normalVersionRegex],
|
||
search: function(uastring) {
|
||
return contains(uastring, 'chromeframe') ? isChromeFrameEnabled() : false;
|
||
}
|
||
},
|
||
{
|
||
name : 'Chrome',
|
||
versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/, normalVersionRegex],
|
||
search : function(uastring) {
|
||
return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe');
|
||
}
|
||
},
|
||
{
|
||
name : 'IE',
|
||
versionRegexes: [/.*?msie\ ?([0-9]+)\.([0-9]+).*/, /.*?rv:([0-9]+)\.([0-9]+).*/],
|
||
search: function(uastring) {
|
||
var containsIE = (contains(uastring, 'msie') || contains(uastring, 'trident'));
|
||
var containsChromeFrame = contains(uastring, 'chromeframe');
|
||
return containsChromeFrame ? containsIE && !isChromeFrameEnabled() : containsIE;
|
||
}
|
||
},
|
||
{
|
||
name : 'Opera',
|
||
versionRegexes: [normalVersionRegex, /.*?opera\/([0-9]+)\.([0-9]+).*/],
|
||
search : checkContains('opera')
|
||
},
|
||
{
|
||
name : 'Firefox',
|
||
versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],
|
||
search : checkContains('firefox')
|
||
},
|
||
{
|
||
name : 'Safari',
|
||
versionRegexes: [normalVersionRegex],
|
||
search : checkContains('safari')
|
||
},
|
||
{
|
||
name : 'Envjs',
|
||
versionRegexes: [/.*?envjs\/\ ?([0-9]+)\.([0-9]+).*/],
|
||
search : checkContains('envjs')
|
||
}
|
||
];
|
||
|
||
var oses = [
|
||
{
|
||
name : 'Windows',
|
||
search : checkContains('win'),
|
||
versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]
|
||
},
|
||
{
|
||
name : 'iOS',
|
||
search : function(uastring) {
|
||
return contains(uastring, 'iphone') || contains(uastring, 'ipad');
|
||
},
|
||
versionRegexes: [/.*?version\/\ ?([0-9]+)\.([0-9]+).*/]
|
||
},
|
||
{
|
||
name : 'Android',
|
||
search : checkContains('android'),
|
||
versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/]
|
||
},
|
||
{
|
||
name : 'OSX',
|
||
search : checkContains('os x'),
|
||
versionRegexes: [/.*?os\ x\ ?([0-9]+)_([0-9]+).*/]
|
||
},
|
||
{
|
||
name : 'Linux',
|
||
search : checkContains('linux')
|
||
},
|
||
{ name : 'Solaris',
|
||
search : checkContains('sunos')
|
||
},
|
||
{
|
||
name : 'FreeBSD',
|
||
search : checkContains('freebsd')
|
||
}
|
||
];
|
||
|
||
|
||
return {
|
||
browsers: browsers,
|
||
oses: oses
|
||
};
|
||
};
|
||
|
||
return {
|
||
create: create,
|
||
chromeFrameChecker: chromeFrameChecker
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.SpecTester',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var meetsSpec = function(spec, browserVersion) {
|
||
var t = typeof spec;
|
||
if (t === 'boolean') {
|
||
return !! spec;
|
||
} else if (t === 'object') {
|
||
var min = spec.minimum;
|
||
return browserVersion.major > min.major || (browserVersion.major === min.major && browserVersion.minor >= min.minor);
|
||
}
|
||
|
||
throw('invalid spec');
|
||
};
|
||
|
||
return {
|
||
meetsSpec: meetsSpec
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.Fn',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var findOneInArrayOr = function(array, theDefault, predicate) {
|
||
for (var i = 0; i < array.length; i++) {
|
||
var x = array[i];
|
||
if (predicate(x, i, array)) return x;
|
||
}
|
||
return theDefault;
|
||
};
|
||
|
||
return {
|
||
findOneInArrayOr: findOneInArrayOr
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.UaStringDetector',
|
||
|
||
[
|
||
'ephox.fred.core.Fn'
|
||
],
|
||
|
||
function (Fn) {
|
||
var detect = function(scope, userAgent) {
|
||
//-------------------------------------------------------------------------
|
||
var findOneInArrayOr = Fn.findOneInArrayOr;
|
||
//-------------------------------------------------------------------------
|
||
|
||
var agent = String(userAgent).toLowerCase();
|
||
return findOneInArrayOr(scope, {name: undefined}, function(x) {
|
||
return x.search(agent);
|
||
});
|
||
};
|
||
|
||
return {
|
||
detect: detect
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.core.VersionDetector',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var detectVersion = function(currentBrowser, agent) {
|
||
function firstMatch(regexes, s) {
|
||
for (var i = 0; i < regexes.length; i++) {
|
||
var x = regexes[i];
|
||
if (x.test(s)) return x;
|
||
}
|
||
return undefined;
|
||
}
|
||
|
||
function find(regexes, agent) {
|
||
var r = firstMatch(regexes, agent);
|
||
if (!r) return { major : 0, minor : 0 };
|
||
var group = function(i) {
|
||
return Number(agent.replace(r, '$' + i));
|
||
};
|
||
return {
|
||
major : group(1),
|
||
minor : group(2)
|
||
};
|
||
}
|
||
|
||
var cleanedAgent = String(agent).toLowerCase();
|
||
var versionRegexes = currentBrowser.versionRegexes;
|
||
|
||
if (!versionRegexes) return { major : 0, minor : 0 };
|
||
return find(versionRegexes, cleanedAgent);
|
||
};
|
||
|
||
return {
|
||
detectVersion: detectVersion
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fred.PlatformDetection',
|
||
|
||
[
|
||
'ephox.fred.core.DeviceType',
|
||
'ephox.fred.core.Platform',
|
||
'ephox.fred.core.Result',
|
||
'ephox.fred.core.SearchInfo',
|
||
'ephox.fred.core.SpecTester',
|
||
'ephox.fred.core.UaStringDetector',
|
||
'ephox.fred.core.VersionDetector'
|
||
],
|
||
|
||
function (DeviceType, Platform, Result, SearchInfo, SpecTester, UaStringDetector, VersionDetector) {
|
||
//-------------------------------------------------------------------------
|
||
var detectVersion = VersionDetector.detectVersion;
|
||
var makeResult = Result.create;
|
||
var meetsSpec = SpecTester.meetsSpec;
|
||
var detectFromUaString = UaStringDetector.detect;
|
||
//-------------------------------------------------------------------------
|
||
|
||
var isSupported = function(supportMatrix, os, browser, browserVersion) {
|
||
if (!!(supportMatrix[os])) {
|
||
if (!!(supportMatrix[os][browser])) {
|
||
return meetsSpec(supportMatrix[os][browser], browserVersion);
|
||
} else {
|
||
return !!(supportMatrix[os]["All"]);
|
||
}
|
||
} else {
|
||
return false;
|
||
}
|
||
};
|
||
|
||
var isSupportedPlatform = function(matrix, platform) {
|
||
var browser = platform.browser;
|
||
var os = platform.os;
|
||
return isSupported(matrix, os.current, browser.current, browser.version);
|
||
};
|
||
|
||
var doDetect = function(userAgent, chromeFrameChecker) {
|
||
var si = SearchInfo.create(chromeFrameChecker);
|
||
|
||
var browsers = si.browsers;
|
||
var oses = si.oses;
|
||
|
||
var os = detectFromUaString(oses, userAgent);
|
||
var osName = os.name;
|
||
var osVersion = detectVersion(os, userAgent);
|
||
|
||
var browser = detectFromUaString(browsers, userAgent);
|
||
var browserName = browser.name;
|
||
var browserVersion = detectVersion(browser, userAgent);
|
||
|
||
|
||
var osObj = makeResult(oses, osName, osVersion);
|
||
var browserObj = makeResult(browsers, browserName, browserVersion);
|
||
|
||
var deviceType = DeviceType(osObj, browserObj, userAgent);
|
||
|
||
var supported = function(supportMatrix) {
|
||
return isSupported(supportMatrix, osName, browserName, browserVersion);
|
||
};
|
||
|
||
return {
|
||
browser : browserObj,
|
||
os : osObj,
|
||
deviceType: deviceType,
|
||
isSupported : supported
|
||
};
|
||
};
|
||
|
||
var detect = function() {
|
||
return doDetect(navigator.userAgent, SearchInfo.chromeFrameChecker);
|
||
};
|
||
|
||
return {
|
||
Platform: Platform,
|
||
detect: detect,
|
||
doDetect: doDetect,
|
||
isSupported: isSupported,
|
||
isSupportedPlatform: isSupportedPlatform
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.FileReader',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE10 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/FileReader
|
||
*/
|
||
return function () {
|
||
var f = Global.getOrDie('FileReader');
|
||
return new f();
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.hermes.utils.ImageExtract',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.epithet.Id',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.hermes.api.ImageAsset',
|
||
'ephox.numerosity.api.FileReader',
|
||
'ephox.numerosity.api.URL',
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Arr, Id, PlatformDetection, ImageAsset, FileReader, URL, Struct) {
|
||
var blobStruct = Struct.immutable('id', 'obj', 'objurl');
|
||
|
||
var blob = function (obj) {
|
||
return blobStruct(Id.generate('image'), obj, URL.createObjectURL(obj));
|
||
};
|
||
|
||
var platform = PlatformDetection.detect();
|
||
|
||
/**
|
||
* Converts a list of blobs into a list of ImageAssets. This is
|
||
* asynchronous. The assets are passed to the callback
|
||
* @param blobs the list of blobs
|
||
* @param callback the callback function for the {ephox.hermes.asset.ImageAsset[]}
|
||
*/
|
||
var fromBlobs = function (blobs, callback) {
|
||
// edge case: where a drop of a non-file takes place
|
||
if (blobs.length === 0) {
|
||
callback([]);
|
||
return;
|
||
}
|
||
|
||
var processed = [];
|
||
|
||
var addAsset = function (asset) {
|
||
processed.push(asset);
|
||
if (processed.length === blobs.length) callback(processed);
|
||
};
|
||
|
||
var readBlob = function (blobData, f) {
|
||
var fr = FileReader();
|
||
fr.onload = function(e) {
|
||
f(blobData, e.target);
|
||
};
|
||
fr.readAsDataURL(blobData);
|
||
};
|
||
|
||
Arr.each(blobs, function (b) {
|
||
var uuid = b.id();
|
||
var blobData = b.obj();
|
||
var objurl = b.objurl();
|
||
readBlob(blobData, function (b, data) {
|
||
var asset = ImageAsset.blob(uuid, b, objurl, data);
|
||
addAsset(asset);
|
||
});
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Converts a list of files into a list of ImageAssets. This is
|
||
* asynchronous. The assets are passed to the callback
|
||
* @param files the list of files
|
||
* @param callback the callback function for the {ephox.hermes.asset.ImageAsset[]}
|
||
*/
|
||
var toAssets = function (files, callback) {
|
||
var blobs = Arr.map(files, function (file) {
|
||
return blob(file);
|
||
});
|
||
|
||
fromBlobs(blobs, callback);
|
||
};
|
||
|
||
var toFiles = function (event) {
|
||
return event.raw().target.files || event.raw().dataTransfer.files;
|
||
};
|
||
|
||
// The following functions are browser-specific ways of determining
|
||
// whether a drag event contains files from the FILE SYSTEM and
|
||
// NOT files being dragged from the host page
|
||
var isFilesIe = function (types) {
|
||
return types.length === 1 && Arr.contains(types, 'Files');
|
||
};
|
||
|
||
var isFilesFF = function (types) {
|
||
return !Arr.contains(types, 'text/_moz_htmlcontext');
|
||
};
|
||
|
||
var isFilesWebkit = function (types) {
|
||
return Arr.contains(types, 'Files');
|
||
};
|
||
|
||
var isFilesOthers = function (types) {
|
||
// just assume we have files. It will just show a different
|
||
// drop container and the browser will do nothing
|
||
return true;
|
||
};
|
||
|
||
var fileDetection = function(){
|
||
if( platform.browser.isChrome()
|
||
|| platform.browser.isSafari()
|
||
|| platform.browser.isOpera() ) {
|
||
return isFilesWebkit;
|
||
}else if( platform.browser.isFirefox() ) {
|
||
return isFilesFF;
|
||
}else if( platform.browser.isIE() ) {
|
||
return isFilesIe;
|
||
}
|
||
return isFilesOthers;
|
||
};
|
||
|
||
var isFiles = fileDetection();
|
||
|
||
/**
|
||
* Maps raw global!Image objects to internal image assets,
|
||
* suitable for adding to a gallery or the editor socket.
|
||
* @param [global!Image] images to process
|
||
* @return {ephox.hermes.asset.ImageAsset[]} mapped image assets
|
||
*/
|
||
var fromImages = function (images) {
|
||
return Arr.map(images, function (img) {
|
||
var uuid = Id.generate('image');
|
||
return ImageAsset.url(uuid, img.src, img);
|
||
});
|
||
};
|
||
|
||
return {
|
||
blob: blob,
|
||
toAssets: toAssets,
|
||
toFiles: toFiles,
|
||
isFiles: isFiles,
|
||
fromImages: fromImages,
|
||
fromBlobs: fromBlobs
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.hermes.api.ImageExtract',
|
||
|
||
[
|
||
'ephox.hermes.utils.ImageExtract'
|
||
],
|
||
|
||
function (ImageExtract) {
|
||
/**
|
||
* Converts a list of files into a list of ImageAssets. This is
|
||
* asynchronous. The assets are passed to the callback
|
||
* @param files the list of files
|
||
* @param callback the callback function for the {ephox.hermes.asset.ImageAsset[]}
|
||
*/
|
||
var toAssets = function (files, callback) {
|
||
return ImageExtract.toAssets(files, callback);
|
||
};
|
||
|
||
/**
|
||
* Converts a list of blobs into a list of ImageAssets. This is
|
||
* asynchronous. The assets are passed to the callback
|
||
* @param blobs the list of blobs
|
||
* @param callback the callback function for the {ephox.hermes.asset.ImageAsset[]}
|
||
*/
|
||
var fromBlobs = function (blobs, callback) {
|
||
return ImageExtract.fromBlobs(blobs, callback);
|
||
};
|
||
|
||
return {
|
||
toAssets: toAssets,
|
||
fromBlobs: fromBlobs,
|
||
blob: ImageExtract.blob
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.ModernPowerDrop',
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.hermes.api.ImageAsset',
|
||
'ephox.hermes.api.ImageExtract',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'global!tinymce'
|
||
],
|
||
function (Arr, ImageAsset, ImageExtract, Attr, Element, tinymce) {
|
||
return function (editor, url, settings, uploader) {
|
||
|
||
var draggingInternally, imageRegex = /^image\/(jpe?g|png|gif|bmp)$/i;
|
||
|
||
editor.on('dragstart dragend', function(e) {
|
||
draggingInternally = e.type === 'dragstart';
|
||
});
|
||
|
||
editor.on('dragover dragend dragleave', function(e) {
|
||
e.preventDefault();
|
||
});
|
||
|
||
var extractImages = function (event) {
|
||
|
||
//TODO: make this more explicit why it's been done
|
||
var files = event.target.files || event.dataTransfer.files;
|
||
|
||
return Arr.filter(files, function (file) {
|
||
//If the file type tests as an image
|
||
return imageRegex.test(file.type);
|
||
});
|
||
|
||
};
|
||
|
||
var stringifyImages = function (assets) {
|
||
|
||
return Arr.map(assets, function (asset) {
|
||
|
||
var image = Element.fromTag('img');
|
||
|
||
var src = ImageAsset.cata(asset,
|
||
//If we're using PP's uploader, return the blob url, otherwise the data url
|
||
uploader.getLocalURL,
|
||
function (id, url, raw) { return url; }
|
||
);
|
||
|
||
Attr.set(image, 'src', src);
|
||
|
||
return image.dom().outerHTML;
|
||
|
||
}).join('');
|
||
|
||
};
|
||
|
||
var processImages = function (images) {
|
||
|
||
ImageExtract.toAssets(images, function (assets) {
|
||
|
||
var stringHTML = stringifyImages(assets);
|
||
|
||
//Content insertion
|
||
editor.insertContent(stringHTML, {merge: editor.settings.paste_merge_formats !== false});
|
||
|
||
uploader.uploadImages(assets);
|
||
|
||
});
|
||
|
||
};
|
||
|
||
editor.on('drop', function (e) {
|
||
|
||
//If we're not dragging from within tiny
|
||
if (!draggingInternally) {
|
||
|
||
//If RangeUtils are exposed (4.2)
|
||
if (tinymce.dom.RangeUtils && tinymce.dom.RangeUtils.getCaretRangeFromPoint) {
|
||
|
||
var rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(e.clientX, e.clientY, editor.getDoc());
|
||
|
||
//Set selection to where the mouse is pointing
|
||
if (rng) editor.selection.setRng(rng);
|
||
|
||
}
|
||
|
||
var images = extractImages(e);
|
||
|
||
if (images.length > 0) processImages(images);
|
||
|
||
e.preventDefault();
|
||
|
||
}
|
||
|
||
});
|
||
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.Blob',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* IE10 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||
*/
|
||
return function (parts, properties) {
|
||
var f = Global.getOrDie('Blob');
|
||
return new f(parts, properties);
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.numerosity.api.Uint8Array',
|
||
|
||
[
|
||
'ephox.numerosity.core.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
/*
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/Uint8Array
|
||
*
|
||
* IE10 and above per
|
||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
|
||
*/
|
||
return function (arr) {
|
||
var f = Global.getOrDie('Uint8Array');
|
||
return new f(arr);
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!parseInt", [], function () { return parseInt; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bowerbird.codes.HexToBlob',
|
||
|
||
[
|
||
'ephox.numerosity.api.Blob',
|
||
'ephox.numerosity.api.Uint8Array',
|
||
'global!Array',
|
||
'global!Math',
|
||
'global!String',
|
||
'global!parseInt'
|
||
],
|
||
|
||
function (Blob, Uint8Array, Array, Math, String, parseInt) {
|
||
// This function is an unholy combination of two Stack Overflow posts:
|
||
// http://stackoverflow.com/questions/3745666/how-to-convert-from-hex-to-ascii-in-javascript
|
||
// http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
|
||
|
||
// This all hinges on each hex digit being two characters.
|
||
// Bytes are only 0-255 (hex 0-FF) though, so that's a pretty safe assumption.
|
||
var toBytes = function (hex) {
|
||
// Pre-allocating the array speeds things up
|
||
var parts = new Array(hex.length / 2);
|
||
for (var i = 0; i < hex.length; i += 2) {
|
||
var h = hex.substr(i, 2);
|
||
|
||
var index = Math.floor(i / 2); // Chrome works with indexes like 1.5, but I don't trust JS
|
||
parts[index] = parseInt(h, 16);
|
||
}
|
||
return parts;
|
||
};
|
||
|
||
var convert = function (input, contentType) {
|
||
if (input.length === 0) throw 'Zero length content passed to Hex conversion';
|
||
|
||
var byteNumbers = toBytes(input);
|
||
var byteArray = Uint8Array(byteNumbers);
|
||
return Blob([byteArray], {type: contentType});
|
||
};
|
||
|
||
return {
|
||
convert: convert
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bowerbird.core.Species',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Fun, Option) {
|
||
var PICT_SIG = '{\\pict{';
|
||
var PICT_ID_REF = 'i';
|
||
var SHP_SIG = '{\\shp{';
|
||
var SHP_ID_REF = 's';
|
||
|
||
var getIndex = function (match, str, idx) {
|
||
return str.indexOf(match, idx);
|
||
};
|
||
|
||
var common = function (start, end, bower, regex, idRef) {
|
||
if (start === -1 || end === -1) return Option.none();
|
||
else return Option.some({
|
||
start: Fun.constant(start),
|
||
end: Fun.constant(end),
|
||
bower: bower, // I wish we had types
|
||
regex: Fun.constant(regex),
|
||
idRef: Fun.constant(idRef)
|
||
});
|
||
};
|
||
|
||
var extractBower = function (str, start, end) {
|
||
return function () {
|
||
return str.substring(start, end);
|
||
};
|
||
};
|
||
|
||
var endBracket = function (str, idx) {
|
||
var nesting = 0;
|
||
var max = str.length;
|
||
var open, close;
|
||
do {
|
||
open = str.indexOf('{', idx);
|
||
close = str.indexOf('}', idx);
|
||
if (close > open && open !== -1) {
|
||
// is open bracket {
|
||
idx = open + 1;
|
||
++nesting;
|
||
} else if ((open > close || open < 0) && close !== -1) {
|
||
// is closed bracket }
|
||
idx = close + 1;
|
||
--nesting;
|
||
}
|
||
if (idx > max || close === -1) return -1;
|
||
} while (nesting > 0);
|
||
return idx;
|
||
};
|
||
|
||
var pict = function (str, idx, start) {
|
||
var end = start === -1 ? start : endBracket(str, start);
|
||
var bower = extractBower(str, start, end);
|
||
var hexRegex = /([a-hA-H0-9]+)\}$/;
|
||
return common(start, end, bower, hexRegex, PICT_ID_REF);
|
||
};
|
||
|
||
var shp = function (str, idx, start) {
|
||
var end = start === -1 ? start : endBracket(str, start);
|
||
var bower = extractBower(str, start, end);
|
||
// On windows or ie there is this unique object which is 32bits long that we don't want to capture
|
||
// {\*\blipuid 2c98da434043a6e60b0c2f43b22569bc}
|
||
// there are ideas for optimising the capture which will make this regex redundant see TBIO-3070
|
||
var hexRegex = /([a-hA-H0-9]{64,})(?:\}.*)/;
|
||
return common(start, end, bower, hexRegex, SHP_ID_REF);
|
||
};
|
||
|
||
// Can identify 2 types of embedded RTF images, pict & shp, if we find more this needs to become generic.
|
||
// It's complex because this is now a parser and either can come first.
|
||
var identify = function (str, idx) {
|
||
var pictStart = getIndex(PICT_SIG, str, idx);
|
||
var shpStart = getIndex(SHP_SIG, str, idx);
|
||
|
||
if (pictStart === -1 && shpStart === -1) return Option.none();
|
||
else if (pictStart === -1) return shp(str, idx, shpStart);
|
||
else if (shpStart === -1) return pict(str, idx, pictStart);
|
||
else if (pictStart < shpStart) return pict(str, idx, pictStart);
|
||
else if (shpStart < pictStart) return shp(str, idx, shpStart);
|
||
else return Option.none(); // this should never happen, but just in case
|
||
};
|
||
|
||
return {
|
||
identify: identify,
|
||
endBracket: endBracket
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bowerbird.core.Rtf',
|
||
|
||
[
|
||
'ephox.bowerbird.codes.HexToBlob',
|
||
'ephox.bowerbird.core.Species',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (HexToBlob, Species, Option) {
|
||
|
||
var nextBower = function (s, currentIndex) {
|
||
// the next nest of info that contains all the data about an image
|
||
return Species.identify(s, currentIndex);
|
||
};
|
||
|
||
var extractContentType = function (pict) {
|
||
//
|
||
// The following picture types are ignored at the moment:
|
||
// \emfblip, \macpict, \pmmetafileN, \wmetafileN, \dibitmapN, \wbitmapN.
|
||
//
|
||
if (pict.indexOf('\\pngblip') >= 0) return Option.some('image/png');
|
||
else if (pict.indexOf('\\jpegblip') >= 0) return Option.some('image/jpeg');
|
||
else return Option.none();
|
||
};
|
||
|
||
var isValidHex = function (s) {
|
||
return s.length % 2 === 0;
|
||
};
|
||
|
||
var removeCrLf = function (s) {
|
||
return s.replace(/\r/g, '').replace(/\n/g, '');
|
||
};
|
||
|
||
var extractOnlyGroup = function (s, regex) {
|
||
var result = s.match(regex);
|
||
return result !== null ? Option.some(result[1]) : Option.none();
|
||
};
|
||
|
||
var extractHex = function (bower, regex) {
|
||
return extractOnlyGroup(bower, regex).filter(isValidHex);
|
||
};
|
||
|
||
var extractId = function (pict) {
|
||
var re = /\\shplid(\d+)/;
|
||
return extractOnlyGroup(pict, re);
|
||
};
|
||
|
||
var imageFromData = function (data) {
|
||
// extract a nest of data
|
||
var bower = data.bower();
|
||
var regex = data.regex();
|
||
return extractId(bower).bind(function (id) {
|
||
return extractContentType(bower).bind(function (contentType) {
|
||
return extractHex(bower, regex).map(function (hex) {
|
||
return {
|
||
'id': data.idRef() + id,
|
||
'contentType': contentType,
|
||
'blob' : HexToBlob.convert(hex, contentType)
|
||
};
|
||
});
|
||
});
|
||
});
|
||
};
|
||
|
||
var extractBowers = function (rtf) {
|
||
var images = [];
|
||
var length = function () {
|
||
return rtf.length; // break
|
||
};
|
||
|
||
var addImage = function (data) {
|
||
var image = imageFromData(data);
|
||
image.each(function (image) {
|
||
images.push(image);
|
||
});
|
||
return data.end() + 1;
|
||
};
|
||
|
||
var currentIndex = 0;
|
||
while (currentIndex < rtf.length) {
|
||
currentIndex = nextBower(rtf, currentIndex).fold(length, addImage);
|
||
}
|
||
return images;
|
||
};
|
||
|
||
var images = function (rtf) {
|
||
return extractBowers(removeCrLf(rtf));
|
||
};
|
||
|
||
return {
|
||
nextBower: nextBower,
|
||
extractId: extractId,
|
||
extractContentType: extractContentType,
|
||
extractHex: extractHex,
|
||
images: images
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bowerbird.api.Rtf',
|
||
|
||
[
|
||
'ephox.bowerbird.core.Rtf'
|
||
],
|
||
|
||
function (Rtf) {
|
||
var images = function (rtf) {
|
||
return Rtf.images(rtf);
|
||
};
|
||
|
||
return {
|
||
images: images
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.Correlation',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.hermes.api.ImageExtract',
|
||
'ephox.perhaps.Option',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Class'
|
||
],
|
||
|
||
function (Arr, ImageExtract, Option, Struct, Attr, Class) {
|
||
var rtfData = Struct.immutable('id', 'url');
|
||
|
||
var byCode = function (image, unique, _index) {
|
||
var candidate = Arr.find(unique, function (u) {
|
||
return Attr.get(image, 'data-image-id') === u.id();
|
||
});
|
||
|
||
return Option.from(candidate);
|
||
};
|
||
|
||
var byPosition = function (image, unique, index) {
|
||
return Option.from(unique[index]);
|
||
};
|
||
|
||
var converters = {
|
||
local: byPosition,
|
||
code: byCode
|
||
};
|
||
|
||
var convert = function (images, imageData, callback) {
|
||
var unique = [];
|
||
|
||
// create the blob struct from the RTF data, and keep a record of unique ID -> url
|
||
var blobs = Arr.bind(imageData, function (i) {
|
||
var isUnique = !Arr.exists(unique, function (d) { return d.id() === i.id; });
|
||
|
||
if (isUnique) {
|
||
var blob = ImageExtract.blob(i.blob);
|
||
|
||
unique.push(rtfData(i.id, blob.objurl()));
|
||
|
||
return [blob];
|
||
} else {
|
||
return [];
|
||
}
|
||
});
|
||
|
||
// load the blob data before updating the document and then firing the callback
|
||
ImageExtract.fromBlobs(blobs, function (assets) {
|
||
|
||
Arr.each(images, function (image, i) {
|
||
// TODO: Standardise the names of these data attributes. Probably namespaced.
|
||
var type = Attr.get(image, 'data-image-type');
|
||
var converter = converters[type] !== undefined ? converters[type] : Option.none;
|
||
converter(image, unique, i).each(function (candidate) {
|
||
Attr.set(image, 'src', candidate.url());
|
||
});
|
||
Class.remove(image, 'rtf-data-image');
|
||
Attr.remove(image, 'data-image-type');
|
||
Attr.remove(image, 'data-image-id');
|
||
});
|
||
|
||
callback(assets);
|
||
});
|
||
};
|
||
|
||
return {
|
||
convert: convert
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.style.Styles',
|
||
|
||
[
|
||
'ephox.flour.style.Resolver'
|
||
],
|
||
|
||
function (Resolver) {
|
||
var styles = Resolver.create('ephox-cement');
|
||
|
||
return {
|
||
resolve: styles.resolve
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.HelpCopy',
|
||
|
||
[
|
||
'ephox.cement.style.Styles',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.InsertAll'
|
||
],
|
||
|
||
function (Styles, PlatformDetection, Class, Element, InsertAll) {
|
||
|
||
var modifierKey = function () {
|
||
var platform = PlatformDetection.detect();
|
||
var mac = platform.os.isOSX();
|
||
return mac ? ['\u2318'] : ['Ctrl'];
|
||
};
|
||
|
||
var pressEscape = function (translations) {
|
||
return Element.fromHtml(
|
||
'<p>' + translations('cement.dialog.flash.press-escape') + '</p>'
|
||
);
|
||
};
|
||
|
||
var paste = function (translations) {
|
||
var container = Element.fromTag('div');
|
||
Class.add(container, Styles.resolve('flashbin-helpcopy'));
|
||
|
||
var key = modifierKey();
|
||
|
||
var instructions = Element.fromHtml(
|
||
'<p>' + translations('cement.dialog.flash.trigger-paste') + '</p>');
|
||
var kbd = Element.fromHtml(
|
||
'<div>' +
|
||
'<span class="ephox-polish-help-kbd">' +
|
||
key +
|
||
'</span>' +
|
||
' + ' +
|
||
'<span class="ephox-polish-help-kbd">V</span>' +
|
||
'</div>'
|
||
);
|
||
|
||
Class.add(kbd, Styles.resolve('flashbin-helpcopy-kbd'));
|
||
|
||
InsertAll.append(container, [ instructions, kbd, pressEscape(translations) ]);
|
||
|
||
return container;
|
||
};
|
||
|
||
var noflash = function (translations) {
|
||
var container = Element.fromTag('div');
|
||
Class.add(container, Styles.resolve('flashbin-helpcopy'));
|
||
|
||
var instructions = Element.fromHtml(
|
||
'<p>' + translations('cement.dialog.flash.missing') + '</p>'
|
||
);
|
||
|
||
InsertAll.append(container, [ instructions, pressEscape(translations) ]);
|
||
|
||
return container;
|
||
};
|
||
|
||
var indicator = function (translations) {
|
||
var loading = Element.fromTag('div');
|
||
Class.add(loading, Styles.resolve('flashbin-loading'));
|
||
|
||
var spinner = Element.fromTag('div');
|
||
Class.add(spinner, Styles.resolve('flashbin-loading-spinner'));
|
||
|
||
var loadNote = Element.fromTag('p');
|
||
loadNote.dom().innerHTML = translations('loading.wait');
|
||
|
||
InsertAll.append(loading, [spinner, loadNote]);
|
||
|
||
return loading;
|
||
};
|
||
|
||
return {
|
||
paste: paste,
|
||
noflash: noflash,
|
||
indicator: indicator
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!window", [], function () { return window; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Css',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Obj',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Body',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.violin.Strings',
|
||
'global!Error',
|
||
'global!console',
|
||
'global!window'
|
||
],
|
||
|
||
function (Type, Obj, Option, Attr, Body, Element, Strings, Error, console, window) {
|
||
var internalSet = function (dom, property, value) {
|
||
// This is going to hurt. Apologies.
|
||
// JQuery coerces numbers to pixels for certain property names, and other times lets numbers through.
|
||
// we're going to be explicit; strings only.
|
||
if (!Type.isString(value)) {
|
||
console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom);
|
||
throw new Error('CSS value must be a string: ' + value);
|
||
}
|
||
|
||
// removed: support for dom().style[property] where prop is camel case instead of normal property name
|
||
dom.style.setProperty(property, value);
|
||
};
|
||
|
||
var set = function (element, property, value) {
|
||
var dom = element.dom();
|
||
internalSet(dom, property, value);
|
||
};
|
||
|
||
var setAll = function (element, css) {
|
||
var dom = element.dom();
|
||
|
||
Obj.each(css, function (v, k) {
|
||
internalSet(dom, k, v);
|
||
});
|
||
};
|
||
|
||
/*
|
||
* NOTE: For certain properties, this returns the "used value" which is subtly different to the "computed value" (despite calling getComputedStyle).
|
||
* Blame CSS 2.0.
|
||
*
|
||
* https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||
*/
|
||
var get = function (element, property) {
|
||
var dom = element.dom();
|
||
/*
|
||
* IE9 and above per
|
||
* https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle
|
||
*
|
||
* Not in numerosity, because it doesn't memoize and looking this up dynamically in performance critical code would be horrendous.
|
||
*
|
||
* JQuery has some magic here for IE popups, but we don't really need that.
|
||
* It also uses element.ownerDocument.defaultView to handle iframes but that hasn't been required since FF 3.6.
|
||
*/
|
||
var styles = window.getComputedStyle(dom);
|
||
var r = styles.getPropertyValue(property);
|
||
|
||
// jquery-ism: If r is an empty string, check that the element is not in a document. If it isn't, return the raw value.
|
||
// Turns out we do this a lot.
|
||
var v = (r === '' && !Body.inBody(element)) ? getUnsafeProperty(dom, property) : r;
|
||
|
||
// undefined is the more appropriate value for JS. JQuery coerces to an empty string, but screw that!
|
||
return v === null ? undefined : v;
|
||
};
|
||
|
||
var getUnsafeProperty = function (dom, property) {
|
||
// removed: support for dom().style[property] where prop is camel case instead of normal property name
|
||
return dom.style.getPropertyValue(property);
|
||
};
|
||
|
||
/*
|
||
* Gets the raw value from the style attribute. Useful for retrieving "used values" from the DOM:
|
||
* https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||
*
|
||
* Returns NONE if the property isn't set, or the value is an empty string.
|
||
*/
|
||
var getRaw = function (element, property) {
|
||
var dom = element.dom();
|
||
var raw = getUnsafeProperty(dom, property);
|
||
|
||
return Option.from(raw).filter(function (r) { return r.length > 0; });
|
||
};
|
||
|
||
var isValidValue = function (tag, property, value) {
|
||
var element = Element.fromTag(tag);
|
||
set(element, property, value);
|
||
var style = getRaw(element, property);
|
||
return style.isSome();
|
||
};
|
||
|
||
var remove = function (element, property) {
|
||
var dom = element.dom();
|
||
/*
|
||
* IE9 and above - MDN doesn't have details, but here's a couple of random internet claims
|
||
*
|
||
* http://help.dottoro.com/ljopsjck.php
|
||
* http://stackoverflow.com/a/7901886/7546
|
||
*/
|
||
dom.style.removeProperty(property);
|
||
|
||
if (Attr.has(element, 'style') && Strings.trim(Attr.get(element, 'style')) === '') {
|
||
// No more styles left, remove the style attribute as well
|
||
Attr.remove(element, 'style');
|
||
}
|
||
};
|
||
|
||
var preserve = function (element, f) {
|
||
var oldStyles = Attr.get(element, 'style');
|
||
var result = f(element);
|
||
var restore = oldStyles === undefined ? Attr.remove : Attr.set;
|
||
restore(element, 'style', oldStyles);
|
||
return result;
|
||
};
|
||
|
||
var copy = function (source, target) {
|
||
target.dom().style.cssText = source.dom().style.cssText;
|
||
};
|
||
|
||
var reflow = function (e) {
|
||
/* NOTE:
|
||
* do not rely on this return value.
|
||
* It's here so the closure compiler doesn't optimise the property access away.
|
||
*/
|
||
return e.dom().offsetWidth;
|
||
};
|
||
|
||
return {
|
||
copy: copy,
|
||
set: set,
|
||
preserve: preserve,
|
||
setAll: setAll,
|
||
remove: remove,
|
||
get: get,
|
||
getRaw: getRaw,
|
||
isValidValue: isValidValue,
|
||
reflow: reflow
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!navigator", [], function () { return navigator; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.FlashInfo',
|
||
|
||
[
|
||
'ephox.cement.flash.HelpCopy',
|
||
'ephox.cement.style.Styles',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'global!navigator'
|
||
],
|
||
|
||
function (HelpCopy, Styles, PlatformDetection, Fun, Class, Css, Element, Insert, InsertAll, navigator) {
|
||
var platform = PlatformDetection.detect();
|
||
|
||
// separated out to constrain the scope of the JSHint override
|
||
var ieFlash = function () {
|
||
/* global ActiveXObject */
|
||
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
|
||
};
|
||
|
||
var hasFlash = function () {
|
||
try {
|
||
var flashObj = platform.browser.isIE() ? ieFlash() : navigator.plugins['Shockwave Flash'];
|
||
return flashObj !== undefined;
|
||
} catch (err) {
|
||
return false;
|
||
}
|
||
};
|
||
|
||
var noflash = function (container, _bin, reflow, translations) {
|
||
var help = HelpCopy.noflash(translations);
|
||
Insert.append(container, help);
|
||
|
||
return {
|
||
reset: Fun.noop
|
||
};
|
||
};
|
||
|
||
var flash = function (container, bin, reflow, translations) {
|
||
var help = HelpCopy.paste(translations);
|
||
var indicator = HelpCopy.indicator(translations);
|
||
|
||
InsertAll.append(container, [ indicator, help, bin.element() ]);
|
||
|
||
var indicatorHide = function () {
|
||
/*
|
||
X-browser magic that makes the flash blocker/s info display nicely with the cement spinner
|
||
*/
|
||
Css.setAll(indicator, {
|
||
'height': '0',
|
||
'padding': '0'
|
||
});
|
||
};
|
||
|
||
var reset = function () {
|
||
Css.set(help, 'display', 'block');
|
||
Css.set(indicator, 'display', 'none');
|
||
reflow();
|
||
};
|
||
|
||
var busy = function () {
|
||
Css.set(help, 'display', 'none');
|
||
Css.set(indicator, 'display', 'block');
|
||
Css.remove(indicator, 'height');
|
||
Css.remove(indicator, 'padding');
|
||
reflow();
|
||
};
|
||
|
||
bin.events.spin.bind(busy);
|
||
bin.events.reset.bind(reset);
|
||
bin.events.hide.bind(indicatorHide);
|
||
|
||
return {
|
||
reset: reset
|
||
};
|
||
};
|
||
|
||
return function (bin, reflow, cementConfig) {
|
||
console.log(bin, reflow, cementConfig)
|
||
var container = Element.fromTag('div');
|
||
var style = 'flashbin-wrapper-' + (platform.os.isOSX() ? 'cmd' : 'ctrl');
|
||
|
||
Class.add(container, Styles.resolve(style));
|
||
|
||
var loader = hasFlash() ? flash : noflash;
|
||
var info = loader(container, bin, reflow, cementConfig.translations);
|
||
|
||
return {
|
||
element: Fun.constant(container),
|
||
reset: info.reset
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!clearInterval", [], function () { return clearInterval; });
|
||
ephox.bolt.module.api.define("global!setInterval", [], function () { return setInterval; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.alien.WaitForFlash',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'global!clearInterval',
|
||
'global!setInterval'
|
||
],
|
||
|
||
function (Type, Arr, clearInterval, setInterval) {
|
||
return function (obj, flashFunctions, callback) {
|
||
var functionsReady = function (dom) {
|
||
// Plugin objects register functions on their DOM node *after* they load, until then they are undefined.
|
||
return Arr.forall(flashFunctions, function (name) {
|
||
return Type.isFunction(dom[name]);
|
||
});
|
||
};
|
||
|
||
var search = function () {
|
||
// Sometimes we never get the onload callback, but PercentLoaded reaches 100 indicating it is actually running.
|
||
// If that happens, once the functions are available we are good to go.
|
||
var dom = obj.dom();
|
||
if (Type.isFunction(dom.PercentLoaded) && dom.PercentLoaded() === 100 && functionsReady(dom)) {
|
||
stop();
|
||
callback();
|
||
}
|
||
};
|
||
|
||
var waiting = true;
|
||
var reference = setInterval(search, 500);
|
||
|
||
var stop = function () {
|
||
if (waiting) {
|
||
clearInterval(reference);
|
||
waiting = false;
|
||
}
|
||
};
|
||
return {
|
||
stop: stop
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.epithet.Namespace',
|
||
|
||
[
|
||
'ephox.epithet.Global'
|
||
],
|
||
|
||
function (Global) {
|
||
var step = function (o, part) {
|
||
if (o[part] === undefined || o[part] === null)
|
||
o[part] = {};
|
||
return o[part];
|
||
};
|
||
|
||
var namespace = function (name, target) {
|
||
var o = target || Global;
|
||
var parts = name.split('.');
|
||
for (var i = 0; i < parts.length; ++i)
|
||
o = step(o, parts[i]);
|
||
return o;
|
||
};
|
||
|
||
return {
|
||
namespace: namespace
|
||
};
|
||
}
|
||
);
|
||
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.oilspill.callback.Globaliser',
|
||
|
||
[
|
||
'ephox.epithet.Namespace'
|
||
],
|
||
|
||
function (Namespace) {
|
||
var install = function (namespace) {
|
||
var manager = Namespace.namespace(namespace);
|
||
manager.callbacks = {};
|
||
|
||
var count = 0;
|
||
var next = function () {
|
||
var ref = 'callback_' + count;
|
||
count++;
|
||
return ref;
|
||
};
|
||
|
||
var global = function (ref) {
|
||
return namespace + '.callbacks.' + ref;
|
||
};
|
||
|
||
var register = function (callback, permanent) {
|
||
var ref = next();
|
||
manager.callbacks[ref] = function () {
|
||
if (!permanent) unregister(ref);
|
||
callback.apply(null, arguments);
|
||
};
|
||
return global(ref);
|
||
};
|
||
|
||
var ephemeral = function (callback) {
|
||
return register(callback, false);
|
||
};
|
||
|
||
var permanent = function (callback) {
|
||
return register(callback, true);
|
||
};
|
||
|
||
var unregister = function (spec) {
|
||
var ref = spec.substring(spec.lastIndexOf('.') + 1);
|
||
if (manager.callbacks[ref] !== undefined)
|
||
delete manager.callbacks[ref];
|
||
};
|
||
|
||
manager.ephemeral = ephemeral;
|
||
manager.permanent = permanent;
|
||
manager.unregister = unregister;
|
||
|
||
return manager;
|
||
};
|
||
|
||
return {
|
||
install: install
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.PredicateFind',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Body',
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.impl.ClosestOrAncestor'
|
||
],
|
||
|
||
function (Type, Arr, Fun, Option, Body, Compare, Element, ClosestOrAncestor) {
|
||
var first = function (predicate) {
|
||
return descendant(Body.body(), predicate);
|
||
};
|
||
|
||
var ancestor = function (scope, predicate, isRoot) {
|
||
var element = scope.dom();
|
||
var stop = Type.isFunction(isRoot) ? isRoot : Fun.constant(false);
|
||
|
||
while (element.parentNode) {
|
||
element = element.parentNode;
|
||
var el = Element.fromDom(element);
|
||
|
||
if (predicate(el)) return Option.some(el);
|
||
else if (stop(el)) break;
|
||
}
|
||
return Option.none();
|
||
};
|
||
|
||
var closest = function (scope, predicate, isRoot) {
|
||
// This is required to avoid ClosestOrAncestor passing the predicate to itself
|
||
var is = function (scope) {
|
||
return predicate(scope);
|
||
};
|
||
return ClosestOrAncestor(is, ancestor, scope, predicate, isRoot);
|
||
};
|
||
|
||
var sibling = function (scope, predicate) {
|
||
var element = scope.dom();
|
||
if (!element.parentNode) return Option.none();
|
||
|
||
return child(Element.fromDom(element.parentNode), function (x) {
|
||
return !Compare.eq(scope, x) && predicate(x);
|
||
});
|
||
};
|
||
|
||
var child = function (scope, predicate) {
|
||
var result = Arr.find(scope.dom().childNodes,
|
||
Fun.compose(predicate, Element.fromDom));
|
||
return Option.from(result).map(Element.fromDom);
|
||
};
|
||
|
||
var descendant = function (scope, predicate) {
|
||
var descend = function (element) {
|
||
for (var i = 0; i < element.childNodes.length; i++) {
|
||
if (predicate(Element.fromDom(element.childNodes[i])))
|
||
return Option.some(Element.fromDom(element.childNodes[i]));
|
||
|
||
var res = descend(element.childNodes[i]);
|
||
if (res.isSome())
|
||
return res;
|
||
}
|
||
|
||
return Option.none();
|
||
};
|
||
|
||
return descend(scope.dom());
|
||
};
|
||
|
||
return {
|
||
first: first,
|
||
ancestor: ancestor,
|
||
closest: closest,
|
||
sibling: sibling,
|
||
child: child,
|
||
descendant: descendant
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.PredicateExists',
|
||
|
||
[
|
||
'ephox.sugar.api.PredicateFind'
|
||
],
|
||
|
||
function (PredicateFind) {
|
||
var any = function (predicate) {
|
||
return PredicateFind.first(predicate).isSome();
|
||
};
|
||
|
||
var ancestor = function (scope, predicate, isRoot) {
|
||
return PredicateFind.ancestor(scope, predicate, isRoot).isSome();
|
||
};
|
||
|
||
var closest = function (scope, predicate, isRoot) {
|
||
return PredicateFind.closest(scope, predicate, isRoot).isSome();
|
||
};
|
||
|
||
var sibling = function (scope, predicate) {
|
||
return PredicateFind.sibling(scope, predicate).isSome();
|
||
};
|
||
|
||
var child = function (scope, predicate) {
|
||
return PredicateFind.child(scope, predicate).isSome();
|
||
};
|
||
|
||
var descendant = function (scope, predicate) {
|
||
return PredicateFind.descendant(scope, predicate).isSome();
|
||
};
|
||
|
||
return {
|
||
any: any,
|
||
ancestor: ancestor,
|
||
closest: closest,
|
||
sibling: sibling,
|
||
child: child,
|
||
descendant: descendant
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Focus',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.PredicateExists',
|
||
'ephox.sugar.api.Traverse',
|
||
'global!document'
|
||
],
|
||
|
||
function (Fun, Option, Compare, Element, PredicateExists, Traverse, document) {
|
||
var focus = function (element) {
|
||
element.dom().focus();
|
||
};
|
||
|
||
var blur = function (element) {
|
||
element.dom().blur();
|
||
};
|
||
|
||
var hasFocus = function (element) {
|
||
var doc = Traverse.owner(element).dom();
|
||
return element.dom() === doc.activeElement;
|
||
};
|
||
|
||
var active = function (_doc) {
|
||
var doc = _doc !== undefined ? _doc.dom() : document;
|
||
return Option.from(doc.activeElement).map(Element.fromDom);
|
||
};
|
||
|
||
var focusInside = function (element) {
|
||
// Only call focus if the focus is not already inside it.
|
||
var doc = Traverse.owner(element);
|
||
var inside = active(doc).filter(function (a) {
|
||
return PredicateExists.closest(a, Fun.curry(Compare.eq, element));
|
||
});
|
||
|
||
inside.fold(function () {
|
||
focus(element);
|
||
}, Fun.noop);
|
||
};
|
||
|
||
/**
|
||
* Return the descendant element that has focus.
|
||
* Use instead of SelectorFind.descendant(container, ':focus')
|
||
* because the :focus selector relies on keyboard focus.
|
||
*/
|
||
var search = function (element) {
|
||
return active(Traverse.owner(element)).filter(function (e) {
|
||
return element.dom().contains(e.dom());
|
||
});
|
||
};
|
||
|
||
return {
|
||
hasFocus: hasFocus,
|
||
focus: focus,
|
||
blur: blur,
|
||
active: active,
|
||
search: search,
|
||
focusInside: focusInside
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!clearTimeout", [], function () { return clearTimeout; });
|
||
ephox.bolt.module.api.define("global!unescape", [], function () { return unescape; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.Flashbin',
|
||
|
||
[
|
||
'ephox.cement.alien.WaitForFlash',
|
||
'ephox.cement.style.Styles',
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.epithet.Id',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.oilspill.callback.Globaliser',
|
||
'ephox.perhaps.Option',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Focus',
|
||
'ephox.sugar.api.Insert',
|
||
'global!clearTimeout',
|
||
'global!console',
|
||
'global!setTimeout',
|
||
'global!unescape',
|
||
'global!window'
|
||
],
|
||
|
||
function (WaitForFlash, Styles, Arr, Obj, Id, PlatformDetection, Globaliser, Option, Event, Events, Class, Css, Element, Focus, Insert, clearTimeout, console, setTimeout, unescape, window) {
|
||
var globaliser = Globaliser.install('ephox.flash');
|
||
var platform = PlatformDetection.detect();
|
||
var unbinders = Option.none();
|
||
|
||
return function (swfLoc) {
|
||
var events = Events.create({
|
||
response: Event(['rtf']),
|
||
spin: Event([]),
|
||
cancel: Event([]),
|
||
error: Event(['message']),
|
||
reset: Event([]),
|
||
hide: Event([])
|
||
});
|
||
|
||
var hasLoaded = false;
|
||
|
||
var target = Element.fromTag('div');
|
||
Class.add(target, Styles.resolve('flashbin-target'));
|
||
|
||
var process = function (rtfData) {
|
||
// The setTimeout here is to detach it from the event stack/queue, so that the UI is updated
|
||
// during this block.
|
||
setTimeout(function () {
|
||
events.trigger.response(unescape(rtfData));
|
||
}, 0);
|
||
};
|
||
|
||
var loadComplete = function () {
|
||
waiting.stop();
|
||
if (hasLoaded) return;
|
||
hasLoaded = true;
|
||
try {
|
||
var dom = bin.dom();
|
||
Obj.each(flashFunctionMapping, function (v, k) {
|
||
var f = dom[k];
|
||
f.call(dom, v); // plugin requires 'this' to be set correctly
|
||
});
|
||
events.trigger.reset();
|
||
clearOverTime();
|
||
focus();
|
||
} catch (e) {
|
||
console.log('Flash dialog - Error during onLoad ', e);
|
||
}
|
||
};
|
||
|
||
var onloadCallback = globaliser.permanent(loadComplete);
|
||
|
||
var flashFunctionMapping = {
|
||
'setSpinCallback': globaliser.permanent(events.trigger.spin),
|
||
'setPasteCallback': globaliser.permanent(process),
|
||
'setEscapeCallback': globaliser.permanent(events.trigger.cancel),
|
||
'setErrorCallback': globaliser.permanent(events.trigger.error)
|
||
};
|
||
|
||
var createObject = function () {
|
||
var swf = swfLoc.replace(/^https?:\/\//, '//');
|
||
var flashVars = 'onLoad=' + onloadCallback;
|
||
|
||
//
|
||
// NOTE: the wmode (window mode) of "opaque" here has been suggested as on various forums as
|
||
// required to get the swf to focus on Firefox. e.g.
|
||
// http://stackoverflow.com/questions/7921690/how-do-i-make-my-flash-object-get-focus-on-load
|
||
// This setting did not seem to cause problems on our other supported browsers.
|
||
// Please do not return this setting to "transparent" without serious testing.
|
||
//
|
||
var commonParams =
|
||
' <param name="allowscriptaccess" value="always">' +
|
||
' <param name="wmode" value="opaque">' +
|
||
' <param name="FlashVars" value="' + flashVars + '">';
|
||
|
||
if (platform.browser.isIE() && platform.browser.version.major === 10) {
|
||
var id = Id.generate('flash-bin');
|
||
return Element.fromHtml(
|
||
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" id="' + id + '">' +
|
||
'<param name="movie" value="' + swf + '">' +
|
||
commonParams +
|
||
'</object>');
|
||
} else {
|
||
return Element.fromHtml(
|
||
'<object type="application/x-shockwave-flash" data="' + swf + '">' +
|
||
commonParams +
|
||
'</object>');
|
||
}
|
||
};
|
||
|
||
var bin = createObject();
|
||
|
||
var binStealth = function () {
|
||
Css.setAll(bin, {
|
||
'width' : '2px',
|
||
'height': '2px'
|
||
});
|
||
};
|
||
|
||
binStealth();
|
||
var waiting = WaitForFlash(bin, Obj.keys(flashFunctionMapping), loadComplete);
|
||
|
||
Insert.append(target, bin);
|
||
|
||
var element = function () {
|
||
return target;
|
||
};
|
||
|
||
var focus = function () {
|
||
if (platform.browser.isFirefox()) {
|
||
// On Firefox, pasting into flash also fires a paste event wherever the window selection is
|
||
window.getSelection().removeAllRanges();
|
||
}
|
||
Focus.focus(bin);
|
||
};
|
||
|
||
var timerHandle = null;
|
||
|
||
var overTime = function () {
|
||
Class.add(target, Styles.resolve('flash-activate'));
|
||
Css.remove(bin, 'height');
|
||
Css.remove(bin, 'width');
|
||
events.trigger.hide();
|
||
};
|
||
|
||
var clearOverTime = function () {
|
||
clearTimeout(timerHandle);
|
||
Class.remove(target, Styles.resolve('flash-activate'));
|
||
binStealth();
|
||
};
|
||
|
||
var activate = function () {
|
||
// TODO: improvement create a custom poll event for .swf has loaded, then fire overtime.
|
||
timerHandle = setTimeout(overTime, 3000);
|
||
events.trigger.spin();
|
||
Css.set(target, 'display', 'block');
|
||
focus();
|
||
};
|
||
|
||
var deactivate = function () {
|
||
Css.set(target, 'display', 'none');
|
||
unbinders.each(function (unbinders) {
|
||
Arr.each(unbinders, function (unbinder) {
|
||
unbinder.unbind();
|
||
});
|
||
});
|
||
};
|
||
|
||
var destroy = function () {
|
||
deactivate();
|
||
Arr.each(Obj.values(flashFunctionMapping), function (s) {
|
||
globaliser.unregister(s);
|
||
});
|
||
globaliser.unregister(onloadCallback);
|
||
waiting.stop(); // in case the user cancels
|
||
};
|
||
|
||
return {
|
||
focus: focus,
|
||
element: element,
|
||
activate: activate,
|
||
deactivate: deactivate,
|
||
destroy: destroy,
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.impl.FilteredEvent',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Fun, Element) {
|
||
|
||
var mkEvent = function (target, x, y, stop, prevent, kill, raw) {
|
||
// switched from a struct to manual Fun.constant() because we are passing functions now, not just values
|
||
return {
|
||
'target': Fun.constant(target),
|
||
'x': Fun.constant(x),
|
||
'y': Fun.constant(y),
|
||
'stop': stop,
|
||
'prevent': prevent,
|
||
'kill': kill,
|
||
'raw': Fun.constant(raw)
|
||
};
|
||
};
|
||
|
||
var handle = function (filter, handler) {
|
||
return function (rawEvent) {
|
||
if (!filter(rawEvent)) return;
|
||
|
||
// IE9 minimum
|
||
var target = Element.fromDom(rawEvent.target);
|
||
|
||
var stop = function () {
|
||
rawEvent.stopPropagation();
|
||
};
|
||
|
||
var prevent = function () {
|
||
rawEvent.preventDefault();
|
||
};
|
||
|
||
var kill = Fun.compose(prevent, stop); // more of a sequence than a compose, but same effect
|
||
|
||
// FIX: Don't just expose the raw event. Need to identify what needs standardisation.
|
||
var evt = mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent);
|
||
handler(evt);
|
||
};
|
||
};
|
||
|
||
var binder = function (element, event, filter, handler, useCapture) {
|
||
var wrapped = handle(filter, handler);
|
||
// IE9 minimum
|
||
element.dom().addEventListener(event, wrapped, useCapture);
|
||
|
||
return {
|
||
unbind: Fun.curry(unbind, element, event, wrapped, useCapture)
|
||
};
|
||
};
|
||
|
||
var bind = function (element, event, filter, handler) {
|
||
return binder(element, event, filter, handler, false);
|
||
};
|
||
|
||
var capture = function (element, event, filter, handler) {
|
||
return binder(element, event, filter, handler, true);
|
||
};
|
||
|
||
var unbind = function (element, event, handler, useCapture) {
|
||
// IE9 minimum
|
||
element.dom().removeEventListener(event, handler, useCapture);
|
||
};
|
||
|
||
return {
|
||
bind: bind,
|
||
capture: capture
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.DomEvent',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.impl.FilteredEvent'
|
||
],
|
||
|
||
function (Fun, FilteredEvent) {
|
||
var filter = Fun.constant(true); // no filter on plain DomEvents
|
||
|
||
var bind = function (element, event, handler) {
|
||
return FilteredEvent.bind(element, event, filter, handler);
|
||
};
|
||
|
||
var capture = function (element, event, handler) {
|
||
return FilteredEvent.capture(element, event, filter, handler);
|
||
};
|
||
|
||
return {
|
||
bind: bind,
|
||
capture: capture
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.FlashDialog',
|
||
|
||
[
|
||
'ephox.cement.flash.FlashInfo',
|
||
'ephox.cement.flash.Flashbin',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.DomEvent',
|
||
'ephox.sugar.api.Element',
|
||
'global!window'
|
||
],
|
||
|
||
function (FlashInfo, Flashbin, Event, Events, DomEvent, Element, window) {
|
||
|
||
return function (createDialog, settings) {
|
||
var translations = settings.translations;
|
||
|
||
var events = Events.create({
|
||
response: Event(['rtf', 'hide']),
|
||
cancel: Event([]),
|
||
error: Event(['message'])
|
||
});
|
||
|
||
var open = function () {
|
||
var bin = Flashbin(settings.swf);
|
||
bin.deactivate();
|
||
|
||
var win = Element.fromDom(window);
|
||
var clickEvent = DomEvent.bind(win, 'mouseup', bin.focus);
|
||
|
||
var hide = function () {
|
||
destroy();
|
||
};
|
||
|
||
var cancel = function () {
|
||
hide();
|
||
events.trigger.cancel();
|
||
};
|
||
|
||
bin.events.cancel.bind(cancel);
|
||
bin.events.response.bind(function (event) {
|
||
// Don't hide immediately - keep the please wait going until the images are converted
|
||
events.trigger.response(event.rtf(), hide);
|
||
});
|
||
|
||
bin.events.error.bind(function (event) {
|
||
hide();
|
||
events.trigger.error(event.message());
|
||
});
|
||
|
||
var dialog = createDialog();
|
||
dialog.setTitle( translations('cement.dialog.flash.title'));
|
||
|
||
var information = FlashInfo(bin, dialog.reflow, settings);
|
||
information.reset();
|
||
dialog.setContent(information.element());
|
||
|
||
dialog.events.close.bind(cancel);
|
||
dialog.show();
|
||
|
||
bin.activate();
|
||
|
||
var destroy = function () {
|
||
clickEvent.unbind();
|
||
dialog.destroy();
|
||
bin.destroy();
|
||
};
|
||
};
|
||
|
||
return {
|
||
open: open,
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Replication',
|
||
|
||
[
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Attr, Element, Insert, InsertAll, Remove, Traverse) {
|
||
var clone = function (original, deep) {
|
||
return Element.fromDom(original.dom().cloneNode(deep));
|
||
};
|
||
|
||
/** Shallow clone - just the tag, no children */
|
||
var shallow = function (original) {
|
||
return clone(original, false);
|
||
};
|
||
|
||
/** Deep clone - everything copied including children */
|
||
var deep = function (original) {
|
||
return clone(original, true);
|
||
};
|
||
|
||
/** Shallow clone, with a new tag */
|
||
var shallowAs = function (original, tag) {
|
||
var nu = Element.fromTag(tag);
|
||
|
||
var attributes = Attr.clone(original);
|
||
Attr.setAll(nu, attributes);
|
||
|
||
return nu;
|
||
};
|
||
|
||
/** Deep clone, with a new tag */
|
||
var copy = function (original, tag) {
|
||
var nu = shallowAs(original, tag);
|
||
|
||
// NOTE
|
||
// previously this used serialisation:
|
||
// nu.dom().innerHTML = original.dom().innerHTML;
|
||
//
|
||
// Clone should be equivalent (and faster), but if TD <-> TH toggle breaks, put it back.
|
||
|
||
var cloneChildren = Traverse.children(deep(original));
|
||
InsertAll.append(nu, cloneChildren);
|
||
|
||
return nu;
|
||
};
|
||
|
||
/** Change the tag name, but keep all children */
|
||
var mutate = function (original, tag) {
|
||
var nu = shallowAs(original, tag);
|
||
|
||
Insert.before(original, nu);
|
||
var children = Traverse.children(original);
|
||
InsertAll.append(nu, children);
|
||
Remove.remove(original);
|
||
return nu;
|
||
};
|
||
|
||
return {
|
||
shallow: shallow,
|
||
shallowAs: shallowAs,
|
||
deep: deep,
|
||
copy: copy,
|
||
mutate: mutate
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.api.RtfImage',
|
||
|
||
[
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.PredicateFind',
|
||
'ephox.sugar.api.Replication',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Option, Attr, Class, Css, Element, Node, PredicateFind, Replication, SelectorFilter, Strings) {
|
||
var searchIn = function (comment, selector) {
|
||
var innards = Node.value(comment);
|
||
var container = Element.fromTag('div');
|
||
var substr = innards.indexOf(']>');
|
||
container.dom().innerHTML = innards.substr(substr + ']>'.length);
|
||
// Note: It doesn't look like sizzle can pick up namespaced tags with selectors.
|
||
return PredicateFind.descendant(container, function (elem) {
|
||
return Node.name(elem) === selector;
|
||
});
|
||
};
|
||
|
||
var scour = function (target) {
|
||
return Node.isComment(target) ? searchIn(target, 'v:shape') : Option.none();
|
||
};
|
||
|
||
var vshape = function (original) {
|
||
return scour(original).map(function (image) {
|
||
// NOTE: If we discover more than 2 possible attributes, de-dupe with HtmlPaste somehow
|
||
var spid = Attr.get(image, 'o:spid');
|
||
var vShapeId = spid === undefined ? Attr.get(image, 'id') : spid;
|
||
|
||
var result = Element.fromTag('img');
|
||
Class.add(result, 'rtf-data-image');
|
||
Attr.set(result, 'data-image-id', vShapeId.substr('_x0000_'.length));
|
||
Attr.set(result, 'data-image-type', 'code');
|
||
Css.setAll(result, {
|
||
width: Css.get(image, 'width'),
|
||
height: Css.get(image, 'height')
|
||
});
|
||
|
||
return result;
|
||
});
|
||
};
|
||
|
||
var local = function (original) {
|
||
if (Node.name(original) === 'img') {
|
||
var src = Attr.get(original, 'src');
|
||
if (src !== undefined && src !== null && Strings.startsWith(src, 'file://')) {
|
||
var result = Replication.shallow(original);
|
||
var parts = src.split(/[\/\\]/);
|
||
var last = parts[parts.length - 1];
|
||
Attr.set(result, 'data-image-id', last);
|
||
Attr.remove(result, 'src');
|
||
Attr.set(result, 'data-image-type', 'local');
|
||
Class.add(result, 'rtf-data-image');
|
||
return Option.some(result);
|
||
} else {
|
||
return Option.none();
|
||
}
|
||
} else {
|
||
return Option.none();
|
||
}
|
||
};
|
||
|
||
var exists = function (container) {
|
||
return find(container).length > 0;
|
||
};
|
||
|
||
var find = function (container) {
|
||
return SelectorFilter.descendants(container, '.rtf-data-image');
|
||
};
|
||
|
||
return {
|
||
local: local,
|
||
vshape: vshape,
|
||
find: find,
|
||
exists: exists,
|
||
scour: scour
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.flash.Flash',
|
||
|
||
[
|
||
'ephox.bowerbird.api.Rtf',
|
||
'ephox.cement.flash.Correlation',
|
||
'ephox.cement.flash.FlashDialog',
|
||
'ephox.compass.Arr',
|
||
'ephox.limbo.api.RtfImage',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Rtf, Correlation, FlashDialog, Arr, RtfImage, Event, Events, Remove, Traverse) {
|
||
return function (createDialog, cementConfig) {
|
||
var events = Events.create({
|
||
error: Event(['message']),
|
||
insert: Event(['elements', 'assets'])
|
||
});
|
||
|
||
var gordon = function (content, baseAssets) {
|
||
var response = function (event) {
|
||
var hideDialog = event.hide();
|
||
var imageData = Rtf.images(event.rtf());
|
||
var images = RtfImage.find(content);
|
||
|
||
Correlation.convert(images, imageData, function (assets) {
|
||
events.trigger.insert(Traverse.children(content), assets.concat(baseAssets));
|
||
|
||
// when images are pasted, the flash dialog doesn't automatically hide
|
||
hideDialog();
|
||
});
|
||
};
|
||
|
||
var cancelImages = function () {
|
||
var images = RtfImage.find(content);
|
||
Arr.each(images, Remove.remove);
|
||
events.trigger.insert(Traverse.children(content), baseAssets);
|
||
};
|
||
|
||
if (cementConfig.allowLocalImages) {
|
||
var flash = FlashDialog(createDialog, cementConfig);
|
||
flash.events.response.bind(response);
|
||
flash.events.cancel.bind(cancelImages);
|
||
flash.events.error.bind(function (event) { events.trigger.error(event.message()); });
|
||
flash.open();
|
||
} else {
|
||
cancelImages();
|
||
events.trigger.error('errors.local.images.disallowed');
|
||
}
|
||
};
|
||
|
||
return {
|
||
events: events.registry,
|
||
gordon: gordon
|
||
};
|
||
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.smartpaste.MergeSettings',
|
||
|
||
[
|
||
'ephox.cement.style.Styles',
|
||
'ephox.highway.Merger',
|
||
'ephox.peanut.Fun',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert'
|
||
],
|
||
|
||
function (Styles, Merger, Fun, Event, Events, Class, Element, Insert) {
|
||
return function (createDialog, settings) {
|
||
var translations = settings.translations;
|
||
|
||
var result = function (value, settings, callback) {
|
||
callback(Merger.merge(settings, {
|
||
mergeOfficeStyles: value,
|
||
mergeHtmlStyles: value
|
||
}));
|
||
};
|
||
|
||
var events = Events.create({
|
||
open: Event([]),
|
||
cancel: Event([]),
|
||
close: Event([])
|
||
});
|
||
|
||
var showDialog = function (settings, callback) {
|
||
var clean = function () {
|
||
hide();
|
||
result(false, settings, callback);
|
||
};
|
||
|
||
var merge = function () {
|
||
hide();
|
||
result(true, settings, callback);
|
||
};
|
||
|
||
var content = Element.fromTag('div');
|
||
|
||
Class.add(content, Styles.resolve('styles-dialog-content'));
|
||
|
||
var instructions = Element.fromTag('p');
|
||
var text = Element.fromText(translations('cement.dialog.paste.instructions'));
|
||
Insert.append(instructions, text);
|
||
Insert.append(content, instructions);
|
||
|
||
var cleanButton = {
|
||
text: translations('cement.dialog.paste.clean'),
|
||
tabindex: 0,
|
||
className: Styles.resolve('clean-styles'),
|
||
click: clean
|
||
};
|
||
var mergeButton = {
|
||
text: translations('cement.dialog.paste.merge'),
|
||
tabindex: 1,
|
||
className: Styles.resolve('merge-styles'),
|
||
click: merge
|
||
};
|
||
|
||
var dialog = createDialog(true);
|
||
dialog.setTitle(translations('cement.dialog.paste.title'));
|
||
dialog.setContent(content);
|
||
dialog.setButtons([cleanButton, mergeButton]);
|
||
dialog.show();
|
||
|
||
var hide = function () {
|
||
events.trigger.close();
|
||
dialog.destroy();
|
||
};
|
||
|
||
var cancel = function () {
|
||
events.trigger.cancel();
|
||
hide();
|
||
};
|
||
|
||
dialog.events.close.bind(cancel);
|
||
|
||
events.trigger.open();
|
||
};
|
||
|
||
var get = function (officePaste, callback) {
|
||
var input = officePaste ? 'officeStyles' : 'htmlStyles';
|
||
|
||
var settingToUse = settings[input];
|
||
|
||
if (settingToUse === 'clean') {
|
||
result(false, settings, callback);
|
||
} else if (settingToUse === 'merge') {
|
||
result(true, settings, callback);
|
||
} else {
|
||
// everything else is prompt
|
||
showDialog(settings, callback);
|
||
}
|
||
};
|
||
|
||
return {
|
||
events: events.registry,
|
||
get: get,
|
||
destroy: Fun.noop
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.smartpaste.Inspection',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
var isValidData = function (data) {
|
||
// IE doesn't even have data. Spartan has data, but no types.
|
||
return data !== undefined && data.types !== undefined && data.types !== null;
|
||
};
|
||
|
||
return {
|
||
isValidData: isValidData
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.html.Transform',
|
||
|
||
[
|
||
'ephox.classify.Type',
|
||
'ephox.sugar.api.Attr'
|
||
],
|
||
|
||
function (Type, Attr) {
|
||
var rotateImage = function (img, vshapeAttrs) {
|
||
// MS Word tends to store rotated images as raw with a rotation applied
|
||
var style = vshapeAttrs.style;
|
||
if (Attr.has(img, 'width') && Attr.has(img, 'height') && Type.isString(style)) {
|
||
var rotation = style.match(/rotation:([^;]*)/);
|
||
if (rotation !== null && (rotation[1] === '90' || rotation[1] === '-90')) {
|
||
// We can't actually rotate the binary data, so just swap the width and height.
|
||
// When we decide to rotate the data, we can't do it here.
|
||
Attr.setAll(img, {
|
||
width: Attr.get(img, 'height'),
|
||
height: Attr.get(img, 'width')
|
||
});
|
||
}
|
||
}
|
||
};
|
||
|
||
return {
|
||
rotateImage: rotateImage
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Comments',
|
||
|
||
[
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Element',
|
||
'global!document'
|
||
],
|
||
|
||
function (PlatformDetection, Fun, Element, document) {
|
||
|
||
var regularGetNodes = function (texas) {
|
||
var ret = [];
|
||
while (texas.nextNode() !== null) ret.push(Element.fromDom(texas.currentNode));
|
||
return ret;
|
||
};
|
||
|
||
var ieGetNodes = function (texas) {
|
||
// IE throws an error on nextNode() when there are zero nodes available, and any attempts I made to detect this
|
||
// just resulted in throwing away valid cases
|
||
try {
|
||
return regularGetNodes(texas);
|
||
} catch (e) {
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// I hate needing platform detection in Sugar, but the alternative is to always try/catch which will swallow coding errors as well
|
||
var browser = PlatformDetection.detect().browser;
|
||
var getNodes = browser.isIE() || browser.isSpartan() ? ieGetNodes : regularGetNodes;
|
||
|
||
// Weird, but oh well
|
||
var noFilter = Fun.constant(Fun.constant(true));
|
||
|
||
var find = function (node, filterOpt) {
|
||
|
||
var vmlFilter = filterOpt.fold(noFilter, function (filter) {
|
||
return function (comment) {
|
||
return filter(comment.nodeValue);
|
||
};
|
||
});
|
||
|
||
// the standard wants an object, IE wants a function. But everything is an object, so let's be sneaky
|
||
// http://www.bennadel.com/blog/2607-finding-html-comment-nodes-in-the-dom-using-treewalker.htm
|
||
vmlFilter.acceptNode = vmlFilter;
|
||
|
||
// TODO: Add NodeFilter to numerosity (requires IE9 so can't be a global import)
|
||
var texas = document.createTreeWalker(node.dom(), NodeFilter.SHOW_COMMENT, vmlFilter, false);
|
||
|
||
return getNodes(texas);
|
||
};
|
||
return {
|
||
find: find
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.alien.Comments',
|
||
|
||
[
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Comments',
|
||
'ephox.violin.Strings',
|
||
'global!document'
|
||
],
|
||
|
||
function (Option, Comments, Strings, document) {
|
||
|
||
var find = function (node) {
|
||
// TODO: Should this be in lingo?
|
||
return Comments.find(node, Option.some(function (value) {
|
||
return Strings.startsWith(value, '[if gte vml 1]'); // faster than contains on huge MS office comments
|
||
}));
|
||
};
|
||
return {
|
||
find: find
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.perhaps.Options',
|
||
|
||
[
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Option) {
|
||
var cat = function (arr) {
|
||
var r = [];
|
||
var push = function (x) {
|
||
r.push(x);
|
||
};
|
||
for (var i = 0; i < arr.length; i++) {
|
||
arr[i].each(push);
|
||
}
|
||
return r;
|
||
};
|
||
|
||
var findMap = function (arr, f) {
|
||
for (var i = 0; i < arr.length; i++) {
|
||
var r = f(arr[i], i);
|
||
if (r.isSome()) {
|
||
return r;
|
||
}
|
||
}
|
||
return Option.none();
|
||
};
|
||
|
||
/**
|
||
if all elements in arr are 'some', their inner values are passed as arguments to f
|
||
f must have arity arr.length
|
||
*/
|
||
var liftN = function(arr, f) {
|
||
var r = [];
|
||
for (var i = 0; i < arr.length; i++) {
|
||
var x = arr[i];
|
||
if (x.isSome()) {
|
||
r.push(x.getOrDie());
|
||
} else {
|
||
return Option.none();
|
||
}
|
||
};
|
||
return Option.some(f.apply(null, r));
|
||
};
|
||
|
||
return {
|
||
cat: cat,
|
||
findMap: findMap,
|
||
liftN: liftN
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.images.ImageReference',
|
||
|
||
[
|
||
'ephox.cement.alien.Comments',
|
||
'ephox.compass.Arr',
|
||
'ephox.limbo.api.RtfImage',
|
||
'ephox.perhaps.Option',
|
||
'ephox.perhaps.Options',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Elements',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'global!console'
|
||
],
|
||
|
||
function (Comments, Arr, RtfImage, Option, Options, Struct, Attr, Elements, SelectorFilter, console) {
|
||
var imgData = Struct.immutable('img', 'vshape');
|
||
|
||
var getImageAttrs = function (image) {
|
||
var imgAttrs = getAttrs(image);
|
||
imgAttrs._rawElement = image.dom();
|
||
return imgAttrs;
|
||
};
|
||
|
||
var getVshapeAttrs = function (vshape) {
|
||
var vsAttr = getAttrs(vshape);
|
||
vsAttr._rawElement = vshape.dom();
|
||
return vsAttr;
|
||
};
|
||
|
||
var getAttrs = function (element) {
|
||
return Attr.clone(element);
|
||
};
|
||
|
||
var extract = function (raw) {
|
||
var nodes = Elements.fromHtml(raw);
|
||
var images = Arr.bind(nodes, function (n) {
|
||
return SelectorFilter.descendants(n, 'img');
|
||
});
|
||
|
||
var comments = Arr.bind(nodes, Comments.find);
|
||
var vshapes = Options.cat(Arr.map(comments, RtfImage.scour));
|
||
|
||
var list = Arr.map(images, function (image) {
|
||
return traverse(image, vshapes);
|
||
});
|
||
return Options.cat(list);
|
||
};
|
||
|
||
var traverse = function (element, vshapes) {
|
||
var vshapeTarget = Attr.get(element, 'v:shapes');
|
||
|
||
var vshapeOpt = Option.from(Arr.find(vshapes, function (vshape) {
|
||
return Attr.get(vshape, 'id') === vshapeTarget;
|
||
}));
|
||
|
||
if (vshapeOpt.isNone()) console.log('WARNING: unable to find data for image', element.dom());
|
||
|
||
return vshapeOpt.map(function (vshape) {
|
||
return pack(element, vshape);
|
||
});
|
||
};
|
||
|
||
var pack = function (img, vshape) {
|
||
return imgData(
|
||
getImageAttrs(img),
|
||
getVshapeAttrs(vshape)
|
||
);
|
||
};
|
||
|
||
return {
|
||
extract: extract
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.photon.Reader',
|
||
|
||
[
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Option, Element) {
|
||
|
||
var iframeDoc = function (element) {
|
||
var dom = element.dom();
|
||
try {
|
||
var idoc = dom.contentWindow ? dom.contentWindow.document : dom.contentDocument;
|
||
return idoc !== undefined && idoc !== null ? Option.some(Element.fromDom(idoc)) : Option.none();
|
||
} catch (err) {
|
||
// ASSUMPTION: Permission errors result in an unusable iframe.
|
||
console.log('Error reading iframe: ', dom);
|
||
console.log('Error was: ' + err);
|
||
return Option.none();
|
||
}
|
||
};
|
||
|
||
var doc = function (element) {
|
||
var optDoc = iframeDoc(element);
|
||
return optDoc.fold(function () {
|
||
return element;
|
||
}, function (v) {
|
||
return v;
|
||
});
|
||
};
|
||
|
||
return {
|
||
doc: doc
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.photon.Writer',
|
||
|
||
[
|
||
'ephox.photon.Reader',
|
||
'ephox.sugar.api.Body'
|
||
],
|
||
|
||
function (Reader, Body) {
|
||
|
||
var write = function (element, content) {
|
||
if (!Body.inBody(element)) throw 'Internal error: attempted to write to an iframe that is not in the DOM';
|
||
|
||
var doc = Reader.doc(element);
|
||
var dom = doc.dom();
|
||
dom.open();
|
||
dom.writeln(content);
|
||
dom.close();
|
||
};
|
||
|
||
return {
|
||
write: write
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Ready',
|
||
|
||
[
|
||
'ephox.sugar.api.DomEvent',
|
||
'ephox.sugar.api.Element',
|
||
'global!document'
|
||
],
|
||
|
||
function (DomEvent, Element, document) {
|
||
var execute = function (f) {
|
||
/*
|
||
* We only use this in one place, so creating one listener per ready request is more optimal than managing
|
||
* a single event with a queue of functions.
|
||
*/
|
||
|
||
/* The general spec describes three states: loading, complete, and interactive.
|
||
* https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness
|
||
*
|
||
* loading: the document is not ready (still loading)
|
||
* interactive: the document is ready, but sub-resources are still loading
|
||
* complete: the document is completely ready.
|
||
*
|
||
* Note, IE and w3 schools talk about: uninitialized and loaded. We may have to handle them in the future.
|
||
*/
|
||
if (document.readyState === 'complete' || document.readyState === 'interactive') f();
|
||
else {
|
||
// Note that this fires when DOM manipulation is allowed, but before all resources are
|
||
// available. This is the best practice but might be a bit weird.
|
||
var listener = DomEvent.bind(Element.fromDom(document), 'DOMContentLoaded', function () { // IE9 minimum
|
||
f();
|
||
listener.unbind();
|
||
});
|
||
}
|
||
};
|
||
|
||
return {
|
||
execute: execute
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.keurig.loader.GWTLoader',
|
||
|
||
[
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.oilspill.callback.Globaliser',
|
||
'ephox.peanut.Fun',
|
||
'ephox.peanut.Thunk',
|
||
'ephox.perhaps.Option',
|
||
'ephox.photon.Writer',
|
||
'ephox.sugar.api.Body',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.DomEvent',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Ready',
|
||
'ephox.sugar.api.Remove'
|
||
],
|
||
|
||
function (PlatformDetection, Globaliser, Fun, Thunk, Option, Writer, Body, Css, DomEvent, Element, Insert, Ready, Remove) {
|
||
var globaliser = Globaliser.install('ephox.keurig.init');
|
||
var cleanFunction = Option.none();
|
||
|
||
// IE11 can't handle loading GWT in an iframe, not with the collapse into a single JS file instead of 5 HTML files.
|
||
// It seems to load ok in WordTest.html, though, which does it directly - worth thinking about if we ever need GWT in IE.
|
||
var load = PlatformDetection.detect().browser.isIE() ? Fun.noop : Thunk.cached(function (baseUrl) {
|
||
var container = Element.fromTag('div');
|
||
if (baseUrl === undefined) throw 'baseUrl was undefined';
|
||
|
||
var iframe = Element.fromTag('iframe');
|
||
|
||
Css.setAll(container, {
|
||
display: 'none'
|
||
});
|
||
|
||
var frameLoad = DomEvent.bind(iframe, 'load', function () {
|
||
// called when gwt moudle has finished loading.
|
||
var gwtInitCallback = function (cleanDocument) {
|
||
cleanFunction = Option.some(cleanDocument);
|
||
if (!PlatformDetection.detect().browser.isSafari()) {
|
||
// Safari can't handle executing JS in an iframe that has been removed from the page
|
||
Remove.remove(container);
|
||
}
|
||
};
|
||
|
||
var gwtInitRef = globaliser.ephemeral(gwtInitCallback);
|
||
var gwtjs = baseUrl + '/wordimport.js';
|
||
|
||
Writer.write(iframe,
|
||
'<script type="text/javascript" src="' + gwtjs + '"></script>' +
|
||
'<script type="text/javascript">' +
|
||
'function gwtInited () {' +
|
||
'parent.window.' + gwtInitRef + '(com.ephox.keurig.WordCleaner.cleanDocument);' +
|
||
'};' +
|
||
'</script>');
|
||
frameLoad.unbind();
|
||
});
|
||
|
||
Ready.execute(function () {
|
||
Insert.append(Body.body(), container);
|
||
Insert.append(container, iframe);
|
||
});
|
||
});
|
||
|
||
var cleanDocument = function (wordHTML, merge) {
|
||
return cleanFunction.map(function (f) {
|
||
// TODO: This should probably do something with the log instead of throwing it away in the Java side
|
||
return f(wordHTML, merge);
|
||
});
|
||
};
|
||
|
||
var ready = function () {
|
||
return cleanFunction.isSome();
|
||
};
|
||
|
||
return {
|
||
load: load,
|
||
cleanDocument: cleanDocument,
|
||
ready: ready
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.keurig.api.WordCleaner',
|
||
|
||
[
|
||
'ephox.keurig.loader.GWTLoader'
|
||
],
|
||
|
||
function (GWTLoader) {
|
||
return function (baseUrl) {
|
||
if (!GWTLoader.ready()) GWTLoader.load(baseUrl);
|
||
|
||
return {
|
||
cleanDocument: GWTLoader.cleanDocument
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.photon.Sandbox',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.photon.Writer',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.DomEvent',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Remove',
|
||
'global!setTimeout'
|
||
],
|
||
|
||
function (Fun, Writer, Css, DomEvent, Element, Insert, Remove, setTimeout) {
|
||
return function (uiContainer) {
|
||
/**
|
||
* Creates a sandbox to play in.
|
||
*
|
||
* Asynchronously creates an iframe, runs the synchronous function `f` on the DOM, and then passes the result to the callback.
|
||
*
|
||
* This is done so that the sandbox can guarantee the iframe has been removed from the page, and available for garbage collection, before the callback is executed.
|
||
*
|
||
* html:
|
||
* source to load into the iframe
|
||
* f: (document -> body -> A)
|
||
* function that operates on the iframe DOM, passed both document reference and body element
|
||
* callback: (A -> Unit)
|
||
* function that receives the output of `f` when the iframe has been cleaned up
|
||
*/
|
||
var play = function (html, f, callback) {
|
||
var outputContainer = Element.fromTag('div');
|
||
var iframe = Element.fromTag('iframe');
|
||
|
||
Css.setAll(outputContainer, {
|
||
display: 'none'
|
||
});
|
||
|
||
var load = DomEvent.bind(iframe, 'load', function () {
|
||
Writer.write(iframe, html);
|
||
|
||
var rawDoc = iframe.dom().contentWindow.document;
|
||
if (rawDoc === undefined) throw "sandbox iframe load event did not fire correctly";
|
||
var doc = Element.fromDom(rawDoc);
|
||
|
||
var rawBody = rawDoc.body;
|
||
if (rawBody === undefined) throw "sandbox iframe does not have a body";
|
||
var body = Element.fromDom(rawBody);
|
||
|
||
// cache
|
||
var result = f(doc, body);
|
||
|
||
// unbind and remove everything
|
||
load.unbind();
|
||
Remove.remove(outputContainer);
|
||
|
||
// setTimeout should allow the garbage collector to cleanup if necessary
|
||
setTimeout(Fun.curry(callback, result), 0);
|
||
});
|
||
Insert.append(outputContainer, iframe);
|
||
Insert.append(uiContainer, outputContainer);
|
||
};
|
||
|
||
return {
|
||
play: play
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.impl.NodeValue',
|
||
|
||
[
|
||
'ephox.perhaps.Option',
|
||
'global!Error'
|
||
],
|
||
|
||
function (Option, Error) {
|
||
return function (is, name) {
|
||
var get = function (element) {
|
||
if (!is(element)) throw new Error('Can only get ' + name + ' value of a ' + name + ' node');
|
||
return getOption(element).getOr('');
|
||
};
|
||
|
||
var getOption = function (element) {
|
||
try {
|
||
return is(element) ? Option.some(element.dom().nodeValue) : Option.none();
|
||
} catch (e) {
|
||
return Option.none(); // Prevent IE10 from throwing exception when setting parent innerHTML clobbers (TBIO-451).
|
||
}
|
||
};
|
||
|
||
var set = function (element, value) {
|
||
if (!is(element)) throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node');
|
||
element.dom().nodeValue = value;
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
getOption: getOption,
|
||
set: set
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Comment',
|
||
|
||
[
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.impl.NodeValue'
|
||
],
|
||
|
||
function (Node, NodeValue) {
|
||
var api = NodeValue(Node.isComment, 'comment');
|
||
|
||
var get = function (element) {
|
||
return api.get(element);
|
||
};
|
||
|
||
var getOption = function (element) {
|
||
return api.getOption(element);
|
||
};
|
||
|
||
var set = function (element, value) {
|
||
api.set(element, value);
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
getOption: getOption,
|
||
set: set
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Html',
|
||
|
||
[
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert'
|
||
],
|
||
|
||
function ( Element, Insert) {
|
||
var get = function (element) {
|
||
return element.dom().innerHTML;
|
||
};
|
||
|
||
var set = function (element, content) {
|
||
element.dom().innerHTML = content;
|
||
};
|
||
|
||
var getOuter = function (element) {
|
||
var container = Element.fromTag('div');
|
||
var clone = Element.fromDom(element.dom().cloneNode(true));
|
||
Insert.append(container, clone);
|
||
return get(container);
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
set: set,
|
||
getOuter: getOuter
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.css.Set',
|
||
|
||
[
|
||
'ephox.sugar.api.Insert'
|
||
],
|
||
|
||
function (Insert) {
|
||
|
||
var setCss = function (style, css, element) {
|
||
if (style.dom().styleSheet)
|
||
style.dom().styleSheet.cssText = css; // IE
|
||
else
|
||
Insert.append(style, element);
|
||
};
|
||
|
||
return {
|
||
setCss: setCss
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.util.Regex',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var escape = function (text) {
|
||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||
};
|
||
|
||
return {
|
||
escape: escape
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
ephox.bolt.module.api.define("global!RegExp", [], function () { return RegExp; });
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.css.Url',
|
||
|
||
[
|
||
'ephox.compass.Obj',
|
||
'ephox.vogue.util.Regex',
|
||
'global!RegExp'
|
||
],
|
||
|
||
function (Obj, Regex, RegExp) {
|
||
var replace = function (css, urlPrefix, replacement) {
|
||
var r = new RegExp('url\\(\\s*[\'"]?' + Regex.escape(urlPrefix) + '(.*?)[\'"]?\\s*\\)', 'g');
|
||
return css.replace(r, 'url("' + replacement + '$1")');
|
||
};
|
||
|
||
var replaceMany = function (css, replacements) {
|
||
var current = css;
|
||
Obj.each(replacements, function (value, key) {
|
||
current = replace(current, key, value);
|
||
});
|
||
return current;
|
||
};
|
||
|
||
return {
|
||
replace: replace,
|
||
replaceMany: replaceMany
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.api.DocStyle',
|
||
|
||
[
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'ephox.vogue.css.Set',
|
||
'ephox.vogue.css.Url',
|
||
'global!Array'
|
||
],
|
||
|
||
function (Attr, Element, Insert, SelectorFind, Set, Url, Array) {
|
||
|
||
var styletag = function (doc) {
|
||
var style = Element.fromTag('style', doc.dom());
|
||
Attr.set(style, 'type', 'text/css');
|
||
return style;
|
||
};
|
||
|
||
var setCss = function (style, css, doc) {
|
||
Set.setCss(style, css, Element.fromText(css, doc.dom()));
|
||
};
|
||
|
||
var inject = function (css, replacements, doc) {
|
||
var style = styletag(doc);
|
||
var replacedCss = replacements === undefined ? css : Url.replaceMany(css, replacements);
|
||
setCss(style, replacedCss, doc);
|
||
var head = SelectorFind.descendant(doc, 'head').getOrDie();
|
||
Insert.append(head, style);
|
||
};
|
||
|
||
var stylesheets = function (doc) {
|
||
var domStyleSheets = doc.dom().styleSheets;
|
||
return Array.prototype.slice.call(domStyleSheets);
|
||
};
|
||
|
||
return {
|
||
stylesheets: stylesheets,
|
||
inject: inject
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.css.Rules',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Arr, Struct) {
|
||
var ruleStruct = Struct.immutable('selector', 'style');
|
||
|
||
var extract = function (stylesheet) {
|
||
var domRules = stylesheet.cssRules;
|
||
return Arr.map(domRules, function (rule) {
|
||
var selector = rule.selectorText;
|
||
var style = rule.style.cssText;
|
||
if (style === undefined) {
|
||
// This should be picked up in testing, and perhaps delete the check eventually
|
||
throw "WARNING: Browser does not support cssText property";
|
||
}
|
||
return ruleStruct(selector, style);
|
||
});
|
||
};
|
||
|
||
var extractAll = function (stylesheets) {
|
||
return Arr.bind(stylesheets, extract);
|
||
};
|
||
|
||
return {
|
||
extract: extract,
|
||
extractAll: extractAll
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.vogue.api.Rules',
|
||
|
||
[
|
||
'ephox.vogue.css.Rules'
|
||
],
|
||
|
||
function (Rules) {
|
||
var extract = function (stylesheet) {
|
||
return Rules.extract(stylesheet);
|
||
};
|
||
|
||
var extractAll = function (stylesheets) {
|
||
return Rules.extractAll(stylesheets);
|
||
};
|
||
|
||
return {
|
||
extract: extract,
|
||
extractAll: extractAll
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.html.HtmlPaste',
|
||
|
||
[
|
||
'ephox.cement.html.Transform',
|
||
'ephox.cement.images.ImageReference',
|
||
'ephox.classify.Type',
|
||
'ephox.compass.Arr',
|
||
'ephox.keurig.api.WordCleaner',
|
||
'ephox.peanut.Fun',
|
||
'ephox.photon.Sandbox',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Comment',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Elements',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'ephox.sugar.api.Traverse',
|
||
'ephox.vogue.api.DocStyle',
|
||
'ephox.vogue.api.Rules',
|
||
'global!document'
|
||
],
|
||
|
||
function (Transform, ImageReference, Type, Arr, WordCleaner, Fun, Sandbox, Event, Events, Attr, Class, Comment, Element, Elements, Html, Remove, SelectorFilter, Traverse, DocStyle, Rules, document) {
|
||
var flagAttribute = 'data-textbox-image';
|
||
|
||
var emptyString = function (s) {
|
||
return s === undefined || s === null || s.length === 0;
|
||
};
|
||
|
||
var stripImageSources = function (html) {
|
||
var count = 1;
|
||
return html.replace(/(<img[^>]*)src=".*?"/g, function (match, p1, offset) {
|
||
// the actual contents are irrelevant, it just needs to be unique
|
||
return p1 + flagAttribute + '="' + count++ + '"';
|
||
});
|
||
};
|
||
|
||
var removeFragmentComments = function (body) {
|
||
var bodyChildren = Traverse.children(body);
|
||
Arr.each(bodyChildren, function (c) {
|
||
Comment.getOption(c).each(function (commentText) {
|
||
if (commentText === 'StartFragment' || commentText === 'EndFragment') {
|
||
Remove.remove(c);
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
var insertRtfCorrelation = function (sourceImageList, tordImages) {
|
||
Arr.each(tordImages, function (img) {
|
||
var imageCounter = Attr.get(img, flagAttribute);
|
||
Arr.each(sourceImageList, function (imgData) {
|
||
var imgAttrs = imgData.img();
|
||
var vshapeAttrs = imgData.vshape();
|
||
if (imgAttrs[flagAttribute] == imageCounter) {
|
||
// NOTE: If we discover more than 2 possible attributes, de-dupe with RtfImage somehow
|
||
var spid = vshapeAttrs['o:spid'];
|
||
var vshapeId = spid === undefined ? vshapeAttrs.id : spid;
|
||
|
||
Transform.rotateImage(img, vshapeAttrs);
|
||
|
||
Class.add(img, 'rtf-data-image');
|
||
Attr.set(img, 'data-image-id', vshapeId.substr('_x0000_'.length));
|
||
Attr.set(img, 'data-image-type', 'code');
|
||
Attr.remove(img, flagAttribute);
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
var mergeInlineStyles = function (body, stylesheets) {
|
||
var rules = Rules.extractAll(stylesheets);
|
||
Arr.each(rules, function (rule) {
|
||
|
||
var matchingElements = SelectorFilter.descendants(body, rule.selector());
|
||
|
||
Arr.each(matchingElements, function (element) {
|
||
Attr.remove(element, 'class');
|
||
Attr.set(element, 'style', rule.style());
|
||
});
|
||
});
|
||
};
|
||
|
||
var tordPostProcessor = function (sourceImageList, mergeStyles) {
|
||
var sandbox = Sandbox(Element.fromDom(document.body));
|
||
return function (dump, callback) {
|
||
// loading dump into the sandbox *will* perform some built-in browser cleanup operations,
|
||
// we are hoping this is a suitable replacement for the use of HTML Tidy in ELJ.
|
||
sandbox.play(dump, function (iframeDoc, body) {
|
||
var images = SelectorFilter.descendants(body, 'img');
|
||
|
||
// post-tord DOM filters
|
||
removeFragmentComments(body);
|
||
insertRtfCorrelation(sourceImageList, images);
|
||
if (mergeStyles) {
|
||
mergeInlineStyles(body, DocStyle.stylesheets(iframeDoc));
|
||
}
|
||
|
||
return Html.get(body);
|
||
}, callback);
|
||
};
|
||
};
|
||
|
||
var cleanEnd = function (raw) {
|
||
// Trim any weirdness that exists after the closing HTML tag.
|
||
var i = raw.indexOf('</html>');
|
||
return (i > -1) ? raw.substr(0, i + '</html>'.length) : raw;
|
||
};
|
||
|
||
return function (mergeSettings, pasteSettings) {
|
||
var cleaner = WordCleaner(pasteSettings.baseUrl);
|
||
|
||
var events = Events.create({
|
||
paste: Event(['elements', 'assets']),
|
||
error: Event(['message'])
|
||
});
|
||
|
||
var handler = function (raw) {
|
||
var html = cleanEnd(raw);
|
||
// This will only be called if we have word styles, so force true
|
||
mergeSettings.get(true, function (settings) {
|
||
var mergeStyles = settings.mergeOfficeStyles;
|
||
|
||
// remove local file references, so that loading the HTML into a DOM does not trigger console warnings
|
||
var safeHtml = stripImageSources(html);
|
||
|
||
var sourceImageList = ImageReference.extract(safeHtml);
|
||
|
||
var postProcess = tordPostProcessor(sourceImageList, mergeStyles);
|
||
|
||
cleaner.cleanDocument(safeHtml, mergeStyles).fold(function () {
|
||
events.trigger.error('errors.paste.word.notready');
|
||
}, function (dump) {
|
||
if (emptyString(dump)) events.trigger.paste([], []);
|
||
else {
|
||
// postProcess is basically String -> Future (String)
|
||
postProcess(dump, function (tordHtml) {
|
||
var elements = Elements.fromHtml(tordHtml);
|
||
events.trigger.paste(elements, []);
|
||
});
|
||
}
|
||
|
||
});
|
||
});
|
||
return true;
|
||
};
|
||
|
||
return {
|
||
handler: handler,
|
||
isSupported: Fun.constant(true),
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.images.ImagePaste',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.hermes.api.ImageAsset',
|
||
'ephox.hermes.api.ImageExtract',
|
||
'ephox.peanut.Fun',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'global!console'
|
||
],
|
||
|
||
function (Arr, PlatformDetection, ImageAsset, ImageExtract, Fun, Event, Events, Attr, Element, console) {
|
||
return function (pasteSettings) {
|
||
var platform = PlatformDetection.detect();
|
||
var CAPTUTED_EVENT = true;
|
||
|
||
//IE & FF handle image paste conversion into base64 data URIs automatically
|
||
var isSupported = !platform.browser.isIE() && !platform.browser.isFirefox();
|
||
|
||
var events = Events.create({
|
||
paste: Event(['elements', 'assets']),
|
||
error: Event(['message'])
|
||
});
|
||
|
||
var readImages = function (assets) {
|
||
return Arr.bind(assets, function (asset) {
|
||
return ImageAsset.cata(asset,
|
||
function (id, file, objurl, data) {
|
||
// create an image and inject it at the current selection
|
||
var image = Element.fromTag('img');
|
||
Attr.set(image, 'src', objurl);
|
||
return image;
|
||
},
|
||
function (id, url, raw) {
|
||
// TODO: Is this the best way?
|
||
console.log('Internal error: Paste operation produced an image URL instead of a Data URI: ', url);
|
||
}
|
||
);
|
||
});
|
||
};
|
||
|
||
var actualHandler = function (clipboardItems) {
|
||
var images = Arr.filter(clipboardItems, function (item) {
|
||
return item.kind === 'file' && /image/.test(item.type);
|
||
});
|
||
|
||
var files = Arr.map(images, function (image) {
|
||
return image.getAsFile();
|
||
});
|
||
|
||
ImageExtract.toAssets(files, function (assets) {
|
||
// perform the insert (SmartPaste handles undo and focus trickery)
|
||
var elements = readImages(assets);
|
||
events.trigger.paste(elements, assets);
|
||
});
|
||
//prevent other content from coming through
|
||
return CAPTUTED_EVENT;
|
||
};
|
||
|
||
var safariHandler = function () {
|
||
events.trigger.error('safari.imagepaste');
|
||
// prevent default paste
|
||
return CAPTUTED_EVENT;
|
||
};
|
||
|
||
var imageDisabled = function () {
|
||
events.trigger.error('errors.local.images.disallowed');
|
||
return CAPTUTED_EVENT;
|
||
};
|
||
|
||
var imageHandler = platform.browser.isSafari() ? safariHandler : actualHandler;
|
||
|
||
var handler = pasteSettings.allowLocalImages ? imageHandler : imageDisabled;
|
||
|
||
return {
|
||
handler: handler,
|
||
isSupported: Fun.constant(isSupported),
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.api.CementConstants',
|
||
|
||
[
|
||
'ephox.cement.style.Styles',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Styles, Fun) {
|
||
|
||
/*
|
||
The filter history may not quite work as I would hope. The problem it is likely to
|
||
have is that it might be the content's selection as well, which means that we are
|
||
changing what is about to used as serialisation ... likely leading to issues. I think
|
||
it just only sets valid selections, so it will probably be ok .. but the cursor will
|
||
be jarring.
|
||
|
||
The paste bin class is added when the paste event is being triggered in the setTimeout.
|
||
That may be too late for it not to end up in the undo history, but currently it seems
|
||
like it will possibly work. Adding the class directly to the element would be more reliable,
|
||
but I haven't thought of a clean enough way to do that yet.
|
||
*/
|
||
var bin = Styles.resolve('smartpaste-eph-bin');
|
||
|
||
return {
|
||
binStyle: Fun.constant(bin)
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.knoch.core.Bounce',
|
||
|
||
[
|
||
'global!Array'
|
||
],
|
||
|
||
function (Array) {
|
||
|
||
var bounce = function(f) {
|
||
return function() {
|
||
var args = Array.prototype.slice.call(arguments);
|
||
var me = this;
|
||
setTimeout(function() {
|
||
f.apply(me, args);
|
||
}, 0);
|
||
};
|
||
};
|
||
|
||
return {
|
||
bounce: bounce
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.knoch.core.FutureOps',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
|
||
return function (nu, get) {
|
||
|
||
/** map :: this Future a -> (a -> b) -> Future b */
|
||
var map = function(fab) {
|
||
return nu(function(callback) {
|
||
get(function(a) {
|
||
callback(fab(a));
|
||
});
|
||
});
|
||
};
|
||
|
||
/** bind :: this Future a -> (a -> Future b) -> Future b */
|
||
var bind = function(aFutureB) {
|
||
return nu(function(callback) {
|
||
get(function(a) {
|
||
aFutureB(a).get(callback);
|
||
});
|
||
});
|
||
};
|
||
|
||
/** anonBind :: this Future a -> Future b -> Future b
|
||
* Returns a future, which evaluates the first future, ignores the result, then evaluates the second.
|
||
*/
|
||
var anonBind = function(futureB) {
|
||
return nu(function(callback) {
|
||
get(function(a) {
|
||
futureB.get(callback);
|
||
});
|
||
});
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
map: map,
|
||
bind: bind,
|
||
anonBind: anonBind
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.knoch.future.Future',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.knoch.core.Bounce',
|
||
'ephox.knoch.core.FutureOps'
|
||
],
|
||
|
||
/** A future value that is evaluated on demand. The base function is re-evaluated each time 'get' is called. */
|
||
function (Arr, Bounce, FutureOps) {
|
||
|
||
// baseFn is a function(callback) { ... }
|
||
var nu = function(baseFn) {
|
||
|
||
var get = function(callback) {
|
||
baseFn(Bounce.bounce(callback));
|
||
};
|
||
|
||
return FutureOps(nu, get);
|
||
};
|
||
|
||
/** [Future a] -> Future [a] */
|
||
var par = function(futures) {
|
||
return nu(function(callback) {
|
||
var r = [];
|
||
var count = 0;
|
||
|
||
var cb = function(i) {
|
||
return function(value) {
|
||
r[i] = value;
|
||
count++;
|
||
if (count >= futures.length) {
|
||
callback(r);
|
||
}
|
||
};
|
||
};
|
||
|
||
if (futures.length === 0) {
|
||
callback([]);
|
||
} else {
|
||
Arr.each(futures, function(future, i) {
|
||
future.get(cb(i));
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
/** [a] -> (a -> Future b) -> Future [b] */
|
||
var mapM = function(as, fn) {
|
||
return par(Arr.map(as, fn));
|
||
};
|
||
|
||
/** (Future a, Future b) -> ((a, b) -> c) -> Future C
|
||
* Executes the two futures in "parallel" with respect to browser JS threading.
|
||
*/
|
||
var lift2 = function(fa, fb, abc) {
|
||
return nu(function(callback) {
|
||
var completeA = false;
|
||
var completeB = false;
|
||
var valueA = undefined;
|
||
var valueB = undefined;
|
||
|
||
var done = function() {
|
||
if (completeA && completeB) {
|
||
var c = abc(valueA, valueB);
|
||
callback(c);
|
||
}
|
||
};
|
||
|
||
fa.get(function(a) {
|
||
valueA = a;
|
||
completeA = true;
|
||
done();
|
||
});
|
||
|
||
fb.get(function(b) {
|
||
valueB = b;
|
||
completeB = true;
|
||
done();
|
||
});
|
||
});
|
||
};
|
||
|
||
return {
|
||
nu: nu,
|
||
par: par,
|
||
mapM: mapM,
|
||
lift2: lift2
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.knoch.future.CachedFuture',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.highway.Merger',
|
||
'ephox.knoch.core.Bounce',
|
||
'ephox.knoch.core.FutureOps',
|
||
'ephox.knoch.future.Future',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
/**
|
||
* A future value.
|
||
* The base function is evaluated eagerly, and only evaluated once.
|
||
* Each call to 'get' queues a callback, which is invoked when the value is available.
|
||
*/
|
||
function (Arr, Merger, Bounce, FutureOps, Future, Fun, Option) {
|
||
|
||
// f is a function(callback) { ... }
|
||
var nu = function (baseFn) {
|
||
|
||
var data = Option.none();
|
||
var callbacks = [];
|
||
|
||
var get = function (callback) {
|
||
isSet() ? call(callback) : callbacks.push(callback);
|
||
};
|
||
|
||
var set = function (x) {
|
||
data = Option.some(x);
|
||
run(callbacks);
|
||
callbacks = [];
|
||
};
|
||
|
||
var isSet = function() {
|
||
return data.isSome();
|
||
};
|
||
|
||
var run = function (cbs) {
|
||
Arr.each(cbs, call);
|
||
};
|
||
|
||
var call = function(cb) {
|
||
data.each(function(x) {
|
||
Bounce.bounce(cb)(x);
|
||
});
|
||
};
|
||
|
||
Future.nu(baseFn).get(set);
|
||
|
||
var ops = FutureOps(nu, get);
|
||
|
||
return Merger.merge(ops, {
|
||
isSet: isSet
|
||
});
|
||
};
|
||
|
||
return {
|
||
nu: nu
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.pastiche.IeBlob',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.epithet.Resolve',
|
||
'ephox.hermes.api.ImageExtract',
|
||
'ephox.knoch.future.CachedFuture',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Arr, Resolve, ImageExtract, CachedFuture, Fun, Option) {
|
||
var convertURL = function (raw) {
|
||
return raw.convertURL !== undefined ? raw.convertURL // Use standard if available.
|
||
: raw.msConvertURL !== undefined ? raw.msConvertURL
|
||
: undefined;
|
||
};
|
||
|
||
var convert = function (raw) {
|
||
// IE11 defines data on the window, but requires the event to convert... /headdesk
|
||
var files = Resolve.resolve('window.clipboardData.files');
|
||
|
||
var convert = convertURL(raw);
|
||
|
||
if (files !== undefined && convert !== undefined && files.length > 0) {
|
||
var blobs = Arr.map(files, function (file) {
|
||
var blob = ImageExtract.blob(file);
|
||
convert.apply(raw, [file, 'specified', blob.objurl()]);
|
||
|
||
return blob;
|
||
});
|
||
|
||
// do the async operation in a future
|
||
var future = CachedFuture.nu(function (callback) {
|
||
ImageExtract.fromBlobs(blobs, callback);
|
||
});
|
||
|
||
// initiate the conversion immediately
|
||
future.get(Fun.noop);
|
||
|
||
return Option.some(future);
|
||
} else {
|
||
return Option.none();
|
||
}
|
||
};
|
||
|
||
return {
|
||
convert: convert
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.api.Situ',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var on = function (element, offset) {
|
||
return folder(function (b, o, a) {
|
||
return o(element, offset);
|
||
});
|
||
};
|
||
|
||
var before = function (element) {
|
||
return folder(function (b, o, a) {
|
||
return b(element);
|
||
});
|
||
};
|
||
|
||
var after = function (element) {
|
||
return folder(function (b, o, a) {
|
||
return a(element);
|
||
});
|
||
};
|
||
|
||
|
||
var folder = function(fold) {
|
||
return {
|
||
fold: fold
|
||
};
|
||
};
|
||
|
||
return {
|
||
on: on,
|
||
before: before,
|
||
after: after
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.api.SelectionRange',
|
||
|
||
[
|
||
'ephox.fussy.api.Situ',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Situ, Struct, Element) {
|
||
var read = Struct.immutable('start', 'soffset', 'finish', 'foffset');
|
||
var general = Struct.immutable('start', 'soffset', 'finish', 'foffset');
|
||
var write = Struct.immutable('start', 'finish');
|
||
|
||
var writeFromNative = function (range) {
|
||
var start = Element.fromDom(range.startContainer);
|
||
var finish = Element.fromDom(range.endContainer);
|
||
return write(
|
||
Situ.on(start, range.startOffset),
|
||
Situ.on(finish, range.endOffset)
|
||
);
|
||
};
|
||
|
||
return {
|
||
read: read,
|
||
general: general,
|
||
write: write,
|
||
writeFromNative: writeFromNative
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.api.Supported',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var run = function (win, w3c) {
|
||
// this is scaffolding for what was an alternate selection model.
|
||
// We now only have one but the concept could be useful later.
|
||
if (win.getSelection) return w3c(win, win.getSelection());
|
||
else throw 'No selection model supported.';
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.DocumentPosition',
|
||
|
||
[
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Compare, Traverse ) {
|
||
var after = function (start, soffset, finish, foffset) {
|
||
var doc = Traverse.owner(start);
|
||
|
||
// TODO: Find a sensible place to put the native range creation code.
|
||
var rng = doc.dom().createRange();
|
||
rng.setStart(start.dom(), soffset);
|
||
rng.setEnd(finish.dom(), foffset);
|
||
|
||
var same = Compare.eq(start, finish) && soffset === foffset;
|
||
return rng.collapsed && !same;
|
||
};
|
||
|
||
return {
|
||
after: after
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.wwwc.Directions',
|
||
|
||
[
|
||
'ephox.fussy.api.SelectionRange',
|
||
'ephox.sugar.api.DocumentPosition',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (SelectionRange, DocumentPosition, Element, Traverse) {
|
||
var isReversed = function (model) {
|
||
return DocumentPosition.after(Element.fromDom(model.anchorNode), model.anchorOffset, Element.fromDom(model.focusNode), model.focusOffset);
|
||
};
|
||
|
||
var flipGet = function (model, range) {
|
||
var start = Element.fromDom(range.startContainer);
|
||
var finish = Element.fromDom(range.endContainer);
|
||
return isReversed(model) ?
|
||
SelectionRange.read(finish, range.endOffset, start, range.startOffset) :
|
||
SelectionRange.read(start, range.startOffset, finish, range.endOffset);
|
||
};
|
||
|
||
var isRtlGet = function (model) {
|
||
return isReversed(model);
|
||
};
|
||
|
||
var flipSet = function (start, startOffset, end, endOffset) {
|
||
return function (model) {
|
||
if (model.extend) {
|
||
model.collapse(start.dom(), startOffset);
|
||
model.extend(end.dom(), endOffset);
|
||
} else {
|
||
// this is IE... we can’t have a backwards range, so reverse it.
|
||
var range = Traverse.owner(start).dom().createRange();
|
||
range.setStart(end.dom(), endOffset);
|
||
range.setEnd(start.dom(), startOffset);
|
||
model.removeAllRanges();
|
||
model.addRange(range);
|
||
}
|
||
};
|
||
};
|
||
|
||
var isRtlSet = function (start, startOffset, end, endOffset) {
|
||
return DocumentPosition.after(start, startOffset, end, endOffset);
|
||
};
|
||
|
||
var read = function () {
|
||
return {
|
||
flip: flipGet,
|
||
isRtl: isRtlGet
|
||
};
|
||
};
|
||
|
||
var write = function () {
|
||
return {
|
||
flip: flipSet,
|
||
isRtl: isRtlSet
|
||
};
|
||
};
|
||
|
||
return {
|
||
read: read,
|
||
write: write
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.wwwc.DomRange',
|
||
|
||
[
|
||
'ephox.fussy.api.SelectionRange',
|
||
'ephox.fussy.wwwc.Directions',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.DocumentPosition',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (SelectionRange, Directions, Option, DocumentPosition, Element) {
|
||
|
||
/*
|
||
* The approach here is to create a range using the selection. If it collapses,
|
||
* and the inverse of the selection does not collapse ... then it is a backwards
|
||
* selection.
|
||
*/
|
||
var reversed = function (win, selection) {
|
||
// Calculate the range going from start -> finish
|
||
var startToFinish = toNativeFrom(win, selection.start(), selection.finish());
|
||
// If it is collapsed, there is a chance that it only collapsed because it was RTL
|
||
if (startToFinish.collapsed === true) {
|
||
// Check that the inverted selection isn't collapsed.
|
||
// If the inverted selection is not collapsed ... it is a backwards selection, otherwise it is just collapsed anyway
|
||
var finishToStart = toNativeFrom(win, selection.finish(), selection.start());
|
||
return finishToStart.collapsed === true ? Option.none() : Option.some(SelectionRange.general(
|
||
Element.fromDom(finishToStart.endContainer),
|
||
finishToStart.endOffset,
|
||
Element.fromDom(finishToStart.startContainer),
|
||
finishToStart.startOffset
|
||
));
|
||
} else {
|
||
return Option.none();
|
||
}
|
||
};
|
||
|
||
var forceRange = function (win, selection) {
|
||
var range = toNativeFrom(win, selection.start(), selection.finish());
|
||
return range.collapsed === true ? toNativeFrom(win, selection.finish(), selection.start()) : range;
|
||
};
|
||
|
||
var toNativeFrom = function (win, start, finish) {
|
||
var range = create(win);
|
||
|
||
start.fold(function (e) {
|
||
range.setStartBefore(e.dom());
|
||
}, function (e, o) {
|
||
range.setStart(e.dom(), o);
|
||
}, function (e) {
|
||
range.setStartAfter(e.dom());
|
||
});
|
||
|
||
finish.fold(function (e) {
|
||
range.setEndBefore(e.dom());
|
||
}, function (e, o) {
|
||
range.setEnd(e.dom(), o);
|
||
}, function (e) {
|
||
range.setEndAfter(e.dom());
|
||
});
|
||
|
||
return range;
|
||
};
|
||
|
||
var toNative = function (win, selection) {
|
||
return toNativeFrom(win, selection.start(), selection.finish());
|
||
};
|
||
|
||
var toExactNative = function (win, s, so, e, eo) {
|
||
var backwards = DocumentPosition.after(s, so, e, eo);
|
||
var range = win.document.createRange();
|
||
if (backwards) {
|
||
range.setStart(e.dom(), eo);
|
||
range.setEnd(s.dom(), so);
|
||
} else {
|
||
range.setStart(s.dom(), so);
|
||
range.setEnd(e.dom(), eo);
|
||
}
|
||
return range;
|
||
};
|
||
|
||
var forwards = function (win, selection) {
|
||
var range = toNative(win, selection);
|
||
|
||
return function (model) {
|
||
model.addRange(range);
|
||
};
|
||
};
|
||
|
||
var build = function (win, selection) {
|
||
var backwards = reversed(win, selection);
|
||
return backwards.fold(function () {
|
||
return forwards(win, selection);
|
||
}, function (range) {
|
||
return Directions.write().flip(range.start(), range.soffset(), range.finish(), range.foffset());
|
||
});
|
||
};
|
||
|
||
var create = function (win) {
|
||
return win.document.createRange();
|
||
};
|
||
|
||
return {
|
||
create: create,
|
||
build: build,
|
||
toNative: toNative,
|
||
forceRange: forceRange,
|
||
toExactNative: toExactNative
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.search.Within',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.fussy.wwwc.DomRange',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'ephox.sugar.api.Selectors'
|
||
],
|
||
|
||
function (Arr, DomRange, Element, Node, SelectorFilter, Selectors) {
|
||
// Adapted from: http://stackoverflow.com/questions/5605401/insert-link-in-contenteditable-element
|
||
var inRange = function (tempRange, range, element) {
|
||
tempRange.selectNodeContents(element.dom());
|
||
return tempRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && tempRange.compareBoundaryPoints(range.START_TO_END, range) > -1;
|
||
};
|
||
|
||
var withinContainer = function (win, container, range, selector) {
|
||
var tempRange = win.document.createRange();
|
||
var self = Selectors.is(container, selector) ? [ container ] : [];
|
||
var elements = self.concat(SelectorFilter.descendants(container, selector));
|
||
return Arr.filter(elements, function (elem) {
|
||
return inRange(tempRange, range, elem);
|
||
});
|
||
};
|
||
|
||
var find = function (win, raw, selector) {
|
||
// Reverse the selection if it is RTL when doing the comparison
|
||
var range = DomRange.forceRange(win, raw);
|
||
var container = Element.fromDom(range.commonAncestorContainer);
|
||
// Note, this might need to change when we have to start looking for non elements.
|
||
return Node.isElement(container) ? withinContainer(win, container, range, selector) : [];
|
||
};
|
||
|
||
return {
|
||
find: find
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.wwwc.Prefilter',
|
||
|
||
[
|
||
'ephox.fussy.api.SelectionRange',
|
||
'ephox.fussy.api.Situ',
|
||
'ephox.sugar.api.Node'
|
||
],
|
||
|
||
function (SelectionRange, Situ, Node) {
|
||
var beforeBr = function (element, offset) {
|
||
return Node.name(element) === 'br' ? Situ.before(element) : Situ.on(element, offset);
|
||
};
|
||
|
||
var preprocess = function (selection) {
|
||
var start = selection.start().fold(Situ.before, beforeBr, Situ.after);
|
||
var finish = selection.finish().fold(Situ.before, beforeBr, Situ.after);
|
||
return SelectionRange.write(start, finish);
|
||
};
|
||
|
||
return {
|
||
preprocess: preprocess
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Fragment',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Element',
|
||
'global!document'
|
||
],
|
||
|
||
function (Arr, Element, document) {
|
||
var fromElements = function (elements, scope) {
|
||
var doc = scope || document;
|
||
var fragment = doc.createDocumentFragment();
|
||
Arr.each(elements, function (element) {
|
||
fragment.appendChild(element.dom());
|
||
});
|
||
return Element.fromDom(fragment);
|
||
};
|
||
|
||
return {
|
||
fromElements: fromElements
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.wwwc.WwwcModel',
|
||
|
||
[
|
||
'ephox.fussy.api.SelectionRange',
|
||
'ephox.fussy.wwwc.Directions',
|
||
'ephox.fussy.wwwc.DomRange',
|
||
'ephox.fussy.wwwc.Prefilter',
|
||
'ephox.perhaps.Option',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Fragment'
|
||
],
|
||
|
||
function (SelectionRange, Directions, DomRange, Prefilter, Option, Element, Fragment) {
|
||
var set = function (raw) {
|
||
return function (win, model) {
|
||
var selection = Prefilter.preprocess(raw);
|
||
var modifier = DomRange.build(win, selection);
|
||
if (model !== undefined && model !== null) {
|
||
model.removeAllRanges();
|
||
modifier(model);
|
||
}
|
||
};
|
||
};
|
||
|
||
var selectElementContents = function (element) {
|
||
return function (win, model) {
|
||
var rng = DomRange.create(win);
|
||
rng.selectNodeContents(element.dom());
|
||
model.removeAllRanges();
|
||
model.addRange(rng);
|
||
};
|
||
};
|
||
|
||
var normaliseRange = function (win, model) {
|
||
// In a multiple rangeset we take the first and the last item in the range, and create a new range model
|
||
var first = model.getRangeAt(0);
|
||
var last = model.getRangeAt(model.rangeCount - 1);
|
||
var range = win.document.createRange();
|
||
range.setStart(first.startContainer, first.startOffset);
|
||
range.setEnd(last.endContainer, last.endOffset);
|
||
return range;
|
||
};
|
||
|
||
var fromNative = function (model, range) {
|
||
var start = Element.fromDom(range.startContainer);
|
||
var finish = Element.fromDom(range.endContainer);
|
||
|
||
return Directions.read().isRtl(model) ?
|
||
Directions.read().flip(model, range) :
|
||
SelectionRange.read(start, range.startOffset, finish, range.endOffset);
|
||
};
|
||
|
||
var getNative = function (win, model) {
|
||
return model !== undefined && model !== null && model.rangeCount > 0 ? Option.from(normaliseRange(win, model)) : Option.none();
|
||
};
|
||
|
||
var get = function (win, model) {
|
||
var range = getNative(win, model);
|
||
return range.map(function (r) {
|
||
return fromNative(model, r);
|
||
});
|
||
};
|
||
|
||
var replace = function (elements) {
|
||
return function (win, model) {
|
||
var selection = getNative(win, model);
|
||
selection.each(function (range) {
|
||
doReplaceRange(win, range, elements);
|
||
});
|
||
};
|
||
};
|
||
|
||
var doReplaceRange = function (win, range, elements) {
|
||
// Note: this document fragment approach may not work on IE9.
|
||
var fragment = Fragment.fromElements(elements, win.document);
|
||
range.deleteContents();
|
||
range.insertNode(fragment.dom());
|
||
};
|
||
|
||
var replaceRange = function (raw, elements) {
|
||
return function (win, model) {
|
||
var selection = Prefilter.preprocess(raw);
|
||
// NOTE: This selection has to be LTR, or the range will collapse.
|
||
var range = DomRange.toNative(win, selection);
|
||
doReplaceRange(win, range, elements);
|
||
};
|
||
};
|
||
|
||
var deleteRange = function (s, so, e, eo) {
|
||
return function (win, model) {
|
||
var rng = DomRange.toExactNative(win, s, so, e, eo);
|
||
rng.deleteContents();
|
||
};
|
||
};
|
||
|
||
var cloneFragment = function (s, so, e, eo) {
|
||
return function (win, model) {
|
||
var rng = DomRange.toExactNative(win, s, so, e, eo);
|
||
var fragment = rng.cloneContents();
|
||
return Element.fromDom(fragment);
|
||
};
|
||
};
|
||
|
||
var rectangleAt = function (s, so, e, eo) {
|
||
return function (win, model) {
|
||
var rng = DomRange.toExactNative(win, s, so, e, eo);
|
||
var rects = rng.getClientRects();
|
||
// ASSUMPTION: The first rectangle is the start of the selection
|
||
var bounds = rects.length > 0 ? rects[0] : rng.getBoundingClientRect();
|
||
return bounds.width > 0 || bounds.height > 0 ? Option.some(bounds) : Option.none();
|
||
};
|
||
};
|
||
|
||
var clearSelection = function (win, model) {
|
||
win.getSelection().removeAllRanges();
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
set: set,
|
||
selectElementContents: selectElementContents,
|
||
replace: replace,
|
||
replaceRange: replaceRange,
|
||
deleteRange: deleteRange,
|
||
cloneFragment: cloneFragment,
|
||
rectangleAt: rectangleAt,
|
||
clearSelection: clearSelection
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.fussy.api.WindowSelection',
|
||
|
||
[
|
||
'ephox.fussy.api.SelectionRange',
|
||
'ephox.fussy.api.Situ',
|
||
'ephox.fussy.api.Supported',
|
||
'ephox.fussy.search.Within',
|
||
'ephox.fussy.wwwc.DomRange',
|
||
'ephox.fussy.wwwc.WwwcModel',
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (SelectionRange, Situ, Supported, Within, DomRange, WwwcModel, Compare, Element) {
|
||
var get = function (win) {
|
||
return Supported.run(win, WwwcModel.get);
|
||
};
|
||
|
||
var set = function (win, raw) {
|
||
Supported.run(win, WwwcModel.set(raw));
|
||
};
|
||
|
||
var setExact = function (win, s, so, e, eo) {
|
||
var raw = SelectionRange.write(
|
||
Situ.on(s, so),
|
||
Situ.on(e, eo)
|
||
);
|
||
set(win, raw);
|
||
};
|
||
|
||
var selectElementContents = function (win, element) {
|
||
Supported.run(win, WwwcModel.selectElementContents(element));
|
||
};
|
||
|
||
var replace = function (win, elements) {
|
||
Supported.run(win, WwwcModel.replace(elements));
|
||
};
|
||
|
||
var replaceRange = function (win, raw, elements) {
|
||
Supported.run(win, WwwcModel.replaceRange(raw, elements));
|
||
};
|
||
|
||
var deleteRange = function (win, s, so, e, eo) {
|
||
Supported.run(win, WwwcModel.deleteRange(s, so, e, eo));
|
||
};
|
||
|
||
var cloneFragment = function (win, s, so, e, eo) {
|
||
return Supported.run(win, WwwcModel.cloneFragment(s, so, e, eo));
|
||
};
|
||
|
||
var isCollapsed = function (s, so, e, eo) {
|
||
return Compare.eq(s, e) && so === eo;
|
||
};
|
||
|
||
var rectangleAt = function (win, s, so, e, eo) {
|
||
return Supported.run(win, WwwcModel.rectangleAt(s, so, e, eo));
|
||
};
|
||
|
||
var findWithin = function (win, raw, selector) {
|
||
// Note, we don't need the getSelection() model for this.
|
||
return Within.find(win, raw, selector);
|
||
};
|
||
|
||
var findWithinExact = function (win, s, so, e, eo, selector) {
|
||
var raw = SelectionRange.write(
|
||
Situ.on(s, so),
|
||
Situ.on(e, eo)
|
||
);
|
||
// Note, we don't need the getSelection() model for this.
|
||
return findWithin(win, raw, selector);
|
||
};
|
||
|
||
var deriveExact = function (win, raw) {
|
||
var rng = DomRange.forceRange(win, raw);
|
||
return SelectionRange.general(Element.fromDom(rng.startContainer), rng.startOffset, Element.fromDom(rng.endContainer), rng.endOffset);
|
||
};
|
||
|
||
var clearAll = function (win) {
|
||
Supported.run(win, WwwcModel.clearSelection);
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
set: set,
|
||
setExact: setExact,
|
||
selectElementContents: selectElementContents,
|
||
replace: replace,
|
||
replaceRange: replaceRange,
|
||
deleteRange: deleteRange,
|
||
isCollapsed: isCollapsed,
|
||
cloneFragment: cloneFragment,
|
||
rectangleAt: rectangleAt,
|
||
findWithin: findWithin,
|
||
findWithinExact: findWithinExact,
|
||
deriveExact: deriveExact,
|
||
clearAll: clearAll
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.api.HtmlPatterns',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
return {
|
||
validStyles: function () {
|
||
return /^(mso-.*|tab-stops|tab-interval|language|text-underline|text-effect|text-line-through|font-color|horiz-align|list-image-[0-9]+|separator-image|table-border-color-(dark|light)|vert-align|vnd\..*)$/;
|
||
},
|
||
specialInline: function () {
|
||
return /^(font|em|strong|samp|acronym|cite|code|dfn|kbd|tt|b|i|u|s|sub|sup|ins|del|var|span)$/;
|
||
}
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.violin.StringMatch',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
var starts = function (value) {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return s(value);
|
||
});
|
||
};
|
||
|
||
var pattern = function (regex) {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return p(regex);
|
||
});
|
||
};
|
||
|
||
var contains = function (value) {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return c(value);
|
||
});
|
||
};
|
||
|
||
var exact = function (value) {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return e(value);
|
||
});
|
||
};
|
||
|
||
var all = function () {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return a();
|
||
});
|
||
};
|
||
|
||
var not = function (sm) {
|
||
return folder(function (s, p, c, e, a, n) {
|
||
return n(sm);
|
||
});
|
||
};
|
||
|
||
var folder = function (fold) {
|
||
var matches = function (str) {
|
||
return fold(function (value) {
|
||
return str.toLowerCase().indexOf(value.toLowerCase()) === 0;
|
||
}, function (regex) {
|
||
return regex.test(str.toLowerCase());
|
||
}, function (value) {
|
||
return str.toLowerCase().indexOf(value.toLowerCase()) >= 0;
|
||
}, function (value) {
|
||
return str.toLowerCase() === value.toLowerCase();
|
||
}, function () {
|
||
return true;
|
||
}, function (other) {
|
||
return !other.matches(str);
|
||
});
|
||
};
|
||
|
||
return {
|
||
fold: fold,
|
||
matches: matches
|
||
};
|
||
};
|
||
|
||
var cata = function (subject, s, p, c, e, a, n) {
|
||
return subject.fold(s, p, c, e, a, n);
|
||
};
|
||
|
||
return {
|
||
starts: starts,
|
||
pattern: pattern,
|
||
contains: contains,
|
||
exact: exact,
|
||
all: all,
|
||
not: not,
|
||
cata: cata
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.api.RuleMatch',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.violin.StringMatch'
|
||
],
|
||
|
||
function (Fun, Node, StringMatch) {
|
||
var keyval = function (element, value, key, rule) {
|
||
var ruleName = rule.name;
|
||
var ruleCondition = rule.condition !== undefined ? rule.condition : Fun.constant(true);
|
||
var ruleValue = rule.value !== undefined ? rule.value : StringMatch.all();
|
||
return ruleName.matches(key) && ruleValue.matches(value) && ruleCondition(element);
|
||
};
|
||
|
||
var name = function (element, rule) {
|
||
var tag = Node.name(element);
|
||
var ruleName = rule.name;
|
||
var ruleCondition = rule.condition !== undefined ? rule.condition : Fun.constant(true);
|
||
return ruleName.matches(tag) && ruleCondition(element);
|
||
};
|
||
|
||
return {
|
||
keyval: keyval,
|
||
name: name
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.cleanup.AttributeAccess',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Attr'
|
||
],
|
||
|
||
function (Arr, Obj, Fun, Attr) {
|
||
var filter = function (element, predicate) {
|
||
var r = {};
|
||
Arr.each(element.dom().attributes, function (a) {
|
||
if (!predicate(a.value, a.name)) r[a.name] = a.value;
|
||
});
|
||
return r;
|
||
};
|
||
|
||
var update = function (element, names, keepers) {
|
||
Arr.each(names, function (name) {
|
||
Attr.remove(element, name);
|
||
});
|
||
|
||
Obj.each(keepers, function (v, k) {
|
||
Attr.set(element, k, v);
|
||
});
|
||
};
|
||
|
||
var clobber = function (element, supported, _unsupported) {
|
||
var names = Arr.map(element.dom().attributes, function (attribute) {
|
||
return attribute.name;
|
||
});
|
||
|
||
if (Obj.size(supported) !== names.length) update(element, names, supported);
|
||
};
|
||
|
||
return {
|
||
filter: filter,
|
||
clobber: clobber,
|
||
// There are no hidden attributes that I know about.
|
||
scan: Fun.constant({})
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.cleanup.StyleAccess',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Arr, Obj, Attr, Css, Strings) {
|
||
var separate = function (style) {
|
||
var css = {};
|
||
var bits = style !== undefined && style !== null ? style.split(';') : [];
|
||
Arr.each(bits, function (bit) {
|
||
var parts = bit.split(':');
|
||
if (parts.length === 2) {
|
||
css[Strings.trim(parts[0])] = Strings.trim(parts[1]);
|
||
}
|
||
});
|
||
return css;
|
||
};
|
||
|
||
var get = function (element, property) {
|
||
return element.dom().style.getPropertyValue(property);
|
||
};
|
||
|
||
var filter = function (element, predicate) {
|
||
var r = {};
|
||
Arr.each(element.dom().style, function (property) {
|
||
var value = get(element, property);
|
||
if (!predicate(value, property)) r[property] = value;
|
||
});
|
||
return r;
|
||
};
|
||
|
||
var set = function (element, property, value) {
|
||
Css.set(element, property, value);
|
||
};
|
||
|
||
// Find the style for any special styles.
|
||
var scan = function (element, special, predicate) {
|
||
var style = element.dom().getAttribute('style');
|
||
var css = separate(style);
|
||
|
||
var before = {};
|
||
Arr.each(special, function (property) {
|
||
var value = css[property];
|
||
if (value !== undefined && !predicate(value, property)) before[property] = value;
|
||
});
|
||
|
||
return before;
|
||
};
|
||
|
||
var serialise = function (unsupported) {
|
||
var preserved = Obj.keys(unsupported);
|
||
return Arr.map(preserved, function (pre) {
|
||
return pre + ': ' + unsupported[pre];
|
||
}).join('; ');
|
||
};
|
||
|
||
var clobber = function (element, supported, unsupported) {
|
||
Attr.set(element, 'style', '');
|
||
|
||
var numSupported = Obj.size(supported);
|
||
var numUnsupported = Obj.size(unsupported);
|
||
|
||
if (numSupported === 0 && numUnsupported === 0) Attr.remove(element, 'style');
|
||
else if (numSupported === 0) Attr.set(element, 'style', serialise(unsupported));
|
||
else {
|
||
Obj.each(supported, function (v, k) {
|
||
set(element, k, v);
|
||
});
|
||
|
||
var base = Attr.get(element, 'style');
|
||
var extra = numUnsupported > 0 ? serialise(unsupported) + '; ' : '';
|
||
Attr.set(element, 'style', extra + base);
|
||
}
|
||
};
|
||
|
||
return {
|
||
filter: filter,
|
||
clobber: clobber,
|
||
scan: scan
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.cleanup.Cleaners',
|
||
|
||
[
|
||
'ephox.pastiche.cleanup.AttributeAccess',
|
||
'ephox.pastiche.cleanup.StyleAccess',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (AttributeAccess, StyleAccess, Fun, Element) {
|
||
var special = [ 'mso-list' ];
|
||
|
||
var style = function (element, predicate) {
|
||
var unsupported = StyleAccess.scan(element, special, predicate);
|
||
var supported = StyleAccess.filter(element, predicate);
|
||
StyleAccess.clobber(element, supported, unsupported);
|
||
};
|
||
|
||
var attribute = function (element, predicate) {
|
||
var keepers = AttributeAccess.filter(element, predicate);
|
||
AttributeAccess.clobber(element, keepers, {});
|
||
};
|
||
|
||
var validateStyles = function (element) {
|
||
var supported = StyleAccess.filter(element, Fun.constant(false));
|
||
StyleAccess.clobber(element, supported, {});
|
||
};
|
||
|
||
var styleDom = function (dom, predicate) {
|
||
style(Element.fromDom(dom), predicate);
|
||
};
|
||
|
||
var attributeDom = function (dom, predicate) {
|
||
attribute(Element.fromDom(dom), predicate);
|
||
};
|
||
|
||
return {
|
||
style: style,
|
||
attribute: attribute,
|
||
styleDom: styleDom,
|
||
attributeDom: attributeDom,
|
||
validateStyles: validateStyles
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Classes',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Class',
|
||
'global!Array'
|
||
],
|
||
|
||
function (Arr, Class, Array) {
|
||
/*
|
||
* ClassList is IE10 minimum:
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/Element.classList
|
||
*/
|
||
|
||
var add = function (element, classes) {
|
||
Arr.each(classes, function (x) {
|
||
Class.add(element, x);
|
||
});
|
||
};
|
||
|
||
var remove = function (element, classes) {
|
||
Arr.each(classes, function (x) {
|
||
Class.remove(element, x);
|
||
});
|
||
};
|
||
|
||
var toggle = function (element, classes) {
|
||
Arr.each(classes, function (x) {
|
||
Class.toggle(element, x);
|
||
});
|
||
};
|
||
|
||
var hasAll = function (element, classes) {
|
||
return Arr.forall(classes, function (clazz) {
|
||
return Class.has(element, clazz);
|
||
});
|
||
};
|
||
|
||
var hasAny = function (element, classes) {
|
||
return Arr.exists(classes, function (clazz) {
|
||
return Class.has(element, clazz);
|
||
});
|
||
};
|
||
|
||
var get = function (element) {
|
||
var classList = element.dom().classList;
|
||
var r = new Array(classList.length);
|
||
for (var i = 0; i < classList.length; i++) {
|
||
r[i] = classList.item(i);
|
||
}
|
||
return r;
|
||
};
|
||
|
||
// set deleted, risks bad performance. Be deterministic.
|
||
|
||
return {
|
||
add: add,
|
||
remove: remove,
|
||
toggle: toggle,
|
||
hasAll: hasAll,
|
||
hasAny: hasAny,
|
||
get: get
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Pipeless',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.highway.Merger',
|
||
'ephox.pastiche.api.RuleMatch',
|
||
'ephox.pastiche.cleanup.Cleaners',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Classes',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFilter'
|
||
],
|
||
|
||
function (Arr, Merger, RuleMatch, Cleaners, Fun, Attr, Class, Classes, Remove, SelectorFilter) {
|
||
var cleaner = function (type, rules, element) {
|
||
// Use Cleaners.style or Cleaners.attribute as "type".
|
||
type(element, function (value, key) {
|
||
return Arr.exists(rules, function (rule) {
|
||
return RuleMatch.keyval(element, value, key, rule);
|
||
});
|
||
});
|
||
};
|
||
|
||
var remover = function (container, strat) {
|
||
var strategy = Merger.merge({ styles: [], attributes: [], classes: [], tags: [] }, strat);
|
||
|
||
var elements = SelectorFilter.descendants(container, '*');
|
||
Arr.each(elements, function (element) {
|
||
cleaner(Cleaners.style, strategy.styles, element);
|
||
cleaner(Cleaners.attribute, strategy.attributes, element);
|
||
|
||
Arr.each(strategy.classes, function (rule) {
|
||
var actual = Attr.has(element, 'class') ? Classes.get(element) : [];
|
||
Arr.each(actual, function (act) {
|
||
if (rule.name.matches(act)) Class.remove(element, act);
|
||
});
|
||
});
|
||
});
|
||
|
||
// Now, remove the tags.
|
||
var postElements = SelectorFilter.descendants(container, '*');
|
||
Arr.each(postElements, function (element) {
|
||
var matching = Arr.exists(strategy.tags, Fun.curry(RuleMatch.name, element));
|
||
if (matching) Remove.remove(element);
|
||
});
|
||
};
|
||
|
||
var unwrapper = function (container, strat) {
|
||
var strategy = Merger.merge({ tags: [] }, strat);
|
||
|
||
var elements = SelectorFilter.descendants(container, '*');
|
||
Arr.each(elements, function (element) {
|
||
var matching = Arr.exists(strategy.tags, Fun.curry(RuleMatch.name, element));
|
||
if (matching) Remove.unwrap(element);
|
||
});
|
||
};
|
||
|
||
var transformer = function (container, strat) {
|
||
var strategy = Merger.merge({ tags: [] }, strat);
|
||
|
||
var elements = SelectorFilter.descendants(container, '*');
|
||
Arr.each(elements, function (element) {
|
||
var rule = Arr.find(strategy.tags, Fun.curry(RuleMatch.name, element));
|
||
if (rule !== undefined && rule !== null) rule.mutate(element);
|
||
});
|
||
};
|
||
|
||
var validator = function (container) {
|
||
var elements = SelectorFilter.descendants(container, '*');
|
||
Arr.each(elements, function (element) {
|
||
Cleaners.validateStyles(element);
|
||
});
|
||
};
|
||
|
||
return {
|
||
remover: remover,
|
||
unwrapper: unwrapper,
|
||
transformer: transformer,
|
||
validator: validator
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Token',
|
||
|
||
[
|
||
'ephox.compass.Obj',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Obj, Css, Element) {
|
||
var START_ELEMENT_TYPE = 'startElement';
|
||
var END_ELEMENT_TYPE = 'endElement';
|
||
var TEXT_TYPE = 'text';
|
||
var COMMENT_TYPE = 'comment';
|
||
|
||
var token = function(node, endNode, syntheticStyles) {
|
||
var tokenType;
|
||
var tagName;
|
||
var tokenText;
|
||
|
||
var element = Element.fromDom(node);
|
||
|
||
switch (node.nodeType) {
|
||
case 1:
|
||
if (endNode) {
|
||
tokenType = END_ELEMENT_TYPE;
|
||
} else {
|
||
tokenType = START_ELEMENT_TYPE;
|
||
|
||
Css.setAll(element, syntheticStyles || {});
|
||
}
|
||
if (node.scopeName !== "HTML" && node.scopeName && node.tagName && node.tagName.indexOf(':') <= 0) {
|
||
tagName = (node.scopeName + ":" + node.tagName).toUpperCase();
|
||
} else {
|
||
tagName = node.tagName;
|
||
}
|
||
|
||
break;
|
||
case 3:
|
||
tokenType = TEXT_TYPE;
|
||
tokenText = node.nodeValue;
|
||
break;
|
||
case 8:
|
||
tokenType = COMMENT_TYPE;
|
||
tokenText = node.nodeValue;
|
||
break;
|
||
default:
|
||
console.log("WARNING: Unsupported node type encountered: " + node.nodeType);
|
||
break;
|
||
}
|
||
|
||
var getNode = function() {
|
||
return node;
|
||
};
|
||
|
||
var tag = function() {
|
||
return tagName;
|
||
};
|
||
|
||
var type = function() {
|
||
return tokenType;
|
||
};
|
||
|
||
var text = function() {
|
||
return tokenText;
|
||
};
|
||
|
||
return {
|
||
getNode: getNode,
|
||
tag: tag,
|
||
type: type,
|
||
text: text
|
||
};
|
||
};
|
||
|
||
var createStartElement = function(tag, attributes, styles, document) {
|
||
var node = document.createElement(tag), css = "";
|
||
|
||
Obj.each(attributes, function(value, name) {
|
||
node.setAttribute(name, value);
|
||
});
|
||
|
||
return token(node, false, styles);
|
||
};
|
||
|
||
var createEndElement = function(tag, document) {
|
||
return token(document.createElement(tag), true);
|
||
};
|
||
|
||
var createComment = function(text, document) {
|
||
return token(document.createComment(text), false);
|
||
};
|
||
|
||
var createText = function(text, document) {
|
||
return token(document.createTextNode(text));
|
||
};
|
||
|
||
var FINISHED = createEndElement('HTML', window.document);
|
||
|
||
return {
|
||
START_ELEMENT_TYPE: START_ELEMENT_TYPE,
|
||
END_ELEMENT_TYPE: END_ELEMENT_TYPE,
|
||
TEXT_TYPE: TEXT_TYPE,
|
||
COMMENT_TYPE: COMMENT_TYPE,
|
||
FINISHED: FINISHED,
|
||
token: token,
|
||
createStartElement: createStartElement,
|
||
createEndElement: createEndElement,
|
||
createComment: createComment,
|
||
createText: createText
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Serialiser',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
var create = function (doc) {
|
||
var currentNode = doc.createDocumentFragment();
|
||
var initialNode = currentNode;
|
||
|
||
var push = function(node) {
|
||
append(node);
|
||
currentNode = node;
|
||
};
|
||
|
||
var pop = function() {
|
||
currentNode = currentNode.parentNode;
|
||
};
|
||
|
||
var append = function(node) {
|
||
currentNode.appendChild(node);
|
||
};
|
||
|
||
var receive = function(token) {
|
||
var startElement = function(token) {
|
||
var node = token.getNode().cloneNode(false);
|
||
push(node);
|
||
};
|
||
|
||
var text = function(token, serializer) {
|
||
// IE7 will crash if you clone a text node that's a URL.
|
||
// IE8 throws an invalid argument error.
|
||
// So while cloning may be faster, we have to create a new node here.
|
||
var node = doc.createTextNode(token.text());
|
||
append(node);
|
||
};
|
||
|
||
switch (token.type()) {
|
||
case Token.START_ELEMENT_TYPE:
|
||
startElement(token);
|
||
break;
|
||
case Token.TEXT_TYPE:
|
||
text(token);
|
||
break;
|
||
case Token.END_ELEMENT_TYPE:
|
||
pop();
|
||
break;
|
||
case Token.COMMENT_TYPE:
|
||
// Ignore.
|
||
break;
|
||
default:
|
||
throw { message: 'Unsupported token type: ' + token.type() };
|
||
}
|
||
};
|
||
|
||
return {
|
||
dom: initialNode,
|
||
receive: receive,
|
||
label: 'SERIALISER'
|
||
};
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Tokeniser',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
var tokenise = function(html, document) {
|
||
var container;
|
||
document = document || window.document;
|
||
container = document.createElement('div');
|
||
document.body.appendChild(container);
|
||
container.style.position = 'absolute';
|
||
container.style.left = '-10000px';
|
||
container.innerHTML = html;
|
||
|
||
nextNode = container.firstChild || Token.FINISHED;
|
||
|
||
var nodeStack = [];
|
||
endNode = false;
|
||
|
||
var getTokenForNode = function(node, endTag) {
|
||
if (node === Token.FINISHED) {
|
||
return node;
|
||
} else if (node) {
|
||
return Token.token(node, endTag);
|
||
} else {
|
||
return undefined;
|
||
}
|
||
};
|
||
|
||
var next = function() {
|
||
var currentNode = nextNode;
|
||
var currentEndNode = endNode;
|
||
if (!endNode && nextNode.firstChild) {
|
||
nodeStack.push(nextNode);
|
||
nextNode = nextNode.firstChild;
|
||
} else if (!endNode && nextNode.nodeType === 1) {
|
||
// Empty element.
|
||
endNode = true;
|
||
} else if (nextNode.nextSibling) {
|
||
nextNode = nextNode.nextSibling;
|
||
endNode = false;
|
||
} else {
|
||
nextNode = nodeStack.pop();
|
||
endNode = true;
|
||
}
|
||
|
||
if (currentNode !== Token.FINISHED && !nextNode) {
|
||
document.body.removeChild(container);
|
||
nextNode = Token.FINISHED;
|
||
}
|
||
|
||
return getTokenForNode(currentNode, currentEndNode);
|
||
};
|
||
|
||
var hasNext = function() {
|
||
return nextNode !== undefined;
|
||
};
|
||
|
||
return {
|
||
hasNext: hasNext,
|
||
next: next
|
||
};
|
||
};
|
||
|
||
return {
|
||
tokenise: tokenise
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Pipeline',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Serialiser',
|
||
'ephox.pastiche.engine.Tokeniser'
|
||
],
|
||
|
||
function (Serialiser, Tokeniser) {
|
||
var build = function(doc, filters, sink) {
|
||
var i, filter = sink;
|
||
for (i = filters.length - 1; i >= 0; i--) {
|
||
//This is calling the function defined by Filter.createFilter().
|
||
//The best description I can come up with is "function composition using CPS".
|
||
//Filters are run in reverse order to the loop, which is reversed so the arrays below define the order.
|
||
//And then the sink comes last (which means it's injected on the first pass of the loop).
|
||
// filter = filters[i](doc, filter);
|
||
|
||
// TEMPORARY:
|
||
filter = filters[i](filter, {}, doc);
|
||
}
|
||
return filter;
|
||
};
|
||
|
||
var run = function(doc, content, filters) {
|
||
var sink = Serialiser.create(doc);
|
||
var tokeniser = Tokeniser.tokenise(content, doc);
|
||
var pipeline = build(doc, filters, sink);
|
||
while (tokeniser.hasNext()) {
|
||
var token = tokeniser.next();
|
||
pipeline.receive(token);
|
||
}
|
||
return sink.dom;
|
||
};
|
||
|
||
return {
|
||
build: build,
|
||
run: run
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.api.HybridAction',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.engine.Pipeless',
|
||
'ephox.pastiche.engine.Pipeline',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Arr, Pipeless, Pipeline, Element, Html, Remove, Traverse) {
|
||
var removal = function (spec) {
|
||
return function (container) {
|
||
Pipeless.remover(container, spec);
|
||
};
|
||
};
|
||
|
||
var unwrapper = function (spec) {
|
||
return function (container) {
|
||
Pipeless.unwrapper(container, spec);
|
||
};
|
||
};
|
||
|
||
var transformer = function (spec) {
|
||
return function (container) {
|
||
Pipeless.transformer(container, spec);
|
||
};
|
||
};
|
||
|
||
var validate = function () {
|
||
return function (container) {
|
||
Pipeless.validator(container);
|
||
};
|
||
};
|
||
|
||
var pipeline = function (pipes) {
|
||
return function (container) {
|
||
var html = Html.get(container);
|
||
var doc = Traverse.owner(container);
|
||
var sink = Pipeline.run(doc.dom(), html, pipes);
|
||
Remove.empty(container);
|
||
container.dom().appendChild(sink);
|
||
};
|
||
};
|
||
|
||
var go = function (doc, input, actions) {
|
||
var container = Element.fromTag('div', doc.dom());
|
||
container.dom().innerHTML = input;
|
||
Arr.each(actions, function (action) {
|
||
action(container);
|
||
});
|
||
return Html.get(container);
|
||
};
|
||
|
||
var isWordContent = function (content) {
|
||
return content.indexOf('<o:p>') >= 0 || // IE, Safari, Opera
|
||
content.indexOf('p.MsoNormal, li.MsoNormal, div.MsoNormal') >= 0 || // Firefox Mac
|
||
content.indexOf('MsoListParagraphCxSpFirst') >= 0 || // Windows list only selection
|
||
content.indexOf('<w:WordDocument>') >= 0; // Firefox Windows
|
||
};
|
||
|
||
return {
|
||
removal: removal,
|
||
unwrapper: unwrapper,
|
||
transformer: transformer,
|
||
validate: validate,
|
||
pipeline: pipeline,
|
||
isWordContent: isWordContent,
|
||
go: go
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.api.RuleConditions',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.PredicateExists'
|
||
],
|
||
|
||
function (Arr, Attr, Html, Node, PredicateExists) {
|
||
var isNotImage = function (elem) {
|
||
return Node.name(elem) !== 'img';
|
||
};
|
||
|
||
var isImportantSpan = function (elem) {
|
||
var attrs = elem.dom().attributes;
|
||
var hasAttrs = attrs !== undefined && attrs !== null && attrs.length > 0;
|
||
return Node.name(elem) === 'span' ? hasAttrs : true;
|
||
};
|
||
|
||
var hasContent = function (elem) {
|
||
if (!hasNoAttributes(elem)) return true;
|
||
else {
|
||
return isImportantSpan(elem) && PredicateExists.descendant(elem, function (e) {
|
||
var hasAttributes = !hasNoAttributes(e);
|
||
var isContentTag = !Arr.contains([
|
||
'font', 'em', 'strong', 'samp', 'acronym', 'cite', 'code', 'dfn', 'kbd', 'tt', 'b', 'i',
|
||
'u', 's', 'sub', 'sup', 'ins', 'del', 'var', 'span'
|
||
], Node.name(e));
|
||
|
||
return Node.isText(e) || hasAttributes || isContentTag;
|
||
});
|
||
}
|
||
};
|
||
|
||
var isList = function (elem) {
|
||
return Node.name(elem) === 'ol' || Node.name(elem) === 'ul';
|
||
};
|
||
|
||
var isLocal = function (element) {
|
||
var src = Attr.get(element, 'src');
|
||
return (/^file:/).test(src);
|
||
};
|
||
|
||
var hasNoAttributes = function (elem) {
|
||
if (elem.dom().attributes === undefined || elem.dom().attributes === null) return true;
|
||
return elem.dom().attributes.length === 0 || (elem.dom().attributes.length === 1 && elem.dom().attributes[0].name === 'style');
|
||
};
|
||
|
||
var isEmpty = function (elem) {
|
||
// Note, this means that things with zero width cursors are NOT considered empty
|
||
return Html.get(elem).length === 0;
|
||
};
|
||
|
||
return {
|
||
isNotImage: isNotImage,
|
||
hasContent: hasContent,
|
||
isList: isList,
|
||
isLocal: isLocal,
|
||
hasNoAttributes: hasNoAttributes,
|
||
isEmpty: isEmpty
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.api.RuleMutations',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.compass.Obj',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Arr, Obj, Attr, Css, Element, Html, Insert, InsertAll, Node, Remove, Traverse) {
|
||
var changeTag = function (tag, element) {
|
||
// We cannot use replication because it uses innerHTML rather than setting the children.
|
||
// Which means that any further transformations done to the children are not represented
|
||
// in the final output.
|
||
var replica = Element.fromTag(tag);
|
||
Insert.before(element, replica);
|
||
|
||
var attributes = element.dom().attributes;
|
||
Arr.each(attributes, function (attr) {
|
||
replica.dom().setAttribute(attr.name, attr.value);
|
||
});
|
||
|
||
var children = Traverse.children(element);
|
||
InsertAll.append(replica, children);
|
||
Remove.remove(element);
|
||
return replica;
|
||
};
|
||
|
||
// Adds a <br> tag to any <p> tags that are empty
|
||
var addBrTag = function (element) {
|
||
if (Html.get(element).length === 0) {
|
||
Insert.append(element, Element.fromTag('br'));
|
||
}
|
||
};
|
||
|
||
var properlyNest = function (element) {
|
||
Traverse.parent(element).each(function (parent) {
|
||
var tag = Node.name(parent);
|
||
if (Arr.contains([ 'ol', 'ul' ], tag)) {
|
||
var li = Element.fromTag('li');
|
||
Css.set(li, 'list-style-type', 'none');
|
||
Insert.wrap(element, li);
|
||
}
|
||
});
|
||
};
|
||
|
||
var fontToSpan = function (element) {
|
||
var span = changeTag('span', element);
|
||
var conversions = {
|
||
face: 'font-family',
|
||
size: 'font-size',
|
||
color: 'font-color'
|
||
};
|
||
|
||
var values = {
|
||
'font-size': {
|
||
'1': '8pt',
|
||
'2': '10pt',
|
||
'3': '12pt',
|
||
'4': '14pt',
|
||
'5': '18pt',
|
||
'6': '24pt',
|
||
'7': '36pt'
|
||
}
|
||
};
|
||
|
||
Obj.each(conversions, function (style, attribute) {
|
||
if (Attr.has(span, attribute)) {
|
||
var value = Attr.get(span, attribute);
|
||
var cssValue = values[style] !== undefined && values[style][value] !== undefined ? values[style][value] : value;
|
||
Css.set(span, style, cssValue);
|
||
Attr.remove(span, attribute);
|
||
}
|
||
});
|
||
};
|
||
|
||
return {
|
||
changeTag: changeTag,
|
||
addBrTag: addBrTag,
|
||
properlyNest: properlyNest,
|
||
fontToSpan: fontToSpan
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.Filter',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Arr, Token) {
|
||
var createFilter = function(actualReceiver, clientReset, label) {
|
||
var filter = function(nextFilter, settings, document, _logger) {
|
||
var logger = _logger !== undefined ? _logger : [];
|
||
|
||
var deferred;
|
||
var receivedTokens, emittedTokens, inTransaction = false;
|
||
|
||
var resetState = function() {
|
||
if (clientReset) clientReset(api);
|
||
inTransaction = false;
|
||
receivedTokens = [];
|
||
emittedTokens = [];
|
||
};
|
||
|
||
var emitTokens = function(tokens) {
|
||
Arr.each(tokens, function(tok) {
|
||
nextFilter.receive(tok);
|
||
});
|
||
};
|
||
|
||
var emit = function(token) {
|
||
if (inTransaction) {
|
||
emittedTokens.push(token);
|
||
} else {
|
||
nextFilter.receive(token);
|
||
}
|
||
};
|
||
|
||
var receive = function(token) {
|
||
if (clientReset) receivedTokens.push(token);
|
||
actualReceiver(api, token);
|
||
if (token === Token.FINISHED) {
|
||
commit();
|
||
}
|
||
};
|
||
|
||
var startTransaction = function() {
|
||
inTransaction = true;
|
||
};
|
||
|
||
var rollback = function() {
|
||
emitTokens(receivedTokens);
|
||
resetState();
|
||
};
|
||
|
||
var commit = function() {
|
||
emitDeferred();
|
||
emitTokens(emittedTokens);
|
||
resetState();
|
||
};
|
||
|
||
var defer = function(token) {
|
||
deferred = deferred || [];
|
||
deferred.push(token);
|
||
};
|
||
|
||
var hasDeferred = function() {
|
||
return deferred && deferred.length > 0;
|
||
};
|
||
|
||
var emitDeferred = function() {
|
||
Arr.each(deferred || [], function(token) {
|
||
emit(token);
|
||
});
|
||
dropDeferred();
|
||
};
|
||
|
||
var dropDeferred = function() {
|
||
deferred = [];
|
||
};
|
||
|
||
var api = {
|
||
document: document || window.document,
|
||
settings: settings || {},
|
||
emit: emit,
|
||
receive: receive,
|
||
startTransaction: startTransaction,
|
||
rollback: rollback,
|
||
commit: commit,
|
||
defer: defer,
|
||
hasDeferred: hasDeferred,
|
||
emitDeferred: emitDeferred,
|
||
dropDeferred: dropDeferred,
|
||
label: label
|
||
};
|
||
|
||
resetState();
|
||
return api;
|
||
};
|
||
return filter;
|
||
};
|
||
|
||
return {
|
||
createFilter: createFilter
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
|
||
[
|
||
'ephox.pastiche.cleanup.StyleAccess',
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (StyleAccess, Token, Fun, Attr, Css, Element) {
|
||
var getAttribute = function (token, property) {
|
||
var element = Element.fromDom(token.getNode());
|
||
return Attr.get(element, property);
|
||
};
|
||
|
||
var getStyle = function (token, property) {
|
||
var element = Element.fromDom(token.getNode());
|
||
return Css.get(element, property);
|
||
};
|
||
|
||
var isWhitespace = function (token) {
|
||
return token.type() === Token.TEXT_TYPE && /^[\s\u00A0]*$/.test(token.text());
|
||
};
|
||
|
||
var getMsoList = function (token) {
|
||
var element = Element.fromDom(token.getNode());
|
||
var styles = StyleAccess.scan(element, [ 'mso-list' ], Fun.constant(false));
|
||
return styles['mso-list'];
|
||
};
|
||
|
||
return {
|
||
getAttribute: getAttribute,
|
||
getStyle: getStyle,
|
||
isWhitespace: isWhitespace,
|
||
getMsoList: getMsoList
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.ListSymbols',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.highway.Merger'
|
||
],
|
||
|
||
function (Arr, Merger) {
|
||
|
||
var orderedListTypes = [
|
||
{ regex: /^\(?[dc][\.\)]$/, type: { tag: 'OL', type: 'lower-alpha' } },
|
||
{ regex: /^\(?[DC][\.\)]$/, type: { tag: 'OL', type: 'upper-alpha' } },
|
||
{ regex: /^\(?M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})[\.\)]$/, type: { tag: 'OL', type: 'upper-roman' } },
|
||
{ regex: /^\(?m*(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})[\.\)]$/, type: { tag: 'OL', type: 'lower-roman' } },
|
||
{ regex: /^\(?[0-9]+[\.\)]$/, type: { tag: 'OL' } },
|
||
{ regex: /^([0-9]+\.)*[0-9]+\.?$/, type: { tag: 'OL', variant: 'outline' } },
|
||
{ regex: /^\(?[a-z]+[\.\)]$/, type: { tag: 'OL', type: 'lower-alpha' } },
|
||
{ regex: /^\(?[A-Z]+[\.\)]$/, type: { tag: 'OL', type: 'upper-alpha' } }
|
||
];
|
||
|
||
var ulChars = {
|
||
'\u2022': { tag: 'UL', type: 'disc' },
|
||
'\u00B7': { tag: 'UL', type: 'disc' },
|
||
'\u00A7': { tag: 'UL', type: 'square' }
|
||
};
|
||
|
||
var ulNonSymbolChars = {
|
||
'o': { tag: 'UL', type: 'circle' },
|
||
'-': { tag: 'UL', type: 'disc' },
|
||
'\u25CF': { tag: 'UL', type: 'disc' },
|
||
'<27>': { tag: 'UL', type: 'circle' }
|
||
};
|
||
|
||
var getVariant = function (type, text) {
|
||
if (type.variant !== undefined) return type.variant;
|
||
else if (text.charAt(0) === '(') return '()';
|
||
else if (text.charAt(text.length - 1) === ')') return ')';
|
||
else return '.';
|
||
};
|
||
|
||
var getStart = function (text) {
|
||
var number = parseInt(text, 10);
|
||
return isNaN(number) ? { } : { start: number };
|
||
};
|
||
|
||
var match = function (text, inSymbol) {
|
||
var nonSymbols = ulNonSymbolChars[text] ? [ ulNonSymbolChars[text] ] : [];
|
||
var symbols = inSymbol && ulChars[text] ? [ ulChars[text] ] : inSymbol ? [{ tag: 'UL', variant: text }] : [];
|
||
var ordered = Arr.bind(orderedListTypes, function (o) {
|
||
return o.regex.test(text) ? [ Merger.merge(o.type, getStart(text), {
|
||
variant: getVariant(o.type, text)
|
||
})] : [];
|
||
});
|
||
|
||
var result = nonSymbols.concat(symbols).concat(ordered);
|
||
return Arr.map(result, function (x) {
|
||
return x.variant !== undefined ? x : Merger.merge(x, {
|
||
variant: text
|
||
});
|
||
});
|
||
};
|
||
|
||
return {
|
||
match: match
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.ListGuess',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.list.detect.ListSymbols',
|
||
'ephox.perhaps.Option',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Arr, ListSymbols, Option, Strings) {
|
||
|
||
var guess = function(bulletInfo, preferredType) {
|
||
var text = bulletInfo ? Strings.trim(bulletInfo.text) : '';
|
||
var symbolFont = bulletInfo ? !!bulletInfo.symbolFont : false;
|
||
var candidates = ListSymbols.match(text, symbolFont);
|
||
var preferred = Arr.find(candidates, function (c) {
|
||
// The original code only ran preferred types for ordered lists. I have
|
||
// no idea whether this is a condition that we want preserved, but one
|
||
// of the QUnit tests implicitly stated it is.
|
||
return c.tag === 'UL' || (preferredType && eqListType(c, preferredType, true));
|
||
});
|
||
return preferred !== undefined ? Option.some(preferred) :
|
||
candidates.length > 0 ? Option.some(candidates[0]) : Option.none();
|
||
};
|
||
|
||
var eqListType = function(t1, t2, ignoreVariant) {
|
||
return t1 === t2 ||
|
||
(t1 && t2 && t1.tag === t2.tag && t1.type === t2.type &&
|
||
(ignoreVariant || t1.variant === t2.variant));
|
||
};
|
||
|
||
return {
|
||
guess: guess,
|
||
eqListType: eqListType
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.ListTypes',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
'ephox.pastiche.list.detect.ListGuess'
|
||
],
|
||
|
||
function (Token, TokenUtil, ListGuess) {
|
||
|
||
var guess = function(bulletInfo, preferredType, originalToken) {
|
||
var candidate = ListGuess.guess(bulletInfo, preferredType);
|
||
return candidate.fold(function () {
|
||
return null;
|
||
}, function (c) {
|
||
if (c.tag === 'OL' && originalToken && (originalToken.tag() !== 'P' || /^MsoHeading/.test(TokenUtil.getAttribute(originalToken, 'class')))) {
|
||
// Don't convert numbered headings but do convert bulleted headings.
|
||
listType = null;
|
||
} else {
|
||
return c;
|
||
}
|
||
});
|
||
};
|
||
|
||
var eqListType = ListGuess.eqListType;
|
||
|
||
var checkFont = function(token, symbolFont) {
|
||
if (token.type() == Token.START_ELEMENT_TYPE) {
|
||
font = TokenUtil.getStyle(token, 'font-family');
|
||
if (font) {
|
||
symbolFont = (font === 'Wingdings' || font === 'Symbol');
|
||
} else if (/^(P|H[1-6]|DIV)$/.test(token.tag())) {
|
||
symbolFont = false;
|
||
}
|
||
}
|
||
return symbolFont;
|
||
};
|
||
|
||
return {
|
||
guess: guess,
|
||
eqListType: eqListType,
|
||
checkFont: checkFont
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.engine.TokenUtil'
|
||
],
|
||
|
||
function (Token, TokenUtil) {
|
||
var isList = function (state, token) {
|
||
var style = TokenUtil.getMsoList(token);
|
||
return style && style !== 'skip';
|
||
};
|
||
|
||
var isIgnore = function (state, token) {
|
||
return token.type() == Token.START_ELEMENT_TYPE && TokenUtil.getMsoList(token) === 'Ignore';
|
||
};
|
||
|
||
return {
|
||
isList: isList,
|
||
isIgnore: isIgnore
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Tags',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Arr, Token, Strings) {
|
||
var isStart = function (state, token) {
|
||
return token.type() === Token.START_ELEMENT_TYPE;
|
||
};
|
||
|
||
var isEnd = function (state, token) {
|
||
return token.type() === Token.END_ELEMENT_TYPE;
|
||
};
|
||
|
||
var isTag = function (tag) {
|
||
return function (state, token) {
|
||
return token.tag() === tag;
|
||
};
|
||
};
|
||
|
||
var isWhitespace = function (tag) {
|
||
return function (state, token) {
|
||
return isTag(tag)(state, token) && Strings.trim(token.getNode().textContent) === '';
|
||
};
|
||
};
|
||
|
||
var isStartOf = function (tag) {
|
||
return function (state, token) {
|
||
return isStart(state, token) && token.tag() === tag;
|
||
};
|
||
};
|
||
|
||
var isEndOf = function (tag) {
|
||
return function (state, token) {
|
||
return isEnd(state, token) && token.tag() === tag;
|
||
};
|
||
};
|
||
|
||
var isStartAny = function (tags) {
|
||
return function (state, token) {
|
||
return isStart(state, token) && Arr.contains(tags, token.tag());
|
||
};
|
||
};
|
||
|
||
var isEndAny = function (tags) {
|
||
return function (state, token, tags) {
|
||
return isEnd(state, token) && Arr.contains(tags, token.tag());
|
||
};
|
||
};
|
||
|
||
return {
|
||
isStart: isStart,
|
||
isEnd: isEnd,
|
||
isTag: isTag,
|
||
isStartOf: isStartOf,
|
||
isEndOf: isEndOf,
|
||
isStartAny: isStartAny,
|
||
isEndAny: isEndAny,
|
||
isWhitespace: isWhitespace
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Paragraphs',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
'ephox.pastiche.inspect.Tags'
|
||
],
|
||
|
||
function (Microsoft, Tags) {
|
||
// MOVE ME.
|
||
var isNormal = function (state, token) {
|
||
return !state.skippedPara.get() && Tags.isStart(state, token, 'P') && !Microsoft.isList(state, token);
|
||
};
|
||
|
||
return {
|
||
isNormal: isNormal
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Texts',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (Token, TokenUtil, Strings) {
|
||
var isWhitespace = function (state, token) {
|
||
return is(state, token) && TokenUtil.isWhitespace(token);
|
||
};
|
||
|
||
var is = function (state, token) {
|
||
return token.type() === Token.TEXT_TYPE;
|
||
};
|
||
|
||
var eq = function (value) {
|
||
return function (state, token) {
|
||
return is(state, token) && token.text() === value;
|
||
};
|
||
};
|
||
|
||
var matches = function (value) {
|
||
return function (state, token) {
|
||
return is(state, token) && value.test(token.text());
|
||
};
|
||
};
|
||
|
||
// CHECK: Is this equivalent to isWhitespace?
|
||
var isBlank = function (state, token) {
|
||
return is(state, token) && Strings.trim(token.text()) === '';
|
||
};
|
||
|
||
return {
|
||
isWhitespace: isWhitespace,
|
||
is: is,
|
||
isBlank: isBlank,
|
||
eq: eq,
|
||
matches: matches
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.Handler',
|
||
|
||
[
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Fun) {
|
||
return function (pred, action, label) {
|
||
return {
|
||
pred: pred,
|
||
action: action,
|
||
label: Fun.constant(label)
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Arr, Fun, Option) {
|
||
var logger = function (label, action) {
|
||
return function (api, state, token) {
|
||
// console.log('LOGGER: ', label, token.getNode().cloneNode(false));
|
||
return action(api, state, token);
|
||
};
|
||
};
|
||
|
||
return function (name, handlers, fallback) {
|
||
var logFallback = logger(name + ' :: FALLBACK --- ', fallback);
|
||
|
||
var r = function (api, state, token) {
|
||
// console.log('token: ', token.getNode().cloneNode(true));
|
||
var match = Option.from(Arr.find(handlers, function (x) {
|
||
return x.pred(state, token);
|
||
}));
|
||
|
||
var action = match.fold(Fun.constant(logFallback), function (m) {
|
||
var label = m.label();
|
||
return label === undefined ? m.action : logger(name + ' :: ' + label, m.action);
|
||
});
|
||
action(api, state, token);
|
||
};
|
||
|
||
r.toString = function () { return 'Handlers for ' + name; };
|
||
return r;
|
||
};
|
||
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.Transitions',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var next = function (state, listState) {
|
||
if (state === undefined || listState === undefined) {
|
||
console.trace();
|
||
throw 'brick';
|
||
}
|
||
state.nextFilter.set(listState);
|
||
};
|
||
|
||
var setNext = function (listState) {
|
||
return function (api, state, token) {
|
||
next(state, listState);
|
||
};
|
||
};
|
||
|
||
var go = function (api, state, token) {
|
||
var nextFilter = state.nextFilter.get();
|
||
nextFilter(api, state, token);
|
||
};
|
||
|
||
var jump = function (listState) {
|
||
return function (api, state, token) {
|
||
next(state, listState);
|
||
go(api, state, token);
|
||
};
|
||
};
|
||
|
||
var isNext = function (state, listState) {
|
||
return state.nextFilter.get() === listState;
|
||
};
|
||
|
||
return {
|
||
next: next,
|
||
go: go,
|
||
jump: jump,
|
||
isNext: isNext,
|
||
setNext: setNext
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.AfterListState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Paragraphs',
|
||
'ephox.pastiche.inspect.Texts',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (Paragraphs, Texts, Handler, Handlers, Transitions) {
|
||
var run = function (skipEmptyParaState, noListState) {
|
||
|
||
var blankAction = function (api, state, token) {
|
||
api.defer(token);
|
||
};
|
||
|
||
var normalParaAction = function (api, state, token) {
|
||
state.openedTag.set(token);
|
||
api.defer(token);
|
||
Transitions.next(state, skipEmptyParaState);
|
||
};
|
||
|
||
var fallback = function (api, state, token) {
|
||
noListState(api, state, token);
|
||
};
|
||
|
||
return Handlers('AfterListState', [
|
||
Handler(Texts.isBlank, blankAction, 'blank text'),
|
||
Handler(Paragraphs.isNormal, normalParaAction, 'normal paragraph')
|
||
], fallback);
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.States',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
// MOVE ME?
|
||
var isCloser = function (state, token) {
|
||
return token.type() === Token.END_ELEMENT_TYPE && state.originalToken.get() && token.tag() === state.originalToken.get().tag();
|
||
};
|
||
|
||
return {
|
||
isCloser: isCloser
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.AfterNoBulletListState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.States',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (States, Handler, Handlers, Transitions) {
|
||
var run = function (afterListState) {
|
||
|
||
var action = function (api, state, token) {
|
||
Transitions.next(state, afterListState);
|
||
/*
|
||
I think that this should be 0, but it breaks qUnit test cases in powerpaste if it isn't -1. Probably
|
||
need to look into it in more detail. The level 0 check in ListModel in the emitter should
|
||
take care of it anyway.
|
||
*/
|
||
state.styleLevelAdjust.set(-1);
|
||
api.emit(token);
|
||
};
|
||
|
||
var fallback = function (api, state, token) {
|
||
api.emit(token);
|
||
};
|
||
|
||
return Handlers('AfterNoBullet', [
|
||
Handler(States.isCloser, action, ' closing open tag')
|
||
], fallback);
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Images',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Tags'
|
||
],
|
||
|
||
function (Tags) {
|
||
|
||
var isEnd = Tags.isEndOf('IMG');
|
||
var isStart = Tags.isStartOf('IMG');
|
||
|
||
return {
|
||
isStart: isStart,
|
||
isEnd: isEnd
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Markers',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
|
||
var is = function (state, token) {
|
||
return token.tag() === 'A' || token.tag() === 'SPAN';
|
||
};
|
||
|
||
var isStart = function (state, token) {
|
||
return token.type() === Token.START_ELEMENT_TYPE && is(state, token);
|
||
};
|
||
|
||
var isEnd = function (state, token) {
|
||
return token.type() === Token.END_ELEMENT_TYPE && is(state, token);
|
||
};
|
||
|
||
return {
|
||
isStart: isStart,
|
||
isEnd: isEnd,
|
||
is: is
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.Spans',
|
||
|
||
[
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (Transitions) {
|
||
var deferAndPop = function (api, state, token) {
|
||
api.defer(token);
|
||
pop(api, state, token);
|
||
};
|
||
|
||
var deferAndPush = function (api, state, token) {
|
||
api.defer(token);
|
||
push(api, state, token);
|
||
};
|
||
|
||
var push = function (api, state, token) {
|
||
state.spanCount.get().push(token);
|
||
};
|
||
|
||
var pop = function (api, state, token) {
|
||
state.spanCount.get().pop();
|
||
};
|
||
|
||
var pushThen = function (listState) {
|
||
return function (api, state, token) {
|
||
push(api, state, token);
|
||
Transitions.next(state, listState);
|
||
};
|
||
};
|
||
|
||
var popThen = function (listState) {
|
||
return function (api, state, token) {
|
||
pop(api, state, token);
|
||
Transitions.next(state, listState);
|
||
};
|
||
};
|
||
|
||
return {
|
||
deferAndPush: deferAndPush,
|
||
deferAndPop: deferAndPop,
|
||
push: push,
|
||
pop: pop,
|
||
pushThen: pushThen,
|
||
popThen: popThen
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.BeforeSpacerState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Images',
|
||
'ephox.pastiche.inspect.Markers',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Images, Markers, Handler, Handlers, Spans, Fun) {
|
||
var run = function (spacerState, closeSpansState, unexpectedToken) {
|
||
|
||
var fallback = function (api, state, token) {
|
||
unexpectedToken(api, token);
|
||
};
|
||
|
||
return Handlers('BeforeSpacer', [
|
||
Handler(Markers.isStart, Spans.pushThen(spacerState), 'start marker'),
|
||
Handler(Markers.isEnd, Spans.popThen(closeSpansState), 'end marker'),
|
||
Handler(Images.isEnd, Fun.noop, 'end image')
|
||
], fallback);
|
||
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Comments',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
|
||
var is = function (state, token) {
|
||
return token.type() === Token.COMMENT_TYPE;
|
||
};
|
||
|
||
var isNotEndIf = function (state, token) {
|
||
return is(state, token) && token.text() !== '[endif]';
|
||
};
|
||
|
||
var isEndIf = function (state, token) {
|
||
return is(state, token) && token.text() === '[endif]';
|
||
};
|
||
|
||
var isListSupport = function (state, token) {
|
||
return is(state, token) && token.text() === '[if !supportLists]';
|
||
};
|
||
|
||
return {
|
||
is: is,
|
||
isNotEndIf: isNotEndIf,
|
||
isEndIf: isEndIf,
|
||
isListSupport: isListSupport
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Logic',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
var any = function (conditions) {
|
||
return function (state, token) {
|
||
return Arr.exists(conditions, function (c) {
|
||
return c(state, token);
|
||
});
|
||
};
|
||
};
|
||
|
||
var all = function (conditions) {
|
||
return function (state, token) {
|
||
return Arr.forall(conditions, function (c) {
|
||
return c(state, token);
|
||
});
|
||
};
|
||
};
|
||
|
||
return {
|
||
any: any,
|
||
all: all
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.CloseSpansState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Comments',
|
||
'ephox.pastiche.inspect.Logic',
|
||
'ephox.pastiche.inspect.Markers',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.inspect.Texts',
|
||
'ephox.pastiche.list.detect.ListTypes',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.perhaps.Result'
|
||
],
|
||
|
||
function (Comments, Logic, Markers, Tags, Texts, ListTypes, Handler, Handlers, Spans, Transitions, Fun, Option, Result) {
|
||
var run = function (itemContentState, unexpectedToken) {
|
||
|
||
var getListType = function (state) {
|
||
var currentType = state.emitter.getCurrentListType();
|
||
var currentLevel = state.emitter.getCurrentLevel();
|
||
// FIX: Don't coerce types.
|
||
var preferred = currentLevel == state.itemLevel.get() ? currentType : null;
|
||
return Option.from(ListTypes.guess(state.bulletInfo.get(), preferred, state.originalToken.get()));
|
||
};
|
||
|
||
var openItem = function (api, state, token) {
|
||
state.emitter.openItem(state.itemLevel.get(), state.originalToken.get(), state.listType.get(), state.skippedPara.get());
|
||
api.emitDeferred();
|
||
while (state.spanCount.get().length > 0) {
|
||
api.emit(state.spanCount.get().shift());
|
||
}
|
||
};
|
||
|
||
var updateState = function (state, token) {
|
||
Transitions.next(state, itemContentState);
|
||
if (state.commentMode.get()) {
|
||
var indent = state.indentGuesser.guessIndentLevel(token, state.originalToken.get(), state.styles, state.bulletInfo.get());
|
||
state.itemLevel.set(indent);
|
||
}
|
||
};
|
||
|
||
var tryItem = function (api, state, token) {
|
||
var listType = getListType(state);
|
||
return listType.fold(function () {
|
||
state.listType.set(null);
|
||
return Result.error("Unknown list type: " + state.bulletInfo.get().text + " Symbol font? " + state.bulletInfo.get().symbolFont);
|
||
}, function (type) {
|
||
state.listType.set(type);
|
||
return Result.value(openItem);
|
||
});
|
||
};
|
||
|
||
var updateAndEmit = function (api, state, token) {
|
||
updateState(state, token);
|
||
var emitter = tryItem(api, state, token);
|
||
emitter.fold(function (msg) {
|
||
console.log(msg);
|
||
api.rollback();
|
||
}, function (x) {
|
||
x(api, state, token);
|
||
api.emit(token);
|
||
});
|
||
};
|
||
|
||
var skipComment = function (api, state, token) {
|
||
updateState(state, token);
|
||
var emitter = tryItem(api, state, token);
|
||
emitter.fold(function (msg) {
|
||
console.log(msg);
|
||
api.rollback();
|
||
}, function (x) {
|
||
x(api, state, token);
|
||
});
|
||
};
|
||
|
||
var handlers = [
|
||
Handler(Logic.any([ Texts.is, Tags.isStart ]), updateAndEmit, 'text or start tag'),
|
||
Handler(Comments.isNotEndIf, updateAndEmit, 'non-endif comment'),
|
||
Handler(Comments.isEndIf, skipComment, 'endif comment'),
|
||
Handler(Markers.isEnd, Spans.pop, 'end marker'),
|
||
Handler(Tags.isEnd, Fun.noop, 'end tag')
|
||
];
|
||
|
||
return Handlers('CloseSpans', handlers, function (api, state, token) {
|
||
unexpectedToken(api, token);
|
||
});
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.FindListTypeState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Images',
|
||
'ephox.pastiche.inspect.Markers',
|
||
'ephox.pastiche.inspect.Texts',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Images, Markers, Texts, Handler, Handlers, Spans, Transitions, Fun) {
|
||
var run = function (beforeSpacerState, unexpectedToken) {
|
||
var action = function (bullets) {
|
||
return function (api, state, token) {
|
||
Transitions.next(state, beforeSpacerState);
|
||
state.bulletInfo.set(bullets(state, token));
|
||
};
|
||
};
|
||
|
||
var textAction = action(function (state, token) {
|
||
return {
|
||
text: token.text(),
|
||
symbolFont: state.symbolFont.get()
|
||
};
|
||
});
|
||
|
||
var imageAction = action(function (state, token) {
|
||
// Custom list image type. We can't access the image so use a normal bullet instead.
|
||
// EditLive! may want this to come through as a CSS reference.
|
||
return {
|
||
text: '\u2202',
|
||
symbolFont: true
|
||
};
|
||
});
|
||
|
||
var fallback = function (api, state, token) {
|
||
unexpectedToken(api, token);
|
||
};
|
||
|
||
return Handlers('FindListType', [
|
||
Handler(Texts.isWhitespace, Fun.noop, 'text is whitespace'),
|
||
Handler(Texts.is, textAction, 'text'),
|
||
Handler(Markers.isStart, Spans.push, 'start marker'),
|
||
Handler(Markers.isEnd, Spans.pop, 'end marker'),
|
||
Handler(Images.isStart, imageAction, 'start image')
|
||
], fallback);
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.ItemContentState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.States',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (States, Handler, Handlers, Transitions) {
|
||
var run = function (afterListState) {
|
||
|
||
var closeItem = function (api, state, token) {
|
||
Transitions.next(state, afterListState);
|
||
state.skippedPara.set(false);
|
||
};
|
||
|
||
var handlers = [
|
||
Handler(States.isCloser, closeItem, 'Closing open tag')
|
||
];
|
||
|
||
return Handlers('ItemContentState', handlers, function (api, state, token) {
|
||
api.emit(token);
|
||
});
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.CommentOff',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Comments',
|
||
'ephox.pastiche.inspect.Texts'
|
||
],
|
||
|
||
function (Comments, Texts) {
|
||
var isText = function (state, token) {
|
||
return !state.commentMode.get() && Texts.is(state, token);
|
||
};
|
||
|
||
var isComment = function (state, token) {
|
||
return !state.commentMode.get() && Comments.is(state, token);
|
||
};
|
||
|
||
return {
|
||
isText: isText,
|
||
isComment: isComment
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.CommentOn',
|
||
|
||
[
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
'ephox.pastiche.inspect.Comments',
|
||
'ephox.pastiche.inspect.Texts'
|
||
],
|
||
|
||
function (TokenUtil, Comments, Texts) {
|
||
var isText = function (state, token) {
|
||
return state.commentMode.get() && Texts.is(state, token);
|
||
};
|
||
|
||
var isComment = function (state, token) {
|
||
return state.commentMode.get() && Comments.is(state, token);
|
||
};
|
||
|
||
var isUnstyled = function (state, token) {
|
||
var style = TokenUtil.getAttribute(token, 'style');
|
||
return state.commentMode.get() && style === "" || style === null;
|
||
};
|
||
|
||
return {
|
||
isText: isText,
|
||
isComment: isComment,
|
||
isUnstyled: isUnstyled
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.ListStartState',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.inspect.Logic',
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.inspect.Texts',
|
||
'ephox.pastiche.list.detect.ListSymbols',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.CommentOff',
|
||
'ephox.pastiche.list.state.CommentOn',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Logic, Microsoft, Tags, Texts, ListSymbols, Handler, Handlers, CommentOff, CommentOn, Spans, Transitions, Fun) {
|
||
var run = function (findListTypeState, afterNoBulletListState, unexpectedToken) {
|
||
|
||
/* In one of the adhoc tests on Word 2007, Win XP ... a span with a massive number of nbsp was
|
||
added at the ListStart stage. This was added to jump to the end of it. */
|
||
var skipWhitespace = function () {
|
||
return Handlers('TESTER', [
|
||
Handler(Tags.isEndOf('SPAN'), Transitions.setNext(result), 'Finishing span'),
|
||
Handler(Texts.isWhitespace, Fun.noop, 'Is whitespace')
|
||
], function (api, state, token) {
|
||
unexpectedToken(api, token);
|
||
});
|
||
};
|
||
|
||
var noBullet = function (api, state, token) {
|
||
// List type without a bullet, we should treat it as a paragraph.
|
||
|
||
// What about if it is 1. or something similar?
|
||
var start = state.originalToken.get();
|
||
var spans = state.spanCount.get();
|
||
state.emitter.closeAllLists();
|
||
api.emit(start);
|
||
Arr.each(spans, api.emit);
|
||
api.emit(token);
|
||
api.commit();
|
||
state.originalToken.set(start);
|
||
Transitions.next(state, afterNoBulletListState);
|
||
};
|
||
|
||
var isBulletSymbol = function (s, t) {
|
||
return Texts.is(s, t) && ListSymbols.match(t.text(), s.symbolFont.get()).length > 0;
|
||
};
|
||
|
||
var result = function (api, state, token) {
|
||
if (Microsoft.isIgnore(state, token)) {
|
||
Transitions.next(state, findListTypeState);
|
||
}
|
||
|
||
var r = Handlers('ListStartState', [
|
||
Handler(
|
||
Logic.all([ Tags.isStartOf('SPAN'), CommentOn.isUnstyled ]),
|
||
Spans.pushThen(findListTypeState),
|
||
'unstyled span'
|
||
),
|
||
|
||
// This handler was added due to adhoc Word 2007 XP content. It breaks QUnit tests.
|
||
/* Handler(Logic.all([ Tags.isStart, Tags.isWhitespace('SPAN') ]), Transitions.setNext(skipWhitespace()), 'a whitespace tag'), */
|
||
Handler(Tags.isStartOf('SPAN'), Spans.push, 'starting span'),
|
||
Handler(Tags.isStartOf('A'), Spans.push, 'starting a'),
|
||
Handler(Tags.isEndOf('A'), Spans.pop, 'ending a'),
|
||
Handler(CommentOn.isText, Transitions.jump(findListTypeState), 'commentOn -> text'),
|
||
// This handler is new. It may be a problem.
|
||
Handler(isBulletSymbol, Transitions.jump(findListTypeState), 'mogran :: text is [1-9].'),
|
||
Handler(Texts.is, noBullet, 'text'),
|
||
Handler(CommentOff.isComment, Fun.noop, 'commentOff -> comment'),
|
||
|
||
// This was added to handle more gracefully the images in some of the test data. May be wrong.
|
||
Handler(Tags.isStartOf('IMG'), Transitions.jump(findListTypeState), 'mogran :: start image tag')
|
||
], function (api, state, token) {
|
||
unexpectedToken(api, token);
|
||
});
|
||
|
||
return r(api, state, token);
|
||
};
|
||
|
||
result.toString = function () { return 'Handlers for ListStartState'; };
|
||
return result;
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Browser',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
'global!document',
|
||
'global!navigator'
|
||
],
|
||
|
||
function (Token, Microsoft, document, navigator) {
|
||
var supportsCustomStyles = function () {
|
||
// Firefox 4 preserves these styles in the DOM, but strips them when pasting.
|
||
// Since we can't trigger a paste there's no way to detect this situation apart from sniffing.
|
||
if (navigator.userAgent.indexOf('Gecko') > 0 && navigator.userAgent.indexOf('WebKit') < 0) return false;
|
||
var div = document.createElement('div');
|
||
try {
|
||
div.innerHTML = '<p style="mso-list: Ignore;"> </p>';
|
||
} catch (ex) {
|
||
// Can't set innerHTML if we're in XHTML mode so just assume we don't get custom styles.
|
||
return false;
|
||
}
|
||
|
||
var token = Token.token(div.firstChild);
|
||
// INVESTIGATE: Does this need to be lowercased?
|
||
return Microsoft.isIgnore({}/*state*/, token);
|
||
};
|
||
|
||
return {
|
||
supportsCustomStyles: supportsCustomStyles
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.ImageList',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var is = function (state, node, symbol) {
|
||
var alt = node !== undefined && node !== null && node.getAttribute !== undefined && node.getAttribute !== null ? node.getAttribute('alt') : '';
|
||
// The check here for === * is because we are disabling image lists as they
|
||
// interfere with regular images. The one exception where it is reasonable
|
||
// to assume it is a list is if the alt text is *.
|
||
return !!node && node.tagName === 'IMG' && alt === '*';
|
||
};
|
||
|
||
return {
|
||
is: is
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.TextList',
|
||
|
||
[
|
||
'ephox.pastiche.list.detect.ListTypes',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (ListTypes, Strings) {
|
||
var is = function (state, rawNode, symbol) {
|
||
var node = rawNode;
|
||
var value = node.nodeValue;
|
||
if (!Strings.trim(value)) {
|
||
// This handles the case where there's a SPAN with nbsps before the bullet such as with roman numerals.
|
||
node = node.parentNode.nextSibling;
|
||
value = node ? node.nodeValue : '';
|
||
}
|
||
|
||
// Real lists have the bullet with NBSPs either side surrounded in a SPAN. If there's anything else, it's not a list.
|
||
if (!node || Strings.trim(node.parentNode.textContent) != value) {
|
||
return false;
|
||
}
|
||
listType = ListTypes.guess({ text: value, symbolFont: symbol }, null, state.originalToken.get());
|
||
|
||
if (listType) {
|
||
|
||
// Don't convert numbered headings to lists.
|
||
var r = !!node.nextSibling && node.nextSibling.tagName === 'SPAN' && /^[\u00A0\s]/.test(node.nextSibling.firstChild.nodeValue) &&
|
||
(state.openedTag.get().tag() === 'P' || listType.tag === 'UL');
|
||
return r;
|
||
} else {
|
||
return false;
|
||
}
|
||
};
|
||
|
||
return {
|
||
is: is
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.detect.ListDetect',
|
||
|
||
[
|
||
'ephox.highway.Merger',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.list.detect.ImageList',
|
||
'ephox.pastiche.list.detect.TextList'
|
||
],
|
||
|
||
function (Merger, Tags, ImageList, TextList) {
|
||
|
||
var update = function (output) {
|
||
var node = output.node;
|
||
var font = node.style.fontFamily;
|
||
return font ? Merger.merge(output, { symbol: (font === 'Wingdings' || font === 'Symbol') }) : output;
|
||
};
|
||
|
||
var hasMarkerFirst = function (node) {
|
||
return !!node.firstChild && (node.firstChild.tagName === 'SPAN' || node.firstChild.tagName === 'A');
|
||
};
|
||
|
||
var findItem = function (node) {
|
||
var output = update({ node: node });
|
||
|
||
var initialEmptyA = node.childNodes.length > 1 && output.node.firstChild.tagName === 'A' && output.node.firstChild.textContent === '';
|
||
output = initialEmptyA ? { node: output.node.childNodes[1], symbol: output.symbol } : output;
|
||
|
||
while (hasMarkerFirst(output.node)) {
|
||
output = update({ node: output.node.firstChild, symbol: output.symbol });
|
||
}
|
||
|
||
return {
|
||
node: output.node.firstChild,
|
||
symbol: output.symbol
|
||
};
|
||
};
|
||
|
||
var isUnofficialList = function (state, token) {
|
||
if (!Tags.isStartOf('SPAN')(state, token) || !state.openedTag.get()) return false;
|
||
var node = state.openedTag.get().getNode();
|
||
var item = findItem(node);
|
||
var detector = item.node && item.node.nodeType === 3 ? TextList : ImageList;
|
||
return detector.is(state, item.node, item.symbol);
|
||
};
|
||
|
||
return {
|
||
isUnofficialList: isUnofficialList
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.inspect.Lists',
|
||
|
||
[
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
'ephox.pastiche.inspect.Browser',
|
||
'ephox.pastiche.inspect.Comments',
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.list.detect.ListDetect',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (TokenUtil, Browser, Comments, Microsoft, Tags, ListDetect, Option) {
|
||
var getLevel = function (token) {
|
||
var msoList = TokenUtil.getMsoList(token);
|
||
var lvl = / level([0-9]+)/.exec(msoList);
|
||
return lvl && lvl[1] ? Option.some(parseInt(lvl[1], 10)) : Option.none();
|
||
};
|
||
|
||
var isStart = function (state, token) {
|
||
return Tags.isStart(state, token) && Microsoft.isList(state, token) && token.tag() !== 'LI';
|
||
};
|
||
|
||
var isValidStart = function (state, token) {
|
||
return isStart(state, token) && getLevel(token).isSome();
|
||
};
|
||
|
||
var isInvalidStart = function (state, token) {
|
||
return isStart(state, token) && getLevel(token).isNone();
|
||
};
|
||
|
||
var isSpecial = function (state, token) {
|
||
var custom = Browser.supportsCustomStyles();
|
||
return !custom && Comments.isListSupport(state, token) || ListDetect.isUnofficialList(state, token);
|
||
};
|
||
|
||
return {
|
||
getLevel: getLevel,
|
||
isStart: isStart,
|
||
isValidStart: isValidStart,
|
||
isInvalidStart: isInvalidStart,
|
||
isSpecial: isSpecial
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.NoListState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Lists',
|
||
'ephox.pastiche.inspect.Markers',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (Lists, Markers, Tags, Handler, Handlers, Spans, Transitions) {
|
||
var run = function (lazyListStartState) {
|
||
|
||
var startList = function (api, state, token) {
|
||
var level = Lists.getLevel(token);
|
||
level.each(function (l) {
|
||
state.itemLevel.set(l + state.styleLevelAdjust.get());
|
||
// Tokens between lists should be dropped (they're just whitespace anyway)
|
||
// however, tokens before a list should be emitted if we find an mso-list style
|
||
// since this is the very first token of the list.
|
||
var deferring = Transitions.isNext(state, result) ? api.emitDeferred : api.dropDeferred;
|
||
deferring();
|
||
|
||
Transitions.next(state, lazyListStartState());
|
||
api.startTransaction();
|
||
state.originalToken.set(token);
|
||
state.commentMode.set(false);
|
||
});
|
||
};
|
||
|
||
var specialList = function (api, state, token) {
|
||
if (Tags.isStartOf('SPAN')(state, token)) {
|
||
Spans.push(api, state, token);
|
||
}
|
||
Transitions.next(state, lazyListStartState());
|
||
api.startTransaction();
|
||
state.originalToken.set(state.openedTag.get());
|
||
state.commentMode.set(true);
|
||
state.openedTag.set(null);
|
||
api.dropDeferred();
|
||
};
|
||
|
||
var startTag = function (api, state, token) {
|
||
if (state.openedTag.get()) {
|
||
state.emitter.closeAllLists();
|
||
api.emitDeferred();
|
||
}
|
||
state.openedTag.set(token);
|
||
api.defer(token);
|
||
};
|
||
|
||
var closeOutLists = function (api, state, token) {
|
||
state.emitter.closeAllLists();
|
||
api.emitDeferred();
|
||
state.openedTag.set(null);
|
||
api.emit(token);
|
||
Transitions.next(state, result);
|
||
};
|
||
|
||
var result = Handlers('NoListState', [
|
||
Handler(Lists.isValidStart, startList, 'valid list so start it'),
|
||
Handler(Lists.isInvalidStart, closeOutLists, 'invalid list so close lists'),
|
||
Handler(Lists.isSpecial, specialList, 'special list'),
|
||
Handler(Markers.isEnd, Spans.deferAndPop, 'closing marker'),
|
||
Handler(Markers.isStart, Spans.deferAndPush, 'starting marker'),
|
||
Handler(Tags.isStart, startTag, 'starting tag')
|
||
], closeOutLists);
|
||
|
||
return result;
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.SkipEmptyParaState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Browser',
|
||
'ephox.pastiche.inspect.Logic',
|
||
'ephox.pastiche.inspect.Microsoft',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.inspect.Texts',
|
||
'ephox.pastiche.list.detect.ListDetect',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (Browser, Logic, Microsoft, Tags, Texts, ListDetect, Handler, Handlers, Spans, Transitions) {
|
||
var run = function (noListState, lazyAfterListState) {
|
||
|
||
var isFirstMarker = function (state, token) {
|
||
return Tags.isStartOf('SPAN')(state, token) && state.spanCount.get().length === 0;
|
||
};
|
||
|
||
var isNotList = function (state, token) {
|
||
return (Browser.supportsCustomStyles() || !ListDetect.isUnofficialList(state, token)) && !Microsoft.isList(state, token);
|
||
};
|
||
|
||
var skip = function (api, state, token) {
|
||
api.defer(token);
|
||
state.skippedPara.set(true);
|
||
state.openedTag.set(null);
|
||
Transitions.next(state, lazyAfterListState());
|
||
};
|
||
|
||
var defer = function (api, state, token) {
|
||
api.defer(token);
|
||
};
|
||
|
||
return Handlers('SkipEmptyPara', [
|
||
Handler(Logic.all([ isFirstMarker, isNotList ]), Spans.deferAndPush, 'first marker or not list'),
|
||
Handler(Tags.isEndOf('SPAN'), Spans.deferAndPop, 'end span'),
|
||
Handler(Tags.isEndOf('P'), skip, 'end p'),
|
||
Handler(Tags.isEnd, Transitions.jump(noListState), 'end tag'),
|
||
Handler(Texts.isWhitespace, defer, 'whitespace')
|
||
], Transitions.jump(noListState));
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.stage.SpacerState',
|
||
|
||
[
|
||
'ephox.pastiche.inspect.Markers',
|
||
'ephox.pastiche.inspect.Tags',
|
||
'ephox.pastiche.list.stage.Handler',
|
||
'ephox.pastiche.list.stage.Handlers',
|
||
'ephox.pastiche.list.state.Spans',
|
||
'ephox.pastiche.list.state.Transitions',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Markers, Tags, Handler, Handlers, Spans, Transitions, Fun) {
|
||
var run = function (closeSpansState) {
|
||
return Handlers('Spacer', [
|
||
Handler(Markers.isEnd, Spans.popThen(closeSpansState), 'end marker'),
|
||
Handler(Tags.isEnd, Transitions.setNext(closeSpansState), 'end tag')
|
||
], Fun.noop);
|
||
};
|
||
|
||
return {
|
||
run: run
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.Emission',
|
||
|
||
[
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Struct) {
|
||
var result = Struct.immutable('state', 'result');
|
||
var value = Struct.immutable('state', 'value');
|
||
var state = Struct.immutable('level', 'type', 'types', 'items');
|
||
|
||
return {
|
||
state: state,
|
||
value: value,
|
||
result: result
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.ItemStack',
|
||
|
||
[
|
||
'ephox.pastiche.list.emit.Emission',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Emission, Option) {
|
||
var finish = function (state) {
|
||
var stack = state.items().slice(0);
|
||
if (stack.length > 0 && stack[stack.length - 1] !== 'P') {
|
||
var item = stack[stack.length - 1];
|
||
stack[stack.length - 1] = 'P';
|
||
var newState = Emission.state(state.level(), state.type(), state.types(), stack);
|
||
return Emission.value(newState, Option.some(item));
|
||
} else {
|
||
return Emission.value(state, Option.none());
|
||
}
|
||
};
|
||
|
||
var start = function (state, tag) {
|
||
var stack = state.items().slice(0);
|
||
var value = tag !== undefined && tag !== 'P' ? Option.some(tag) : Option.none();
|
||
|
||
value.fold(function () {
|
||
stack.push('P');
|
||
}, function (v) {
|
||
stack.push(v);
|
||
});
|
||
|
||
var newState = Emission.state(state.level(), state.type(), state.types(), stack);
|
||
return Emission.value(newState, value);
|
||
};
|
||
|
||
return {
|
||
start: start,
|
||
finish: finish
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.ListLevels',
|
||
|
||
[
|
||
'ephox.pastiche.list.emit.Emission'
|
||
],
|
||
|
||
function (Emission) {
|
||
var moveUntil = function (state, predicate, f) {
|
||
var tokens = [];
|
||
|
||
var current = state;
|
||
while (predicate(current)) {
|
||
var acc = f(current);
|
||
current = acc.state();
|
||
tokens = tokens.concat(acc.result());
|
||
}
|
||
return Emission.result(current, tokens);
|
||
};
|
||
|
||
var moveRight = function (state, level, open) {
|
||
var predicate = function (s) {
|
||
return s.level() < level;
|
||
};
|
||
|
||
return moveUntil(state, predicate, open);
|
||
};
|
||
|
||
var moveLeft = function (state, level, close) {
|
||
var predicate = function (state) {
|
||
return state.level() > level;
|
||
};
|
||
|
||
return moveUntil(state, predicate, close);
|
||
};
|
||
|
||
return {
|
||
moveRight: moveRight,
|
||
moveLeft: moveLeft,
|
||
moveUntil: moveUntil
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.ListItemStyles',
|
||
|
||
[
|
||
'ephox.pastiche.engine.TokenUtil'
|
||
],
|
||
|
||
function (TokenUtil) {
|
||
|
||
var unsafeFrom = function (token) {
|
||
var leftMargin = TokenUtil.getStyle(token, 'margin-left');
|
||
return leftMargin !== undefined && leftMargin !== '0px' ? { 'margin-left': leftMargin } : {};
|
||
};
|
||
|
||
var from = function (token) {
|
||
var noToken = {
|
||
'list-style-type': 'none'
|
||
};
|
||
|
||
return !token ? noToken : unsafeFrom(token);
|
||
};
|
||
|
||
return {
|
||
from: from
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.SkippedTokens',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Token, Fun) {
|
||
var para = function (doc) {
|
||
return [
|
||
Fun.curry(Token.createStartElement, 'P', {}, {}),
|
||
Fun.curry(Token.createText, '\u00A0'),
|
||
Fun.curry(Token.createEndElement, 'P')
|
||
];
|
||
};
|
||
|
||
return {
|
||
para: para
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.ListTokens',
|
||
|
||
[
|
||
'ephox.pastiche.cleanup.Cleaners',
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.list.detect.ListTypes',
|
||
'ephox.pastiche.list.emit.Emission',
|
||
'ephox.pastiche.list.emit.ItemStack',
|
||
'ephox.pastiche.list.emit.ListItemStyles',
|
||
'ephox.pastiche.list.emit.SkippedTokens',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Cleaners, Token, ListTypes, Emission, ItemStack, ListItemStyles, SkippedTokens, Fun) {
|
||
var open = function(state, type, style) {
|
||
var attributes = type.start && type.start > 1 ? { start: type.start } : {};
|
||
var level = state.level() + 1;
|
||
var listType = type;
|
||
var listTypes = state.types().concat([type]);
|
||
var result = [ Fun.curry(Token.createStartElement, type.tag, attributes, style) ];
|
||
var newState = Emission.state(level, listType, listTypes, state.items());
|
||
return Emission.result(newState, result);
|
||
};
|
||
|
||
var close = function(state) {
|
||
var listTypes = state.types().slice(0);
|
||
var result = [ Fun.curry(Token.createEndElement, listTypes.pop().tag) ];
|
||
var level = state.level() - 1;
|
||
var listType = listTypes[listTypes.length - 1];
|
||
var newState = Emission.state(level, listType, listTypes, state.items());
|
||
return Emission.result(newState, result);
|
||
};
|
||
|
||
var shuffle = function (state, type, skippedPara) {
|
||
var e1 = close(state);
|
||
var extra = skippedPara ? SkippedTokens.para() : [];
|
||
var e2 = open(e1.state(), type, type.type ? { 'list-style-type': type.type } : {});
|
||
return Emission.result(e2.state(), e1.result().concat(extra).concat(e2.result()));
|
||
};
|
||
|
||
var openItem = function(state, paragraphToken, type, skippedPara) {
|
||
var attributes = {};
|
||
var style = ListItemStyles.from(paragraphToken);
|
||
|
||
var e1 = state.type() && !ListTypes.eqListType(state.type(), type) ?
|
||
shuffle(state, type, skippedPara) :
|
||
Emission.result(state, []);
|
||
var tokens = [ Fun.curry(Token.createStartElement, 'LI', attributes, style) ];
|
||
|
||
var e2 = ItemStack.start(e1.state(), paragraphToken && paragraphToken.tag());
|
||
var moreTokens = e2.value().map(function (v) {
|
||
Cleaners.styleDom(paragraphToken.getNode(), Fun.constant(true));
|
||
return [ Fun.constant(paragraphToken) ];
|
||
}).getOr([]);
|
||
|
||
return Emission.result(e2.state(), e1.result().concat(tokens).concat(moreTokens));
|
||
};
|
||
|
||
var closeItem = function(state) {
|
||
var li = Fun.curry(Token.createEndElement, 'LI');
|
||
var e1 = ItemStack.finish(state);
|
||
var r = e1.value().fold(function () {
|
||
return [ li ];
|
||
}, function (item) {
|
||
return [ Fun.curry(Token.createEndElement, item), li ];
|
||
});
|
||
return Emission.result(e1.state(), r);
|
||
};
|
||
|
||
return {
|
||
open: open,
|
||
openItem: openItem,
|
||
close: close,
|
||
closeItem: closeItem
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.ListModel',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.pastiche.list.emit.Emission',
|
||
'ephox.pastiche.list.emit.ItemStack',
|
||
'ephox.pastiche.list.emit.ListLevels',
|
||
'ephox.pastiche.list.emit.ListTokens',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Arr, Token, Emission, ItemStack, ListLevels, ListTokens, Fun, Option) {
|
||
var compose = function (emissions) {
|
||
if (emissions.length === 0) throw 'Compose must have at least one element in the list';
|
||
var last = emissions[emissions.length - 1];
|
||
var tokens = Arr.bind(emissions, function (emission) {
|
||
return emission.result();
|
||
});
|
||
return Emission.result(last.state(), tokens);
|
||
};
|
||
|
||
var closeLevel = function (state) {
|
||
var e1 = ListTokens.closeItem(state);
|
||
var e2 = ListTokens.close(e1.state());
|
||
return compose([ e1, e2 ]);
|
||
};
|
||
|
||
var openLevel = function (state, type, level, paragraphToken) {
|
||
var style = state.level() === level - 1 && type.type ? { 'list-style-type': type.type } : {};
|
||
var e1 = ListTokens.open(state, type, style);
|
||
var e2 = ListTokens.openItem(e1.state(), e1.state().level() == level ? paragraphToken : undefined, type);
|
||
return compose([ e1, e2 ]);
|
||
};
|
||
|
||
var sameLevel = function (state, type, paragraphToken, skippedPara) {
|
||
var e1 = state.level() > 0 ? ListTokens.closeItem(state) : Emission.result(state, []);
|
||
var e2 = ListTokens.openItem(e1.state(), paragraphToken, type, skippedPara);
|
||
return compose([ e1, e2 ]);
|
||
};
|
||
|
||
var openLevels = function (state, type, level, paragraphToken) {
|
||
return ListLevels.moveRight(state, level, function (s) {
|
||
return openLevel(s, type, level, paragraphToken);
|
||
});
|
||
};
|
||
|
||
var closeLevels = function (state, level) {
|
||
return ListLevels.moveLeft(state, level, closeLevel);
|
||
};
|
||
|
||
var jumpToLevel = function (state, type, level, paragraphToken) {
|
||
var e1 = level > 1 ? ItemStack.finish(state) : Emission.value(state, Option.none());
|
||
var tokens = e1.value().map(function (tag) {
|
||
return [ Fun.curry(Token.createEndElement, tag) ];
|
||
}).getOr([]);
|
||
|
||
var numLevels = level - e1.state().level();
|
||
var e2 = openLevels(e1.state(), type, level, paragraphToken);
|
||
return Emission.result(e2.state(), tokens.concat(e2.result()));
|
||
};
|
||
|
||
var openItem = function(state, level, paragraphToken, type, skippedPara) {
|
||
var e1 = state.level() > level ? closeLevels(state, level) : Emission.result(state, []);
|
||
var e2 = e1.state().level() === level ?
|
||
sameLevel(e1.state(), type, paragraphToken, skippedPara) :
|
||
jumpToLevel(e1.state(), type, level, paragraphToken);
|
||
return compose([ e1, e2 ]);
|
||
};
|
||
|
||
var closeAllLists = closeLevels;
|
||
|
||
return {
|
||
openItem: openItem,
|
||
closeAllLists: closeAllLists
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.emit.Emitter',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.list.emit.Emission',
|
||
'ephox.pastiche.list.emit.ListModel'
|
||
],
|
||
|
||
function (Arr, Emission, ListModel) {
|
||
var impliedULatLevel = [ 'disc', 'circle', 'square' ];
|
||
|
||
var removeImpliedListType = function(type, level) {
|
||
if (type.tag === 'UL') {
|
||
if (impliedULatLevel[level - 1] === type.type) {
|
||
type = { tag: 'UL' };
|
||
}
|
||
}
|
||
return type;
|
||
};
|
||
|
||
return function (api, doc) {
|
||
|
||
var state = Emission.state(0, undefined, [], []);
|
||
|
||
var emit = function (emission) {
|
||
Arr.each(emission.result(), function (x) {
|
||
var token = x(doc);
|
||
api.emit(token);
|
||
});
|
||
};
|
||
|
||
var closeAllLists = function() {
|
||
var e1 = ListModel.closeAllLists(state, 0);
|
||
state = e1.state();
|
||
emit(e1);
|
||
api.commit();
|
||
};
|
||
|
||
var openItem = function(level, paragraphToken, type, skippedPara) {
|
||
var style = {}, token;
|
||
if (!type) return;
|
||
var cleanType = removeImpliedListType(type, level);
|
||
var e1 = ListModel.openItem(state, level, paragraphToken, cleanType, skippedPara);
|
||
state = e1.state();
|
||
emit(e1);
|
||
};
|
||
|
||
var getCurrentLevel = function() {
|
||
return state.level();
|
||
};
|
||
var getCurrentListType = function() {
|
||
return state.type();
|
||
};
|
||
|
||
return {
|
||
closeAllLists: closeAllLists,
|
||
openItem: openItem,
|
||
getCurrentListType: getCurrentListType,
|
||
getCurrentLevel: getCurrentLevel
|
||
};
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.indent.ListIndent',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.pastiche.engine.TokenUtil',
|
||
'ephox.violin.Strings',
|
||
'global!Math'
|
||
],
|
||
|
||
function (Arr, TokenUtil, Strings, Math) {
|
||
var getLeftOffset = function(node, paragraph) {
|
||
var parent, child, offset = 0;
|
||
parent = node.parentNode;
|
||
while (parent !== null && parent !== paragraph.parentNode) {
|
||
offset += parent.offsetLeft;
|
||
parent = parent.offsetParent;
|
||
}
|
||
return offset;
|
||
};
|
||
|
||
var ephoxGetComputedStyle = function(node) {
|
||
if (node.ownerDocument.defaultView) {
|
||
return node.ownerDocument.defaultView.getComputedStyle(node, null);
|
||
}
|
||
return node.currentStyle || {};
|
||
};
|
||
|
||
|
||
/** A simplified memoize function which only supports one or two function parameters.
|
||
*
|
||
* @param fn
|
||
* @param param the funtion p
|
||
* @returns
|
||
*/
|
||
var memoize2 = function(fn) {
|
||
var cache = {};
|
||
return function(param1, param2) {
|
||
var result, key = param1 + "," + param2;
|
||
if (cache.hasOwnProperty(key)) {
|
||
return cache[key];
|
||
}
|
||
result = fn.call(null, param1, param2);
|
||
cache[key] = result;
|
||
return result;
|
||
};
|
||
};
|
||
|
||
var findStyles = memoize2(function(css, className) {
|
||
var results, matcher = /([^{]+){([^}]+)}/g, match, el, computedStyle;
|
||
matcher.lastIndex = 0; // Firefox Mac reuses the same regex so we need to reset it.
|
||
while ((results = matcher.exec(css)) !== null && !match) {
|
||
Arr.each(results[1].split(','), function(selector) {
|
||
var dotIndex = selector.indexOf('.');
|
||
if (dotIndex >= 0 && Strings.trim(selector.substring(dotIndex + 1)) === className) {
|
||
match = results[2];
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
if (match) {
|
||
el = document.createElement('p');
|
||
el.setAttribute("style", match);
|
||
computedStyle = ephoxGetComputedStyle(el);
|
||
return computedStyle ? "" + computedStyle.marginLeft : false;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
var indentGuesser = function() {
|
||
|
||
var listIndentAdjust;
|
||
var listIndentAmount;
|
||
var guessIndentLevel = function(currentToken, token, styles, bulletInfo) {
|
||
var indentAmount, itemIndent, el, level = 1;
|
||
|
||
if (bulletInfo && /^([0-9]+\.)+[0-9]+\.?$/.test(bulletInfo.text)) {
|
||
// Outline list type so we can just count the number of sections.
|
||
return bulletInfo.text.replace(/([0-9]+|\.$)/g, '').length + 1;
|
||
}
|
||
indentAmount = listIndentAmount || parseInt(findStyles(styles, TokenUtil.getAttribute(token, 'class')));
|
||
|
||
itemIndent = getLeftOffset(currentToken.getNode(), token.getNode());
|
||
if (!indentAmount) {
|
||
indentAmount = 48;
|
||
} else {
|
||
// We might get a 0 item indent if the list CSS code wasn't pasted as happens on Windows.
|
||
if (listIndentAdjust) {
|
||
itemIndent += listIndentAdjust;
|
||
} else if (itemIndent === 0) {
|
||
listIndentAdjust = indentAmount;
|
||
itemIndent += indentAmount;
|
||
}
|
||
}
|
||
listIndentAmount = indentAmount = Math.min(itemIndent, indentAmount);
|
||
level = Math.max(1, Math.floor(itemIndent / indentAmount)) || 1;
|
||
return level;
|
||
};
|
||
return {
|
||
guessIndentLevel: guessIndentLevel
|
||
};
|
||
};
|
||
|
||
return {
|
||
indentGuesser: indentGuesser
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.ListStyles',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Token'
|
||
],
|
||
|
||
function (Token) {
|
||
return function () {
|
||
var inStyle = false;
|
||
var styles = "";
|
||
var check = function(token) {
|
||
if (inStyle && token.type() === Token.TEXT_TYPE) {
|
||
styles += token.text();
|
||
return true;
|
||
} else if (token.type() === Token.START_ELEMENT_TYPE && token.tag() === 'STYLE') {
|
||
inStyle = true;
|
||
return true;
|
||
} else if (token.type() === Token.END_ELEMENT_TYPE && token.tag() === 'STYLE') {
|
||
inStyle = false;
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
return {
|
||
check: check
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.scullion.Cell',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var Cell = function (initial) {
|
||
var value = initial;
|
||
|
||
var get = function () {
|
||
return value;
|
||
};
|
||
|
||
var set = function (v) {
|
||
value = v;
|
||
};
|
||
|
||
var clone = function () {
|
||
return Cell(get());
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
set: set,
|
||
clone: clone
|
||
};
|
||
};
|
||
|
||
return Cell;
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.list.state.ListState',
|
||
|
||
[
|
||
'ephox.pastiche.list.emit.Emitter',
|
||
'ephox.pastiche.list.indent.ListIndent',
|
||
'ephox.pastiche.list.state.ListStyles',
|
||
'ephox.peanut.Fun',
|
||
'ephox.scullion.Cell'
|
||
],
|
||
|
||
function (Emitter, ListIndent, ListStyles, Fun, Cell) {
|
||
|
||
var indentGuesser = ListIndent.indentGuesser();
|
||
var styles = ListStyles();
|
||
|
||
|
||
var emitter = {
|
||
getCurrentListType: function () {
|
||
return lazyEmitter().getCurrentListType();
|
||
},
|
||
getCurrentLevel: function () {
|
||
return lazyEmitter().getCurrentLevel();
|
||
},
|
||
closeAllLists: function () {
|
||
return lazyEmitter().closeAllLists.apply(undefined, arguments);
|
||
},
|
||
openItem: function () {
|
||
return lazyEmitter().openItem.apply(undefined, arguments);
|
||
}
|
||
};
|
||
|
||
var lazyEmitter = function () {
|
||
return {
|
||
getCurrentListType: Fun.constant({}),
|
||
getCurrentLevel: Fun.constant(1),
|
||
closeAllLists: Fun.identity,
|
||
openItem: Fun.identity
|
||
};
|
||
};
|
||
|
||
return function (initialState) {
|
||
var nextFilter = Cell(initialState);
|
||
var itemLevel = Cell(0);
|
||
var originalToken = Cell(null);
|
||
var commentMode = Cell(false);
|
||
var openedTag = Cell(null);
|
||
var symbolFont = Cell(false);
|
||
var listType = Cell(null);
|
||
var spanCount = Cell([]);
|
||
var skippedPara = Cell(false);
|
||
var styleLevelAdjust = Cell(0);
|
||
var bulletInfo = Cell(undefined);
|
||
var logger = Cell([]);
|
||
|
||
var reset = function (api) {
|
||
nextFilter.set(initialState);
|
||
itemLevel.set(0);
|
||
originalToken.set(null);
|
||
commentMode.set(false);
|
||
openedTag.set(null);
|
||
symbolFont.set(false);
|
||
listType.set(null);
|
||
spanCount.set([]);
|
||
skippedPara.set(false);
|
||
styleLevelAdjust.set(0);
|
||
bulletInfo.set(undefined);
|
||
logger.set([]);
|
||
|
||
// ASSUMPTION: I think this approach in the past also papered over the stack needing resetting in the emitter.
|
||
_emitter = Emitter(api, api.document);
|
||
lazyEmitter = Fun.constant(_emitter);
|
||
};
|
||
|
||
return {
|
||
reset: reset,
|
||
nextFilter: nextFilter,
|
||
itemLevel: itemLevel,
|
||
originalToken: originalToken,
|
||
commentMode: commentMode,
|
||
openedTag: openedTag,
|
||
symbolFont: symbolFont,
|
||
listType: listType,
|
||
spanCount: spanCount,
|
||
skippedPara: skippedPara,
|
||
styleLevelAdjust: styleLevelAdjust,
|
||
bulletInfo: bulletInfo,
|
||
logger: logger,
|
||
|
||
emitter: emitter,
|
||
styles: styles,
|
||
indentGuesser: indentGuesser
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.pastiche.filter.Lists',
|
||
|
||
[
|
||
'ephox.pastiche.engine.Filter',
|
||
'ephox.pastiche.list.detect.ListTypes',
|
||
'ephox.pastiche.list.stage.AfterListState',
|
||
'ephox.pastiche.list.stage.AfterNoBulletListState',
|
||
'ephox.pastiche.list.stage.BeforeSpacerState',
|
||
'ephox.pastiche.list.stage.CloseSpansState',
|
||
'ephox.pastiche.list.stage.FindListTypeState',
|
||
'ephox.pastiche.list.stage.ItemContentState',
|
||
'ephox.pastiche.list.stage.ListStartState',
|
||
'ephox.pastiche.list.stage.NoListState',
|
||
'ephox.pastiche.list.stage.SkipEmptyParaState',
|
||
'ephox.pastiche.list.stage.SpacerState',
|
||
'ephox.pastiche.list.state.ListState',
|
||
'ephox.pastiche.list.state.Transitions'
|
||
],
|
||
|
||
function (Filter, ListTypes, AfterListState, AfterNoBulletListState, BeforeSpacerState, CloseSpansState, FindListTypeState, ItemContentState, ListStartState, NoListState, SkipEmptyParaState, SpacerState, ListState, Transitions) {
|
||
var unexpectedToken = function(api, token) {
|
||
var info = 'Type: ' + token.type() + ', Tag: ' + token.tag() + ' Text: ' + token.text();
|
||
console.log('Unexpected token in list conversion: ' + info, activeState.nextFilter.get());
|
||
api.rollback();
|
||
};
|
||
|
||
var noListState = NoListState.run(function () {
|
||
return listStartState;
|
||
});
|
||
var skipEmptyParaState = SkipEmptyParaState.run(noListState, function () {
|
||
return afterListState;
|
||
});
|
||
var afterListState = AfterListState.run(skipEmptyParaState, noListState);
|
||
var itemContentState = ItemContentState.run(afterListState);
|
||
var closeSpansState = CloseSpansState.run(itemContentState, unexpectedToken);
|
||
var spacerState = SpacerState.run(closeSpansState);
|
||
var beforeSpacerState = BeforeSpacerState.run(spacerState, closeSpansState, unexpectedToken);
|
||
var findListTypeState = FindListTypeState.run(beforeSpacerState, unexpectedToken);
|
||
var afterNoBulletListState = AfterNoBulletListState.run(afterListState);
|
||
var listStartState = ListStartState.run(findListTypeState, afterNoBulletListState, unexpectedToken);
|
||
|
||
var activeState = ListState(noListState);
|
||
|
||
var receive = function(api, token) {
|
||
if (activeState.styles && activeState.styles.check(token)) {
|
||
return;
|
||
}
|
||
|
||
activeState.symbolFont.set(ListTypes.checkFont(token, activeState.symbolFont.get()));
|
||
Transitions.go(api, activeState, token);
|
||
};
|
||
|
||
return Filter.createFilter(receive, activeState.reset, 'lists');
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.process.Strategies',
|
||
|
||
[
|
||
'ephox.pastiche.api.HtmlPatterns',
|
||
'ephox.pastiche.api.HybridAction',
|
||
'ephox.pastiche.api.RuleConditions',
|
||
'ephox.pastiche.api.RuleMutations',
|
||
'ephox.pastiche.filter.Lists',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.violin.StringMatch'
|
||
],
|
||
|
||
function (HtmlPatterns, HybridAction, RuleConditions, RuleMutations, Lists, Fun, Class, Css, StringMatch) {
|
||
// This will UNWRAP! tags such as <O:> and <U1:>
|
||
// Context: Word copy and paste.
|
||
var unwrapWordTags = HybridAction.unwrapper({
|
||
tags: [
|
||
{ name: StringMatch.pattern(/^([OVWXP]|U[0-9]+|ST[0-9]+):/i) }
|
||
]
|
||
});
|
||
|
||
// This will try and turn p tags into ol,ul and li tags where appropriate
|
||
// Context: Word copy and paste.
|
||
var parseLists = HybridAction.pipeline([ Lists ]);
|
||
|
||
// This will remove attributes matching: v:.., href with #_toc|#_mso, xmlns:, align
|
||
// and type on lists.
|
||
// Context: Word copy and paste.
|
||
var removeWordAttributes = HybridAction.removal({
|
||
attributes: [
|
||
{ name: StringMatch.pattern(/^v:/) },
|
||
{ name: StringMatch.exact('href'), value: StringMatch.contains('#_toc') },
|
||
{ name: StringMatch.exact('href'), value: StringMatch.contains('#_mso') },
|
||
{ name: StringMatch.pattern(/^xmlns(:|$)/) },
|
||
{ name: StringMatch.exact('align') },
|
||
{ name: StringMatch.exact('type'), condition: RuleConditions.isList }
|
||
]
|
||
});
|
||
|
||
// This will remove script,meta,link,empty-style tags and on,id,name,lang attributes
|
||
// or styles containing OLE_LINK
|
||
// Context: All
|
||
var removeExcess = HybridAction.removal({
|
||
tags: [
|
||
{ name: StringMatch.exact('script') },
|
||
{ name: StringMatch.exact('meta') },
|
||
{ name: StringMatch.exact('link') },
|
||
{ name: StringMatch.exact('style'), condition: RuleConditions.isEmpty }
|
||
],
|
||
attributes: [
|
||
{ name: StringMatch.starts('on') },
|
||
{ name: StringMatch.exact('"') },
|
||
{ name: StringMatch.exact('id') },
|
||
{ name: StringMatch.exact('name') },
|
||
{ name: StringMatch.exact('lang') },
|
||
{ name: StringMatch.exact('language') }
|
||
// INVESTIGATE: Should language go here as well ?
|
||
],
|
||
styles: [
|
||
{ name: StringMatch.all(), value: StringMatch.pattern(/OLE_LINK/i) }
|
||
]
|
||
});
|
||
|
||
var isNotTransformed = function (element) {
|
||
return !Class.has(element, 'ephox-limbo-transform');
|
||
};
|
||
|
||
// This will remove any styles that are not list-style-type for all tags, and keep the width and height
|
||
// styles only for images.
|
||
// Context: Clean copy and paste.
|
||
var cleanStyles = HybridAction.removal({
|
||
styles: [
|
||
{
|
||
name: StringMatch.not(
|
||
StringMatch.pattern(/width|height|list-style-type/)
|
||
),
|
||
condition: isNotTransformed
|
||
},
|
||
{ name: StringMatch.pattern(/width|height/), condition: RuleConditions.isNotImage }
|
||
]
|
||
});
|
||
|
||
// This will remove all classes that are not 'rtf-data-image'
|
||
// Context: Clean copy and paste.
|
||
var cleanClasses = HybridAction.removal({
|
||
classes: [
|
||
{
|
||
name: StringMatch.not(
|
||
StringMatch.exact('rtf-data-image')
|
||
)
|
||
}
|
||
]
|
||
});
|
||
|
||
// This will remove all styles that are not considered valid.
|
||
// Context: Merge copy and paste.
|
||
var mergeStyles = HybridAction.removal({
|
||
styles: [
|
||
{ name: StringMatch.pattern(HtmlPatterns.validStyles()) }
|
||
]
|
||
});
|
||
|
||
// This will remove all classes that have mso in them.
|
||
// Context: Merge copy and paste.
|
||
var mergeClasses = HybridAction.removal({
|
||
classes: [
|
||
{ name: StringMatch.pattern(/mso/i) }
|
||
]
|
||
});
|
||
|
||
// This will remove (unwrap) all images that have a file protocol
|
||
// Context: Copy and paste with images.
|
||
var removeLocalImages = HybridAction.unwrapper({
|
||
tags: [
|
||
{ name: StringMatch.exact('img'), condition: RuleConditions.isLocal },
|
||
// OLE_LINK exact part.
|
||
{ name: StringMatch.exact('a'), condition: RuleConditions.hasNoAttributes }
|
||
]
|
||
});
|
||
|
||
// This will unwrap any <a> tags that have no attributes (i.e. are essentially useless)
|
||
// Context: Any
|
||
var removeVacantLinks = HybridAction.unwrapper({
|
||
tags: [
|
||
{ name: StringMatch.exact('a'), condition: RuleConditions.hasNoAttributes }
|
||
]
|
||
});
|
||
|
||
// This will remove any style attribute that has no content.
|
||
// Context: Any
|
||
var removeEmptyStyle = HybridAction.removal({
|
||
attributes: [
|
||
{ name: StringMatch.exact('style'), value: StringMatch.exact(''), debug: true }
|
||
]
|
||
});
|
||
|
||
// This will remove any style attribute that has no content.
|
||
// Context: Any
|
||
var removeEmptyClass = HybridAction.removal({
|
||
attributes: [
|
||
{ name: StringMatch.exact('class'), value: StringMatch.exact(''), debug: true }
|
||
]
|
||
});
|
||
|
||
// This will remove any inline elements that no longer serve a purpose:
|
||
// Fancy inline elements: contain no content
|
||
// Span inline elements: have no attributes
|
||
// Context: Any
|
||
var pruneInlineTags = HybridAction.unwrapper({
|
||
tags: [
|
||
{
|
||
name: StringMatch.pattern(HtmlPatterns.specialInline()),
|
||
condition: Fun.not(RuleConditions.hasContent)
|
||
}
|
||
]
|
||
});
|
||
|
||
var addPlaceholders = HybridAction.transformer({
|
||
tags: [
|
||
{ name: StringMatch.exact('p'), mutate: RuleMutations.addBrTag }
|
||
]
|
||
});
|
||
|
||
var toUnderline = function (element) {
|
||
var span = RuleMutations.changeTag('span', element);
|
||
Class.add(span, 'ephox-limbo-transform');
|
||
Css.set(span, 'text-decoration', 'underline');
|
||
};
|
||
|
||
var nestedListFixes = HybridAction.transformer({
|
||
tags: [
|
||
{ name: StringMatch.pattern(/ol|ul/), mutate: RuleMutations.properlyNest }
|
||
]
|
||
});
|
||
|
||
var inlineTagFixes = HybridAction.transformer({
|
||
tags: [
|
||
{ name: StringMatch.exact('b'), mutate: Fun.curry(RuleMutations.changeTag, 'strong') },
|
||
{ name: StringMatch.exact('i'), mutate: Fun.curry(RuleMutations.changeTag, 'em') },
|
||
{ name: StringMatch.exact('u'), mutate: toUnderline },
|
||
{ name: StringMatch.exact('s'), mutate: Fun.curry(RuleMutations.changeTag, 'strike') },
|
||
{ name: StringMatch.exact('font'), mutate: RuleMutations.fontToSpan, debug: true }
|
||
]
|
||
});
|
||
|
||
// This will remove all classes that were put in to preserve transformations.
|
||
// Context: Any
|
||
var cleanupFlags = HybridAction.removal({
|
||
classes: [
|
||
{ name: StringMatch.exact('ephox-limbo-transform') }
|
||
]
|
||
});
|
||
|
||
// This will remove any href attributes of a tags that are local.
|
||
// Context: Any
|
||
var removeLocalLinks = HybridAction.removal({
|
||
attributes: [
|
||
{ name: StringMatch.exact('href'), value: StringMatch.starts('file:///'), debug: true }
|
||
]
|
||
});
|
||
|
||
return {
|
||
unwrapWordTags: unwrapWordTags,
|
||
removeWordAttributes: removeWordAttributes,
|
||
parseLists: parseLists,
|
||
removeExcess: removeExcess,
|
||
cleanStyles: cleanStyles,
|
||
cleanClasses: cleanClasses,
|
||
mergeStyles: mergeStyles,
|
||
mergeClasses: mergeClasses,
|
||
removeLocalImages: removeLocalImages,
|
||
removeVacantLinks: removeVacantLinks,
|
||
removeEmptyStyle: removeEmptyStyle,
|
||
removeEmptyClass: removeEmptyClass,
|
||
pruneInlineTags: pruneInlineTags,
|
||
addPlaceholders: addPlaceholders,
|
||
nestedListFixes: nestedListFixes,
|
||
inlineTagFixes: inlineTagFixes,
|
||
cleanupFlags: cleanupFlags,
|
||
removeLocalLinks: removeLocalLinks,
|
||
none: Fun.noop
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.process.PasteFilters',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.limbo.api.RtfImage',
|
||
'ephox.limbo.process.Strategies',
|
||
'ephox.pastiche.api.HybridAction',
|
||
'ephox.pastiche.engine.Filter',
|
||
'ephox.pastiche.engine.Token',
|
||
'ephox.sugar.api.Element'
|
||
],
|
||
|
||
function (Arr, RtfImage, Strategies, HybridAction, Filter, Token, Element) {
|
||
var isIE11 = function (platform) {
|
||
return platform.browser.isIE() && platform.browser.version.major >= 11;
|
||
};
|
||
|
||
var transform = function (f) {
|
||
// TODO: Move this to API in pastiche or change how this works.
|
||
// I think the reason I want to keep it in the tokenizer is because
|
||
// it needs to access comments.
|
||
return Filter.createFilter(function (api, token) {
|
||
var next = f(Element.fromDom(token.getNode())).fold(function () {
|
||
return token;
|
||
}, function (sugared) {
|
||
var node = sugared.dom();
|
||
return Token.token(node, token.type() === Token.END_ELEMENT_TYPE, {});
|
||
});
|
||
api.emit(next);
|
||
});
|
||
};
|
||
|
||
var images = function (isWord, merging, platform) {
|
||
var searcher = platform.browser.isFirefox() ? RtfImage.local : RtfImage.vshape;
|
||
var transformer = isIE11(platform) ? Strategies.none : HybridAction.pipeline([ transform(searcher) ]);
|
||
var local = searcher === RtfImage.local ? Strategies.none : Strategies.removeLocalImages;
|
||
var annotate = isWord ? transformer : Strategies.none;
|
||
|
||
return {
|
||
annotate: [ annotate ],
|
||
local: [ local ]
|
||
};
|
||
};
|
||
|
||
var styling = function (isWord, merging, platform) {
|
||
var merge = [ Strategies.mergeStyles, Strategies.mergeClasses ];
|
||
var clean = [ Strategies.cleanStyles, Strategies.cleanClasses ];
|
||
return merging ? merge : clean;
|
||
};
|
||
|
||
var word = function (isWord, merging, platform) {
|
||
if (!isWord) return Strategies.none;
|
||
var base = [ Strategies.unwrapWordTags ];
|
||
var lists = isIE11(platform) ? [] : Strategies.parseLists;
|
||
return base.concat(lists);
|
||
};
|
||
|
||
var derive = function (isWord, merging, platform) {
|
||
var imageFilters = images(isWord, merging, platform);
|
||
|
||
return Arr.flatten([
|
||
imageFilters.annotate,
|
||
[ Strategies.inlineTagFixes ],
|
||
word(isWord, merging, platform),
|
||
[ Strategies.nestedListFixes ],
|
||
[ Strategies.removeExcess ],
|
||
imageFilters.local,
|
||
styling(isWord, merging, platform),
|
||
[ Strategies.removeLocalLinks, Strategies.removeVacantLinks ],
|
||
[ Strategies.removeEmptyStyle ],
|
||
[ Strategies.removeEmptyClass ],
|
||
[ Strategies.pruneInlineTags ],
|
||
[ Strategies.addPlaceholders ],
|
||
[ Strategies.cleanupFlags ]
|
||
]);
|
||
};
|
||
|
||
return {
|
||
derive: derive
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.boss.common.TagBoundaries',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
// TODO: We need to consolidate this list. I think when we get rid of boss/universe, we can do it then.
|
||
return [
|
||
'body',
|
||
'p',
|
||
'div',
|
||
'article',
|
||
'aside',
|
||
'figcaption',
|
||
'figure',
|
||
'footer',
|
||
'header',
|
||
'nav',
|
||
'section',
|
||
'ol',
|
||
'ul',
|
||
'li',
|
||
'table',
|
||
'thead',
|
||
'tbody',
|
||
'caption',
|
||
'tr',
|
||
'td',
|
||
'th',
|
||
'h1',
|
||
'h2',
|
||
'h3',
|
||
'h4',
|
||
'h5',
|
||
'h6',
|
||
'blockquote',
|
||
'pre',
|
||
'address'
|
||
];
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.Text',
|
||
|
||
[
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.impl.NodeValue'
|
||
],
|
||
|
||
function (Node, NodeValue) {
|
||
var api = NodeValue(Node.isText, 'text');
|
||
|
||
var get = function (element) {
|
||
return api.get(element);
|
||
};
|
||
|
||
var getOption = function (element) {
|
||
return api.getOption(element);
|
||
};
|
||
|
||
var set = function (element, value) {
|
||
api.set(element, value);
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
getOption: getOption,
|
||
set: set
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.boss.api.DomUniverse',
|
||
|
||
[
|
||
'ephox.boss.common.TagBoundaries',
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Compare',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.PredicateFilter',
|
||
'ephox.sugar.api.PredicateFind',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFilter',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'ephox.sugar.api.Text',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (TagBoundaries, Arr, Fun, Attr, Compare, Css, Element, Insert, InsertAll, Node, PredicateFilter, PredicateFind, Remove, SelectorFilter, SelectorFind, Text, Traverse) {
|
||
return function () {
|
||
var clone = function (element) {
|
||
return Element.fromDom(element.dom().cloneNode(false));
|
||
};
|
||
|
||
var isBoundary = function (element) {
|
||
if (!Node.isElement(element)) return false;
|
||
if (Node.name(element) === 'body') return true;
|
||
var display = Css.get(element, 'display');
|
||
// When the read display value is empty, we need to check the node name.
|
||
return display !== undefined && display.length > 0 ?
|
||
Arr.contains(['block', 'table-cell', 'table-row', 'table', 'list-item'], display) :
|
||
Arr.contains(TagBoundaries, Node.name(element));
|
||
};
|
||
|
||
var isEmptyTag = function (element) {
|
||
if (!Node.isElement(element)) return false;
|
||
return Arr.contains(['br', 'img', 'hr'], Node.name(element));
|
||
};
|
||
|
||
var comparePosition = function (element, other) {
|
||
return element.dom().compareDocumentPosition(other.dom());
|
||
};
|
||
|
||
var copyAttributesTo = function (source, destination) {
|
||
var as = Attr.clone(source);
|
||
Attr.setAll(destination, as);
|
||
};
|
||
|
||
return {
|
||
up: Fun.constant({
|
||
selector: SelectorFind.ancestor,
|
||
closest: SelectorFind.closest,
|
||
predicate: PredicateFind.ancestor,
|
||
all: Traverse.parents
|
||
}),
|
||
down: Fun.constant({
|
||
selector: SelectorFilter.descendants,
|
||
predicate: PredicateFilter.descendants
|
||
}),
|
||
styles: Fun.constant({
|
||
get: Css.get,
|
||
set: Css.set,
|
||
remove: Css.remove
|
||
}),
|
||
attrs: Fun.constant({
|
||
get: Attr.get,
|
||
set: Attr.set,
|
||
remove: Attr.remove,
|
||
copyTo: copyAttributesTo
|
||
}),
|
||
insert: Fun.constant({
|
||
before: Insert.before,
|
||
after: Insert.after,
|
||
afterAll: InsertAll.after,
|
||
append: Insert.append,
|
||
appendAll: InsertAll.append,
|
||
prepend: Insert.prepend,
|
||
wrap: Insert.wrap
|
||
}),
|
||
remove: Fun.constant({
|
||
unwrap: Remove.unwrap,
|
||
remove: Remove.remove
|
||
}),
|
||
create: Fun.constant({
|
||
nu: Element.fromTag,
|
||
clone: clone,
|
||
text: Element.fromText
|
||
}),
|
||
query: Fun.constant({
|
||
comparePosition: comparePosition,
|
||
prevSibling: Traverse.prevSibling,
|
||
nextSibling: Traverse.nextSibling
|
||
}),
|
||
property: Fun.constant({
|
||
children: Traverse.children,
|
||
name: Node.name,
|
||
parent: Traverse.parent,
|
||
isText: Node.isText,
|
||
isElement: Node.isElement,
|
||
getText: Text.get,
|
||
setText: Text.set,
|
||
isBoundary: isBoundary,
|
||
isEmptyTag: isEmptyTag
|
||
}),
|
||
eq: Compare.eq,
|
||
is: Compare.is
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.data.NamedPattern',
|
||
|
||
[
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Struct) {
|
||
return Struct.immutable('word', 'pattern');
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.data.Spot',
|
||
|
||
[
|
||
'ephox.scullion.Struct'
|
||
],
|
||
|
||
function (Struct) {
|
||
var point = Struct.immutable('element', 'offset');
|
||
var delta = Struct.immutable('element', 'deltaOffset');
|
||
var range = Struct.immutable('element', 'start', 'finish');
|
||
var points = Struct.immutable('begin', 'end');
|
||
var text = Struct.immutable('element', 'text');
|
||
|
||
return {
|
||
point: point,
|
||
delta: delta,
|
||
range: range,
|
||
points: points,
|
||
text: text
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.extract.TypedItem',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
/**
|
||
* Church encoded ADT representing whether an element is:
|
||
* - boundary (block tag or inline tag with block CSS display)
|
||
* - empty
|
||
* - text
|
||
*/
|
||
function (Fun, Option) {
|
||
var no = Fun.constant(false);
|
||
var yes = Fun.constant(true);
|
||
|
||
var boundary = function (item, universe) {
|
||
return folder(function (b, e, t) {
|
||
return b(item, universe);
|
||
});
|
||
};
|
||
|
||
var empty = function (item, universe) {
|
||
return folder(function (b, e, t) {
|
||
return e(item, universe);
|
||
});
|
||
};
|
||
|
||
var text = function (item, universe) {
|
||
return folder(function (b, e, t) {
|
||
return t(item, universe);
|
||
});
|
||
};
|
||
|
||
var folder = function (fold) {
|
||
var isBoundary = function () {
|
||
return fold(yes, no, no);
|
||
};
|
||
|
||
var toText = function () {
|
||
return fold(Option.none, Option.none, function (item, universe) {
|
||
return Option.some(item);
|
||
});
|
||
};
|
||
|
||
var is = function (other) {
|
||
return fold(no, no, function (item, universe) {
|
||
return universe.eq(item, other);
|
||
});
|
||
};
|
||
|
||
var len = function () {
|
||
return fold(Fun.constant(0), Fun.constant(1), function (item, universe) {
|
||
return universe.property().getText(item).length;
|
||
});
|
||
};
|
||
|
||
return {
|
||
isBoundary: isBoundary,
|
||
fold: fold,
|
||
toText: toText,
|
||
is: is,
|
||
len: len
|
||
};
|
||
};
|
||
|
||
return {
|
||
text: text,
|
||
boundary: boundary,
|
||
empty: empty
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.array.Boundaries',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Fun) {
|
||
var boundAt = function (xs, left, right, comparator) {
|
||
var leftIndex = Arr.findIndex(xs, Fun.curry(comparator, left));
|
||
var first = leftIndex > -1 ? leftIndex : 0;
|
||
var rightIndex = Arr.findIndex(xs, Fun.curry(comparator, right));
|
||
var last = rightIndex > -1 ? rightIndex + 1 : xs.length;
|
||
return xs.slice(first, last);
|
||
};
|
||
|
||
return {
|
||
boundAt: boundAt
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.array.Slice',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
/**
|
||
* Slice an array at the first item matched by the predicate
|
||
*/
|
||
var sliceby = function (list, pred) {
|
||
var index = Arr.findIndex(list, pred);
|
||
return list.slice(0, index);
|
||
};
|
||
|
||
return {
|
||
sliceby: sliceby
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.array.Split',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
/**
|
||
* Split an array into chunks matched by the predicate
|
||
*/
|
||
var splitby = function (xs, pred) {
|
||
var r = [];
|
||
var part = [];
|
||
Arr.each(xs, function (x) {
|
||
if (pred(x)) {
|
||
r.push(part);
|
||
part = [];
|
||
} else {
|
||
part.push(x);
|
||
}
|
||
});
|
||
|
||
if (part.length > 0) r.push(part);
|
||
return r;
|
||
};
|
||
|
||
return {
|
||
splitby: splitby
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.api.Arrays',
|
||
|
||
[
|
||
'ephox.polaris.array.Boundaries',
|
||
'ephox.polaris.array.Slice',
|
||
'ephox.polaris.array.Split'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Boundaries, Slice, Split) {
|
||
var boundAt = function (xs, left, right, comparator) {
|
||
return Boundaries.boundAt(xs, left, right, comparator);
|
||
};
|
||
|
||
var splitby = function (array, predicate) {
|
||
return Split.splitby(array, predicate);
|
||
};
|
||
|
||
var sliceby = function (array, predicate) {
|
||
return Slice.sliceby(array, predicate);
|
||
};
|
||
|
||
return {
|
||
splitby: splitby,
|
||
sliceby: sliceby,
|
||
boundAt: boundAt
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.extract.TypedList',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.phoenix.api.data.Spot',
|
||
'ephox.polaris.api.Arrays'
|
||
],
|
||
|
||
/**
|
||
* Extracts various information from a list of TypedItem
|
||
*/
|
||
function (Arr, Fun, Option, Spot, Arrays) {
|
||
|
||
var count = function (parray) {
|
||
return Arr.foldr(parray, function (b, a) {
|
||
return a.len() + b;
|
||
}, 0);
|
||
};
|
||
|
||
var dropUntil = function (parray, target) {
|
||
return Arrays.sliceby(parray, function (x) {
|
||
return x.is(target);
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Transform a TypedItem into a range representing that item from the start position.
|
||
*
|
||
* The generation function for making a PositionArray out of a list of TypedItems.
|
||
*/
|
||
var gen = function (unit, start) {
|
||
return unit.fold(Option.none, function (e) {
|
||
return Option.some(Spot.range(e, start, start + 1));
|
||
}, function (t) {
|
||
return Option.some(Spot.range(t, start, start + unit.len()));
|
||
});
|
||
};
|
||
|
||
var justText = function (parray) {
|
||
return Arr.bind(parray, function (x) {
|
||
return x.fold(Fun.constant([]), Fun.constant([]), Fun.identity);
|
||
});
|
||
};
|
||
|
||
return {
|
||
count: count,
|
||
dropUntil: dropUntil,
|
||
gen: gen,
|
||
justText: justText
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.extract.Extract',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.phoenix.api.data.Spot',
|
||
'ephox.phoenix.extract.TypedItem',
|
||
'ephox.phoenix.extract.TypedList'
|
||
],
|
||
|
||
function (Arr, Spot, TypedItem, TypedList) {
|
||
/**
|
||
* Flattens the item tree into TypedItem representations.
|
||
*
|
||
* Boundaries are returned twice, before and after their children.
|
||
*/
|
||
var typed = function (universe, item, optimise) {
|
||
if (universe.property().isText(item)) {
|
||
return [ TypedItem.text(item, universe) ];
|
||
} else if (universe.property().isEmptyTag(item)) {
|
||
return [ TypedItem.empty(item, universe) ];
|
||
} else if (universe.property().isElement(item)) {
|
||
var children = universe.property().children(item);
|
||
var boundary = universe.property().isBoundary(item) ? [TypedItem.boundary(item, universe)] : [];
|
||
var rest = optimise !== undefined && optimise(item) ? [] : Arr.bind(children, function (child) {
|
||
return typed(universe, child, optimise);
|
||
});
|
||
return boundary.concat(rest).concat(boundary);
|
||
} else {
|
||
return [];
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Returns just the actual elements from a call to typed().
|
||
*/
|
||
var items = function (universe, item, optimise) {
|
||
var typedItemList = typed(universe, item, optimise);
|
||
|
||
var raw = function (item, universe) { return item; };
|
||
|
||
return Arr.map(typedItemList, function (typedItem) {
|
||
return typedItem.fold(raw, raw, raw);
|
||
});
|
||
};
|
||
|
||
var extractToElem = function (universe, child, offset, item, optimise) {
|
||
var extractions = typed(universe, item, optimise);
|
||
var prior = TypedList.dropUntil(extractions, child);
|
||
var count = TypedList.count(prior);
|
||
return Spot.point(item, count + offset);
|
||
};
|
||
|
||
/**
|
||
* Generates an absolute point in the child's parent
|
||
* that can be used to reference the offset into child later.
|
||
*
|
||
* To find the exact reference later, use Find.
|
||
*/
|
||
var extract = function (universe, child, offset, optimise) {
|
||
return universe.property().parent(child).fold(function () {
|
||
return Spot.point(child, offset);
|
||
}, function (parent) {
|
||
return extractToElem(universe, child, offset, parent, optimise);
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Generates an absolute point that can be used to reference the offset into child later.
|
||
* This absolute point will be relative to a parent node (specified by predicate).
|
||
*
|
||
* To find the exact reference later, use Find.
|
||
*/
|
||
var extractTo = function (universe, child, offset, pred, optimise) {
|
||
return universe.up().predicate(child, pred).fold(function () {
|
||
return Spot.point(child, offset);
|
||
}, function (v) {
|
||
return extractToElem(universe, child, offset, v, optimise);
|
||
});
|
||
};
|
||
|
||
return {
|
||
typed: typed,
|
||
items: items,
|
||
extractTo: extractTo,
|
||
extract: extract
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.extract.ExtractText',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.phoenix.extract.Extract'
|
||
],
|
||
|
||
function (Arr, Fun, Extract) {
|
||
var newline = '\n';
|
||
var space = ' ';
|
||
|
||
var onEmpty = function (item, universe) {
|
||
return universe.property().name(item) === 'img' ? space : newline;
|
||
};
|
||
|
||
var from = function (universe, item, optimise) {
|
||
var typed = Extract.typed(universe, item, optimise);
|
||
return Arr.map(typed, function (t) {
|
||
return t.fold(Fun.constant(newline), onEmpty, universe.property().getText);
|
||
}).join('');
|
||
};
|
||
|
||
return {
|
||
from: from
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.parray.Generator',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Fun) {
|
||
|
||
/**
|
||
* Generate a PositionArray
|
||
*
|
||
* xs: list of thing
|
||
* f: thing -> Optional unit
|
||
* _start: sets the start position to search at
|
||
*/
|
||
var make = function (xs, f, _start) {
|
||
|
||
var init = {
|
||
len: _start !== undefined ? _start : 0,
|
||
list: []
|
||
};
|
||
|
||
var r = Arr.foldl(xs, function (b, a) {
|
||
var value = f(a, b.len);
|
||
return value.fold(Fun.constant(b), function (v) {
|
||
return {
|
||
len: v.finish(),
|
||
list: b.list.concat([v])
|
||
};
|
||
});
|
||
}, init);
|
||
|
||
return r.list;
|
||
};
|
||
|
||
return {
|
||
make: make
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.parray.Query',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Arr, Option) {
|
||
|
||
/**
|
||
* Simple "is position within unit" utility function
|
||
*/
|
||
var inUnit = function (unit, position) {
|
||
return position >= unit.start() && position <= unit.finish();
|
||
};
|
||
|
||
/**
|
||
* Finds the unit in the PositionArray that contains this offset (if there is one)
|
||
*/
|
||
var get = function (parray, offset) {
|
||
var unit = Arr.find(parray, function (x) {
|
||
return inUnit(x, offset);
|
||
});
|
||
|
||
return Option.from(unit);
|
||
};
|
||
|
||
var startindex = function (parray, offset) {
|
||
return Arr.findIndex(parray, function (unit) {
|
||
return unit.start() === offset;
|
||
});
|
||
};
|
||
|
||
var tryend = function (parray, finish) {
|
||
var finishes = parray[parray.length - 1] && parray[parray.length - 1].finish() === finish;
|
||
return finishes ? parray.length + 1 : -1;
|
||
};
|
||
|
||
|
||
/**
|
||
* Extracts the pieces of the PositionArray that are bounded *exactly* on the start and finish offsets
|
||
*/
|
||
var sublist = function (parray, start, finish) {
|
||
var first = startindex(parray, start);
|
||
var rawlast = startindex(parray, finish);
|
||
var last = rawlast > -1 ? rawlast : tryend(parray, finish);
|
||
|
||
return first > -1 && last > -1 ? parray.slice(first, last) : [];
|
||
};
|
||
|
||
var find = function (parray, pred) {
|
||
return Option.from(Arr.find(parray, pred));
|
||
};
|
||
|
||
return {
|
||
get: get,
|
||
find: find,
|
||
inUnit: inUnit,
|
||
sublist: sublist
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.parray.Translate',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.highway.Merger',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Arr, Merger, Fun) {
|
||
/** Adjust a PositionArray positions by an offset */
|
||
var translate = function (parray, offset) {
|
||
return Arr.map(parray, function (unit) {
|
||
return Merger.merge(unit, {
|
||
start: Fun.constant(unit.start() + offset),
|
||
finish: Fun.constant(unit.finish() + offset)
|
||
});
|
||
});
|
||
};
|
||
|
||
return {
|
||
translate: translate
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.parray.Split',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.polaris.parray.Query',
|
||
'ephox.polaris.parray.Translate'
|
||
],
|
||
|
||
function (Arr, Query, Translate) {
|
||
/**
|
||
* After subdivide has split the unit, update the resulting PositionArray based on the unit start position.
|
||
*/
|
||
var divide = function (unit, positions, subdivide) {
|
||
var mini = subdivide(unit, positions);
|
||
return Translate.translate(mini, unit.start());
|
||
};
|
||
|
||
/**
|
||
* Adds extra split points into a PositionArray, using subdivide to split if necessary
|
||
*/
|
||
var splits = function (parray, positions, subdivide) {
|
||
if (positions.length === 0) return parray;
|
||
|
||
return Arr.bind(parray, function (unit) {
|
||
var relevant = Arr.bind(positions, function (pos) {
|
||
return Query.inUnit(unit, pos) ? [ pos - unit.start() ] : [];
|
||
});
|
||
|
||
return relevant.length > 0 ? divide(unit, relevant, subdivide) : [ unit ];
|
||
});
|
||
};
|
||
|
||
return {
|
||
splits: splits
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.api.PositionArray',
|
||
|
||
[
|
||
'ephox.polaris.parray.Generator',
|
||
'ephox.polaris.parray.Query',
|
||
'ephox.polaris.parray.Split',
|
||
'ephox.polaris.parray.Translate'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Generator, Query, Split, Translate) {
|
||
var generate = function (items, generator, _start) {
|
||
return Generator.make(items, generator, _start);
|
||
};
|
||
|
||
var get = function (parray, offset) {
|
||
return Query.get(parray, offset);
|
||
};
|
||
|
||
var find = function (parray, pred) {
|
||
return Query.find(parray, pred);
|
||
};
|
||
|
||
var splits = function (parray, positions, subdivide) {
|
||
return Split.splits(parray, positions, subdivide);
|
||
};
|
||
|
||
var translate = function (parray, amount) {
|
||
return Translate.translate(parray, amount);
|
||
};
|
||
|
||
var sublist = function (parray, start, finish) {
|
||
return Query.sublist(parray, start, finish);
|
||
};
|
||
|
||
return {
|
||
generate: generate,
|
||
get: get,
|
||
find: find,
|
||
splits: splits,
|
||
translate: translate,
|
||
sublist: sublist
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.extract.Find',
|
||
|
||
[
|
||
'ephox.phoenix.api.data.Spot',
|
||
'ephox.phoenix.extract.Extract',
|
||
'ephox.phoenix.extract.TypedList',
|
||
'ephox.polaris.api.PositionArray'
|
||
],
|
||
|
||
function (Spot, Extract, TypedList, PositionArray) {
|
||
|
||
/**
|
||
* Finds an exact reference to a document point generated by Extract
|
||
*/
|
||
var find = function (universe, parent, offset, optimise) {
|
||
var extractions = Extract.typed(universe, parent, optimise);
|
||
|
||
var parray = PositionArray.generate(extractions, TypedList.gen);
|
||
var spot = PositionArray.get(parray, offset);
|
||
return spot.map(function (v) {
|
||
return Spot.point(v.element(), offset - v.start());
|
||
});
|
||
};
|
||
|
||
return {
|
||
find: find
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.general.Extract',
|
||
|
||
[
|
||
'ephox.phoenix.extract.Extract',
|
||
'ephox.phoenix.extract.ExtractText',
|
||
'ephox.phoenix.extract.Find'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Extract, ExtractText, Find) {
|
||
|
||
var from = function (universe, item, optimise) {
|
||
return Extract.typed(universe, item, optimise);
|
||
};
|
||
|
||
var all = function (universe, item, optimise) {
|
||
return Extract.items(universe, item, optimise);
|
||
};
|
||
|
||
var extract = function (universe, child, offset, optimise) {
|
||
return Extract.extract(universe, child, offset, optimise);
|
||
};
|
||
|
||
var extractTo = function (universe, child, offset, pred, optimise) {
|
||
return Extract.extractTo(universe, child, offset, pred, optimise);
|
||
};
|
||
|
||
var find = function (universe, parent, offset, optimise) {
|
||
return Find.find(universe, parent, offset, optimise);
|
||
};
|
||
|
||
var toText = function (universe, item, optimise) {
|
||
return ExtractText.from(universe, item, optimise);
|
||
};
|
||
|
||
return {
|
||
extract: extract,
|
||
extractTo: extractTo,
|
||
all: all,
|
||
from: from,
|
||
find: find,
|
||
toText: toText
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.family.Group',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.phoenix.api.general.Extract',
|
||
'ephox.polaris.api.Arrays'
|
||
],
|
||
|
||
function (Arr, Extract, Arrays) {
|
||
/**
|
||
* Return an array of arrays split by boundaries
|
||
*/
|
||
var group = function (universe, items, optimise) {
|
||
var extractions = Arr.bind(items, function (item) {
|
||
return Extract.from(universe, item, optimise);
|
||
});
|
||
|
||
var segments = Arrays.splitby(extractions, function (item) {
|
||
return item.isBoundary();
|
||
});
|
||
|
||
return Arr.filter(segments, function (x) { return x.length > 0; });
|
||
};
|
||
|
||
return {
|
||
group: group
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.family.Parents',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option'
|
||
],
|
||
|
||
function (Arr, Option) {
|
||
/**
|
||
* Search the parents of both items for a common element
|
||
*/
|
||
var common = function (universe, item1, item2) {
|
||
var item1parents = [item1].concat(universe.up().all(item1));
|
||
var item2parents = [item2].concat(universe.up().all(item2));
|
||
|
||
var r = Arr.find(item1parents, function (x) {
|
||
return Arr.exists(item2parents, function (y) {
|
||
return universe.eq(y, x);
|
||
});
|
||
});
|
||
|
||
return Option.from(r);
|
||
};
|
||
|
||
return {
|
||
common: common
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.wrap.OrphanText',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
// Textnodes cannot be children of these tags
|
||
var textBlacklist = [ 'table', 'tbody', 'thead', 'tfoot', 'tr', 'ul', 'ol' ];
|
||
|
||
return function (universe) {
|
||
var domUtils = universe.property();
|
||
var validateParent = function (node, blacklist) {
|
||
return domUtils.parent(node).map(domUtils.name).map(function (name) {
|
||
return !Arr.contains(blacklist, name);
|
||
}).getOr(false);
|
||
};
|
||
|
||
var validateText = function (textNode) {
|
||
return domUtils.isText(textNode) && validateParent(textNode, textBlacklist);
|
||
};
|
||
|
||
return {
|
||
validateText: validateText
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.family.Range',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.phoenix.api.general.Extract',
|
||
'ephox.phoenix.family.Parents',
|
||
'ephox.phoenix.wrap.OrphanText'
|
||
],
|
||
|
||
function (Arr, Fun, Extract, Parents, OrphanText) {
|
||
var index = function (universe, items, item) {
|
||
return Arr.findIndex(items, Fun.curry(universe.eq, item));
|
||
};
|
||
|
||
var order = function (items, a, delta1, b, delta2) {
|
||
return a < b ? items.slice(a + delta1, b + delta2) : items.slice(b + delta2, a + delta1);
|
||
};
|
||
|
||
/**
|
||
* Returns a flat array of text nodes between two defined nodes.
|
||
*
|
||
* Deltas are a broken concept. They control whether the item passed is included in the result.
|
||
*/
|
||
var range = function (universe, item1, delta1, item2, delta2) {
|
||
if (universe.eq(item1, item2)) return [item1];
|
||
|
||
return Parents.common(universe, item1, item2).fold(function () {
|
||
return []; // no common parent, therefore no intervening path. How does this clash with Path in robin?
|
||
}, function (parent) {
|
||
var items = [ parent ].concat(Extract.all(universe, parent, Fun.constant(false)));
|
||
var start = index(universe, items, item1);
|
||
var finish = index(universe, items, item2);
|
||
var result = start > -1 && finish > -1 ? order(items, start, delta1, finish, delta2) : [];
|
||
var orphanText = OrphanText(universe);
|
||
return Arr.filter(result, orphanText.validateText);
|
||
});
|
||
};
|
||
|
||
return {
|
||
range: range
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.general.Family',
|
||
|
||
[
|
||
'ephox.phoenix.family.Group',
|
||
'ephox.phoenix.family.Range'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Group, Range) {
|
||
var range = function (universe, start, startDelta, finish, finishDelta) {
|
||
return Range.range(universe, start, startDelta, finish, finishDelta);
|
||
};
|
||
|
||
var group = function (universe, items, optimise) {
|
||
return Group.group(universe, items, optimise);
|
||
};
|
||
|
||
return {
|
||
range: range,
|
||
group: group
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.string.Sanitise',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function () {
|
||
/**
|
||
* Sanitises a string for use in a CSS class name
|
||
*/
|
||
var css = function (str) {
|
||
// special case; the first character must a letter. More strict than CSS, but easier to implement.
|
||
var r = /^[a-zA-Z]/.test(str) ? '' : 'e';
|
||
|
||
// any non-word character becomes a hyphen
|
||
var sanitised = str.replace(/[^\w]/gi, '-');
|
||
|
||
return r + sanitised;
|
||
};
|
||
|
||
return {
|
||
css: css
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.string.Split',
|
||
|
||
[
|
||
'ephox.compass.Arr'
|
||
],
|
||
|
||
function (Arr) {
|
||
/**
|
||
* Splits a string into multiple chunks
|
||
*/
|
||
var splits = function (value, indices) {
|
||
if (indices.length === 0) return [value];
|
||
|
||
var divisions = Arr.foldl(indices, function (acc, x) {
|
||
if (x === 0) return acc;
|
||
|
||
var part = value.substring(acc.prev, x);
|
||
return {
|
||
prev: x,
|
||
values: acc.values.concat([part])
|
||
};
|
||
}, { prev: 0, values: [] });
|
||
|
||
var lastPoint = indices[indices.length - 1];
|
||
return lastPoint < value.length ? divisions.values.concat(value.substring(lastPoint)) : divisions.values;
|
||
};
|
||
|
||
return {
|
||
splits: splits
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.api.Strings',
|
||
|
||
[
|
||
'ephox.polaris.string.Sanitise',
|
||
'ephox.polaris.string.Split'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Sanitise, Split) {
|
||
var splits = function (text, points) {
|
||
return Split.splits(text, points);
|
||
};
|
||
|
||
var cssSanitise = function (str) {
|
||
return Sanitise.css(str);
|
||
};
|
||
|
||
return {
|
||
cssSanitise: cssSanitise,
|
||
splits: splits
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.search.Splitter',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option',
|
||
'ephox.phoenix.api.data.Spot',
|
||
'ephox.polaris.api.PositionArray',
|
||
'ephox.polaris.api.Strings'
|
||
],
|
||
|
||
function (Arr, Option, Spot, PositionArray, Strings) {
|
||
|
||
/**
|
||
* Re-generates an item's text nodes, split as defined by the positions array.
|
||
*
|
||
* Returns a PositionArray of the result.
|
||
*/
|
||
var subdivide = function (universe, item, positions) {
|
||
var text = universe.property().getText(item);
|
||
var pieces = Arr.filter(Strings.splits(text, positions), function (section) {
|
||
return section.length > 0;
|
||
});
|
||
|
||
if (pieces.length <= 1) return [ Spot.range(item, 0, text.length) ];
|
||
universe.property().setText(item, pieces[0]);
|
||
|
||
var others = PositionArray.generate(pieces.slice(1), function (a, start) {
|
||
var nu = universe.create().text(a);
|
||
var result = Spot.range(nu, start, start + a.length);
|
||
return Option.some(result);
|
||
}, pieces[0].length);
|
||
|
||
var otherElements = Arr.map(others, function (a) { return a.element(); });
|
||
universe.insert().afterAll(item, otherElements);
|
||
|
||
return [ Spot.range(item, 0, pieces[0].length) ].concat(others);
|
||
};
|
||
|
||
return {
|
||
subdivide: subdivide
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.search.MatchSplitter',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.phoenix.search.Splitter',
|
||
'ephox.polaris.api.PositionArray'
|
||
],
|
||
|
||
function (Arr, Fun, Splitter, PositionArray) {
|
||
/**
|
||
* Split each text node in the list using the match endpoints.
|
||
*
|
||
* Each match is then mapped to the word it matched and the elements that make up the word.
|
||
*/
|
||
var separate = function (universe, list, matches) {
|
||
var allPositions = Arr.bind(matches, function (match) {
|
||
return [ match.start(), match.finish() ];
|
||
});
|
||
|
||
var subdivide = function (unit, positions) {
|
||
return Splitter.subdivide(universe, unit.element(), positions);
|
||
};
|
||
|
||
var structure = PositionArray.splits(list, allPositions, subdivide);
|
||
|
||
var collate = function (match) {
|
||
var sub = PositionArray.sublist(structure, match.start(), match.finish());
|
||
|
||
var elements = Arr.map(sub, function (unit) { return unit.element(); });
|
||
|
||
var exact = Arr.map(elements, universe.property().getText).join('');
|
||
return {
|
||
elements: Fun.constant(elements),
|
||
word: match.word,
|
||
exact: Fun.constant(exact)
|
||
};
|
||
};
|
||
|
||
return Arr.map(matches, collate);
|
||
};
|
||
|
||
return {
|
||
separate: separate
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.bud.Unicode',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
// \u200B needs to be removed manually as it is not considered whitespace when trimming
|
||
// \uFEFF does not need to be removed manually. It is considered whitespace when trimming
|
||
var zeroWidth = function () {
|
||
return '\u200B';
|
||
};
|
||
|
||
// Note, we are separating these out so that we are accounting for the subtle differences
|
||
// between them. Eventually, we'll want to combine them again.
|
||
var trimNative = function (str) {
|
||
return str.replace(/\u200B/, '').trim();
|
||
};
|
||
|
||
var trimWithRegex = function (str) {
|
||
return str.replace(/^\s+|\s+$/g, '').replace(/\u200B/, '');
|
||
};
|
||
|
||
return {
|
||
zeroWidth: zeroWidth,
|
||
trimNative: trimNative,
|
||
trimWithRegex: trimWithRegex
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.pattern.Chars',
|
||
|
||
[
|
||
'ephox.bud.Unicode',
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Unicode, Fun) {
|
||
// \w is a word character
|
||
// \' is an apostrophe
|
||
// '-' is a hyphen
|
||
// \u00C0 - \u00FF are various language characters
|
||
// \u2018 and \u2019 are the smart quote characters
|
||
var chars = '\\w' + '\'' + '\\-' + '\\u00C0-\\u00FF' + Unicode.zeroWidth() + '\\u2018\\u2019';
|
||
var wordbreak = '[^' + chars + ']';
|
||
var wordchar = '[' + chars + ']';
|
||
|
||
return {
|
||
chars: Fun.constant(chars),
|
||
wordbreak: Fun.constant(wordbreak),
|
||
wordchar: Fun.constant(wordchar)
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.pattern.Custom',
|
||
|
||
[
|
||
'global!RegExp'
|
||
],
|
||
|
||
function (RegExp) {
|
||
return function (regex, prefix, suffix, flags) {
|
||
var term = function () {
|
||
return new RegExp(regex, flags.getOr('g'));
|
||
};
|
||
|
||
return {
|
||
term: term,
|
||
prefix: prefix,
|
||
suffix: suffix
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.pattern.Unsafe',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.polaris.pattern.Chars',
|
||
'ephox.polaris.pattern.Custom'
|
||
],
|
||
|
||
function (Fun, Option, Chars, Custom) {
|
||
|
||
/**
|
||
* Tokens have no prefix or suffix
|
||
*/
|
||
var token = function (input) {
|
||
return Custom(input, Fun.constant(0), Fun.constant(0), Option.none());
|
||
};
|
||
|
||
/**
|
||
* Words have complex rules as to what a "word break" actually is.
|
||
*
|
||
* These are consumed by the regex and then excluded by prefix/suffix lengths.
|
||
*/
|
||
var word = function (input) {
|
||
var regex = '((?:^\'?)|(?:' + Chars.wordbreak() + '+\'?))' + input + '((?:\'?$)|(?:\'?' + Chars.wordbreak() + '+))';
|
||
|
||
// ASSUMPTION: There are no groups in their input
|
||
var prefix = function (match) {
|
||
return match.length > 1 ? match[1].length : 0;
|
||
};
|
||
|
||
var suffix = function (match) {
|
||
return match.length > 2 ? match[2].length : 0;
|
||
};
|
||
|
||
return Custom(regex, prefix, suffix, Option.none());
|
||
};
|
||
|
||
return {
|
||
token: token,
|
||
word: word
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.pattern.Safe',
|
||
|
||
[
|
||
'ephox.polaris.pattern.Unsafe'
|
||
],
|
||
|
||
/** Sanitises all inputs to Unsafe */
|
||
function (Unsafe) {
|
||
/** Escapes regex characters in a string */
|
||
var sanitise = function (input) {
|
||
return input.replace(/[-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
||
};
|
||
|
||
var word = function (input) {
|
||
var value = sanitise(input);
|
||
return Unsafe.word(value);
|
||
};
|
||
|
||
var token = function (input) {
|
||
var value = sanitise(input);
|
||
return Unsafe.token(value);
|
||
};
|
||
|
||
return {
|
||
sanitise: sanitise,
|
||
word: word,
|
||
token: token
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.api.Pattern',
|
||
|
||
[
|
||
'ephox.polaris.pattern.Chars',
|
||
'ephox.polaris.pattern.Custom',
|
||
'ephox.polaris.pattern.Safe',
|
||
'ephox.polaris.pattern.Unsafe'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Chars, Custom, Safe, Unsafe) {
|
||
var safeword = function (input) {
|
||
return Safe.word(input);
|
||
};
|
||
|
||
var safetoken = function (input) {
|
||
return Safe.token(input);
|
||
};
|
||
|
||
var custom = function (input, prefix, suffix, flags) {
|
||
return Custom(input, prefix, suffix, flags);
|
||
};
|
||
|
||
var unsafeword = function (input) {
|
||
return Unsafe.word(input);
|
||
};
|
||
|
||
var unsafetoken = function (input) {
|
||
return Unsafe.token(input);
|
||
};
|
||
|
||
var sanitise = function (input) {
|
||
return Safe.sanitise(input);
|
||
};
|
||
|
||
var chars = function () {
|
||
return Chars.chars();
|
||
};
|
||
|
||
var wordbreak = function () {
|
||
return Chars.wordbreak();
|
||
};
|
||
|
||
var wordchar = function () {
|
||
return Chars.wordchar();
|
||
};
|
||
|
||
return {
|
||
safeword: safeword,
|
||
safetoken: safetoken,
|
||
custom: custom,
|
||
unsafeword: unsafeword,
|
||
unsafetoken: unsafetoken,
|
||
sanitise: sanitise,
|
||
chars: chars,
|
||
wordbreak: wordbreak,
|
||
wordchar: wordchar
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.search.Find',
|
||
|
||
[
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Fun) {
|
||
|
||
/**
|
||
* Returns the offset pairs of all matches of pattern on the input string, adjusting for prefix and suffix offsets
|
||
*/
|
||
var all = function (input, pattern) {
|
||
var term = pattern.term();
|
||
var r = [];
|
||
var match = term.exec(input);
|
||
while (match) {
|
||
var start = match.index + pattern.prefix(match);
|
||
var length = match[0].length - pattern.prefix(match) - pattern.suffix(match);
|
||
r.push({
|
||
start: Fun.constant(start),
|
||
finish: Fun.constant(start + length)
|
||
});
|
||
term.lastIndex = start + length;
|
||
match = term.exec(input);
|
||
}
|
||
return r;
|
||
};
|
||
|
||
return {
|
||
all: all
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.search.Sleuth',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.highway.Merger',
|
||
'ephox.polaris.search.Find',
|
||
'global!Array'
|
||
],
|
||
|
||
function (Arr, Merger, Find, Array) {
|
||
var sort = function (array) {
|
||
var r = Array.prototype.slice.call(array, 0);
|
||
r.sort(function (a, b) {
|
||
if (a.start() < b.start()) return -1;
|
||
else if (b.start() < a.start()) return 1;
|
||
else return 0;
|
||
});
|
||
return r;
|
||
};
|
||
|
||
/**
|
||
* For each target (pattern, ....), find the matching text (if there is any) and record the start and end offsets.
|
||
*
|
||
* Then sort the result by start point.
|
||
*/
|
||
var search = function (text, targets) {
|
||
var unsorted = Arr.bind(targets, function (t) {
|
||
var results = Find.all(text, t.pattern());
|
||
return Arr.map(results, function (r) {
|
||
return Merger.merge(t, {
|
||
start: r.start,
|
||
finish: r.finish
|
||
});
|
||
});
|
||
});
|
||
|
||
return sort(unsorted);
|
||
};
|
||
|
||
return {
|
||
search: search
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.polaris.api.Search',
|
||
|
||
[
|
||
'ephox.polaris.search.Find',
|
||
'ephox.polaris.search.Sleuth'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Find, Sleuth) {
|
||
var findall = function (input, pattern) {
|
||
return Find.all(input, pattern);
|
||
};
|
||
|
||
var findmany = function (input, targets) {
|
||
return Sleuth.search(input, targets);
|
||
};
|
||
|
||
return {
|
||
findall: findall,
|
||
findmany: findmany
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.search.Searcher',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option',
|
||
'ephox.phoenix.api.data.NamedPattern',
|
||
'ephox.phoenix.api.data.Spot',
|
||
'ephox.phoenix.api.general.Family',
|
||
'ephox.phoenix.extract.TypedList',
|
||
'ephox.phoenix.search.MatchSplitter',
|
||
'ephox.polaris.api.Pattern',
|
||
'ephox.polaris.api.PositionArray',
|
||
'ephox.polaris.api.Search'
|
||
],
|
||
|
||
function (Arr, Option, NamedPattern, Spot, Family, TypedList, MatchSplitter, Pattern, PositionArray, Search) {
|
||
var gen = function (universe, input) {
|
||
return PositionArray.generate(input, function (unit, offset) {
|
||
var finish = offset + universe.property().getText(unit).length;
|
||
return Option.from(Spot.range(unit, offset, finish));
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Extracts groups of elements separated by boundaries.
|
||
*
|
||
* For each group, search the text for pattern matches.
|
||
*
|
||
* Returns a list of matches.
|
||
*/
|
||
var run = function (universe, elements, patterns, optimise) {
|
||
var sections = Family.group(universe, elements, optimise);
|
||
var result = Arr.bind(sections, function (x) {
|
||
var input = TypedList.justText(x);
|
||
var text = Arr.map(input, universe.property().getText).join('');
|
||
|
||
var matches = Search.findmany(text, patterns);
|
||
var plist = gen(universe, input);
|
||
|
||
return MatchSplitter.separate(universe, plist, matches);
|
||
});
|
||
|
||
return result;
|
||
};
|
||
|
||
|
||
/**
|
||
* Runs a search for one or more words
|
||
*/
|
||
var safeWords = function (universe, elements, words, optimise) {
|
||
var patterns = Arr.map(words, function (word) {
|
||
var pattern = Pattern.safeword(word);
|
||
return NamedPattern(word, pattern);
|
||
});
|
||
return run(universe, elements, patterns, optimise);
|
||
};
|
||
|
||
|
||
/**
|
||
* Runs a search for a single token
|
||
*/
|
||
var safeToken = function (universe, elements, token, optimise) {
|
||
var pattern = NamedPattern(token, Pattern.safetoken(token));
|
||
return run(universe, elements, [pattern], optimise);
|
||
};
|
||
|
||
return {
|
||
safeWords: safeWords,
|
||
safeToken: safeToken,
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.general.Search',
|
||
|
||
[
|
||
'ephox.phoenix.search.Searcher'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (Searcher) {
|
||
var run = function (universe, items, patterns, optimise) {
|
||
return Searcher.run(universe, items, patterns, optimise);
|
||
};
|
||
|
||
var safeWords = function (universe, items, words, optimise) {
|
||
return Searcher.safeWords(universe, items, words, optimise);
|
||
};
|
||
|
||
var safeToken = function (universe, items, token, optimise) {
|
||
return Searcher.safeToken(universe, items, token, optimise);
|
||
};
|
||
|
||
return {
|
||
safeWords: safeWords,
|
||
safeToken: safeToken,
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.phoenix.api.dom.DomSearch',
|
||
|
||
[
|
||
'ephox.boss.api.DomUniverse',
|
||
'ephox.phoenix.api.general.Search'
|
||
],
|
||
|
||
/**
|
||
* Documentation is in the actual implementations.
|
||
*/
|
||
function (DomUniverse, Search) {
|
||
var universe = DomUniverse();
|
||
|
||
var run = function (elements, patterns, optimise) {
|
||
return Search.run(universe, elements, patterns, optimise);
|
||
};
|
||
|
||
var safeWords = function (elements, words, optimise) {
|
||
return Search.safeWords(universe, elements, words, optimise);
|
||
};
|
||
|
||
var safeToken = function (elements, token, optimise) {
|
||
return Search.safeToken(universe, elements, token, optimise);
|
||
};
|
||
|
||
return {
|
||
safeWords: safeWords,
|
||
safeToken: safeToken,
|
||
run: run
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sugar.api.SelectorExists',
|
||
|
||
[
|
||
'ephox.sugar.api.SelectorFind'
|
||
],
|
||
|
||
function (SelectorFind) {
|
||
var any = function (selector) {
|
||
return SelectorFind.first(selector).isSome();
|
||
};
|
||
|
||
var ancestor = function (scope, selector, isRoot) {
|
||
return SelectorFind.ancestor(scope, selector, isRoot).isSome();
|
||
};
|
||
|
||
var sibling = function (scope, selector) {
|
||
return SelectorFind.sibling(scope, selector).isSome();
|
||
};
|
||
|
||
var child = function (scope, selector) {
|
||
return SelectorFind.child(scope, selector).isSome();
|
||
};
|
||
|
||
var descendant = function (scope, selector) {
|
||
return SelectorFind.descendant(scope, selector).isSome();
|
||
};
|
||
|
||
var closest = function (scope, selector, isRoot) {
|
||
return SelectorFind.closest(scope, selector, isRoot).isSome();
|
||
};
|
||
|
||
return {
|
||
any: any,
|
||
ancestor: ancestor,
|
||
sibling: sibling,
|
||
child: child,
|
||
descendant: descendant,
|
||
closest: closest
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.process.Preprocessor',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.perhaps.Option',
|
||
'ephox.phoenix.api.dom.DomSearch',
|
||
'ephox.polaris.api.Pattern',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.SelectorExists'
|
||
],
|
||
|
||
function (Arr, Option, DomSearch, Pattern, Struct, Attr, Css, Element, Html, Insert, InsertAll, Node, SelectorExists) {
|
||
//the big fat holy grail of URL pattern matching..
|
||
var regex = '((([A-Za-z]{3,9}:(?:\\/\\/)?)(?:[\\-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9\\.\\-]+|(?:www\\.|[\\-;:&=\\+\\$,\\w]+@)[A-Za-z0-9\\.\\-]+)(:[0-9]+)?((?:\\/[\\+~%\\/\\.\\w\\-_]*)?\\??(?:[\\-\\+=&;%@\\.\\w_]*)#?(?:[\\.\\!\\/\\\\\\w]*))?)';
|
||
|
||
var findLinks = function (elements) {
|
||
var data = Struct.immutable('word', 'pattern');
|
||
var term = Pattern.unsafetoken(regex);
|
||
var pattern = data('__INTERNAL__', term);
|
||
return DomSearch.run(elements, [pattern]);
|
||
};
|
||
|
||
var notInLink = function (element) {
|
||
// return true;
|
||
return !SelectorExists.closest(element, 'a');
|
||
};
|
||
|
||
var wrap = function (elements) {
|
||
return Option.from(elements[0]).filter(notInLink).map(function (first) {
|
||
var tag = Element.fromTag('a');
|
||
Insert.before(first, tag);
|
||
InsertAll.append(tag, elements);
|
||
Attr.set(tag, 'href', Html.get(tag));
|
||
return tag;
|
||
});
|
||
};
|
||
|
||
var links = function (elements) {
|
||
var matches = findLinks(elements);
|
||
Arr.each(matches, function (match) {
|
||
// TBIO-2444 Do not wrap anything with @ symbol, it could be an email
|
||
if(match.exact().indexOf('@') < 0) wrap(match.elements());
|
||
});
|
||
};
|
||
|
||
var position = function (elements) {
|
||
Arr.each(elements, function (elem) {
|
||
if (Node.isElement(elem)) Css.remove(elem, 'position');
|
||
});
|
||
};
|
||
|
||
return {
|
||
links: links,
|
||
position: position
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.api.RunPaste',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.limbo.process.PasteFilters',
|
||
'ephox.limbo.process.Preprocessor',
|
||
'ephox.pastiche.api.HybridAction',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Arr, PasteFilters, Preprocessor, HybridAction, Html, Traverse) {
|
||
var preprocess = function (platform, container) {
|
||
var children = Traverse.children(container);
|
||
Arr.each([ Preprocessor.links, Preprocessor.position ], function (f) {
|
||
f(children);
|
||
});
|
||
};
|
||
|
||
var go = function (doc, platform, container, merging, isWord) {
|
||
preprocess(platform, container);
|
||
var html = Html.get(container);
|
||
var filters = PasteFilters.derive(isWord, merging, platform);
|
||
return HybridAction.go(doc, html, filters);
|
||
};
|
||
|
||
return {
|
||
go: go
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.limbo.api.Sources',
|
||
|
||
[
|
||
'ephox.pastiche.api.HybridAction',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.PredicateExists'
|
||
],
|
||
|
||
function (HybridAction, Attr, Html, PredicateExists) {
|
||
var ie11 = function (container) {
|
||
// This looks expensive. Using grep on corpus,
|
||
// string searching for "<font" or "mso-list:" picked up all relevant cases
|
||
return PredicateExists.descendant(container, function (el) {
|
||
return Attr.has(el, 'style') ? Attr.get(el, 'style').indexOf('mso-') > -1 : false;
|
||
});
|
||
};
|
||
|
||
var other = function (container) {
|
||
var html = Html.get(container);
|
||
return HybridAction.isWordContent(html);
|
||
};
|
||
|
||
var isWord = function (platform, container) {
|
||
var browser = platform.browser;
|
||
var detector = browser.isIE() && browser.version.major >= 11 ? ie11 : other;
|
||
return detector(container);
|
||
};
|
||
|
||
return {
|
||
isWord: isWord
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.data.Range',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.sugar.api.Compare'
|
||
],
|
||
|
||
function (Fun, Compare) {
|
||
return function (startContainer, startOffset, endContainer, endOffset) {
|
||
var collapsed = Compare.eq(startContainer, endContainer) && startOffset === endOffset;
|
||
|
||
return {
|
||
startContainer: Fun.constant(startContainer),
|
||
startOffset: Fun.constant(startOffset),
|
||
endContainer: Fun.constant(endContainer),
|
||
endOffset: Fun.constant(endOffset),
|
||
collapsed: Fun.constant(collapsed)
|
||
};
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.api.BodySwitch',
|
||
|
||
[
|
||
'ephox.sloth.data.Range',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Range, Element, Insert, Remove, Traverse) {
|
||
return function (selection) {
|
||
|
||
var placeholder = Element.fromTag('br');
|
||
|
||
var toOn = function (element, offscreen) {
|
||
element.dom().focus();
|
||
};
|
||
|
||
var getWin = function (offscreen) {
|
||
var doc = Traverse.owner(offscreen);
|
||
return doc.dom().defaultView;
|
||
};
|
||
|
||
var toOff = function (element, offscreen) {
|
||
var win = getWin(offscreen);
|
||
win.focus();
|
||
Insert.append(offscreen, placeholder);
|
||
selection.set(win, Range(placeholder, 0, placeholder, 0));
|
||
};
|
||
|
||
var cleanup = function () {
|
||
Remove.remove(placeholder);
|
||
};
|
||
|
||
return {
|
||
cleanup: cleanup,
|
||
toOn: toOn,
|
||
toOff: toOff
|
||
};
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.api.DivSwitch',
|
||
|
||
[
|
||
'ephox.peanut.Fun'
|
||
],
|
||
|
||
function (Fun) {
|
||
return function () {
|
||
var toOn = function (element, offscreen) {
|
||
element.dom().focus();
|
||
};
|
||
|
||
var toOff = function (element, offscreen) {
|
||
offscreen.dom().focus();
|
||
};
|
||
|
||
var cleanup = Fun.identity;
|
||
|
||
return {
|
||
toOn: toOn,
|
||
toOff: toOff,
|
||
cleanup: cleanup
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.plumber.tap.control.BlockControl',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var create = function () {
|
||
var blocked = false;
|
||
var isBlocked = function () { return blocked; };
|
||
var block = function () { blocked = true; };
|
||
var unblock = function () { blocked = false; };
|
||
|
||
return {
|
||
isBlocked: isBlocked,
|
||
block: block,
|
||
unblock: unblock
|
||
}
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.plumber.tap.wrap.Tapped',
|
||
|
||
[
|
||
],
|
||
|
||
function () {
|
||
var create = function (control, instance) {
|
||
return {
|
||
control: control,
|
||
instance: instance
|
||
}
|
||
};
|
||
|
||
return {
|
||
create: create
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.plumber.tap.function.BlockTap',
|
||
|
||
[
|
||
'ephox.plumber.tap.control.BlockControl',
|
||
'ephox.plumber.tap.wrap.Tapped'
|
||
],
|
||
|
||
function (BlockControl, Tapped) {
|
||
var tap = function (fn) {
|
||
var control = BlockControl.create();
|
||
|
||
var instance = function () {
|
||
if (!control.isBlocked())
|
||
fn.apply(null, arguments);
|
||
};
|
||
|
||
return Tapped.create(control, instance);
|
||
};
|
||
|
||
return {
|
||
tap: tap
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.api.Paste',
|
||
|
||
[
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.peanut.Fun',
|
||
'global!setTimeout'
|
||
],
|
||
|
||
function (PlatformDetection, Fun, setTimeout) {
|
||
var detection = PlatformDetection.detect();
|
||
|
||
var ie10 = function (doc, tap, postpaste) {
|
||
// Block the tap, and fire a paste.
|
||
tap.control.block();
|
||
doc.dom().execCommand('paste');
|
||
postpaste();
|
||
tap.control.unblock();
|
||
};
|
||
|
||
var others = function (doc, tap, postpaste) {
|
||
setTimeout(postpaste, 1);
|
||
};
|
||
|
||
// Most browsers can just let the paste event continue.
|
||
// on IE10, the paste event must be cancelled and done manually
|
||
var willBlock = detection.browser.isIE() && detection.browser.version.major <= 10;
|
||
|
||
var runner = willBlock ? ie10 : others;
|
||
|
||
var run = function (doc, tap, postpaste) {
|
||
return runner(doc, tap, postpaste);
|
||
};
|
||
|
||
return {
|
||
willBlock: Fun.constant(willBlock),
|
||
run: run
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.engine.Consolidator',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.InsertAll',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Arr, Element, Insert, InsertAll, Remove, Traverse) {
|
||
// TBIO-2440. In some situations on Windows 7 Chrome, pasting into the offscreen div
|
||
// actually splits the div in two. The purpose of this function is to incorporate
|
||
// any of the split divs into the main one.
|
||
var consolidate = function (offscreen, isOffscreen) {
|
||
Traverse.nextSibling(offscreen).filter(isOffscreen).each(function (other) {
|
||
var children = Traverse.children(other);
|
||
InsertAll.append(offscreen, children);
|
||
Remove.remove(other);
|
||
});
|
||
oneChild(offscreen, isOffscreen);
|
||
};
|
||
// TBIO-3010: In Chrome (reproducible in both Windows and Mac) when pasting from notepad the offscreen div
|
||
// generates multiple sloth divs, causing the content to be not pasted correctly. This function
|
||
// runs across the children of the offscreen div and if it is a sloth element then it extract
|
||
// the content and wraps it in a normal div.
|
||
var cleanChild = function (child, offscreen) {
|
||
var children = Traverse.children(child);
|
||
var wrapper = Element.fromTag('div', Traverse.owner(child).dom());
|
||
InsertAll.append(wrapper, children);
|
||
Insert.before(child, wrapper);
|
||
Remove.remove(child);
|
||
};
|
||
|
||
var oneChild = function (offscreen, isOffscreen) {
|
||
var children = Traverse.children(offscreen);
|
||
Arr.each(children, function (child) {
|
||
if (isOffscreen(child)) cleanChild(child, offscreen);
|
||
});
|
||
};
|
||
|
||
return {
|
||
consolidate: consolidate
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.engine.Offscreen',
|
||
|
||
[
|
||
'ephox.epithet.Id',
|
||
'ephox.scullion.Struct',
|
||
'ephox.sloth.engine.Consolidator',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Css',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Html',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Id, Struct, Consolidator, Attr, Class, Css, Element, Html, Insert, Remove, SelectorFind, Traverse) {
|
||
var hash = Id.generate('ephox-sloth-bin');
|
||
|
||
return function (switcher) {
|
||
var offscreen = Element.fromTag('div');
|
||
Attr.set(offscreen, 'contenteditable', 'true');
|
||
Class.add(offscreen, hash);
|
||
Css.setAll(offscreen, {
|
||
position: 'absolute',
|
||
left: '0px',
|
||
top: '0px',
|
||
width: '0px',
|
||
height: '0px',
|
||
overflow: 'hidden'
|
||
});
|
||
|
||
var attach = function (target) {
|
||
Remove.empty(offscreen);
|
||
Insert.append(target, offscreen);
|
||
};
|
||
|
||
var focus = function () {
|
||
var body = SelectorFind.ancestor(offscreen, 'body');
|
||
body.each(function (b) {
|
||
switcher.toOff(b, offscreen);
|
||
});
|
||
};
|
||
|
||
var isOffscreen = function (other) {
|
||
return Class.has(other, hash);
|
||
};
|
||
|
||
var contents = function () {
|
||
Consolidator.consolidate(offscreen, isOffscreen);
|
||
var data = Struct.immutable('elements', 'html', 'container');
|
||
var elements = Traverse.children(offscreen);
|
||
var html = Html.get(offscreen);
|
||
return data(elements, html, offscreen);
|
||
};
|
||
|
||
var detach = function () {
|
||
Remove.remove(offscreen);
|
||
};
|
||
|
||
var container = function () {
|
||
return offscreen;
|
||
};
|
||
|
||
return {
|
||
attach: attach,
|
||
focus: focus,
|
||
contents: contents,
|
||
container: container,
|
||
detach: detach
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.sloth.api.ProxyPaste',
|
||
|
||
[
|
||
'ephox.peanut.Fun',
|
||
'ephox.plumber.tap.function.BlockTap',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sloth.api.Paste',
|
||
'ephox.sloth.engine.Offscreen',
|
||
'ephox.sugar.api.Traverse'
|
||
],
|
||
|
||
function (Fun, BlockTap, Event, Events, Paste, Offscreen, Traverse) {
|
||
return function (switcher, element) {
|
||
var offscreen = Offscreen(switcher);
|
||
|
||
var postpaste = function () {
|
||
switcher.cleanup();
|
||
var contents = offscreen.contents();
|
||
offscreen.detach();
|
||
events.trigger.after(contents.elements(), contents.html(), offscreen.container());
|
||
};
|
||
|
||
var tap = BlockTap.tap(function () {
|
||
events.trigger.before();
|
||
offscreen.attach(element);
|
||
offscreen.focus();
|
||
Paste.run(Traverse.owner(element), tap, postpaste);
|
||
});
|
||
|
||
var handler = function () {
|
||
tap.instance();
|
||
};
|
||
|
||
var events = Events.create({
|
||
before: Event([]),
|
||
after: Event(['elements', 'html', 'container'])
|
||
});
|
||
|
||
var destroy = Fun.noop;
|
||
|
||
return {
|
||
instance: Fun.constant(handler),
|
||
destroy: destroy,
|
||
events: events.registry
|
||
};
|
||
};
|
||
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.pastiche.Pastiche',
|
||
|
||
[
|
||
'ephox.cement.api.CementConstants',
|
||
'ephox.cement.pastiche.IeBlob',
|
||
'ephox.compass.Arr',
|
||
'ephox.fred.PlatformDetection',
|
||
'ephox.fussy.api.WindowSelection',
|
||
'ephox.limbo.api.RunPaste',
|
||
'ephox.limbo.api.Sources',
|
||
'ephox.peanut.Fun',
|
||
'ephox.perhaps.Option',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.sloth.api.BodySwitch',
|
||
'ephox.sloth.api.DivSwitch',
|
||
'ephox.sloth.api.ProxyPaste',
|
||
'ephox.sugar.api.Class',
|
||
'ephox.sugar.api.Elements',
|
||
'ephox.sugar.api.Node',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.Traverse',
|
||
'global!console',
|
||
'global!setTimeout'
|
||
],
|
||
|
||
function (CementConstants, IeBlob, Arr, PlatformDetection, WindowSelection, RunPaste, Sources, Fun, Option, Event, Events, BodySwitch, DivSwitch, ProxyPaste, Class, Elements, Node, Remove, Traverse, console, setTimeout) {
|
||
var platform = PlatformDetection.detect();
|
||
|
||
return function (prePasteFilter, body, mergeSettings, intraFlag) {
|
||
// Temporary hack until we restructure in TBIO-1515
|
||
var findClipboardTags = function (container, isWord) {
|
||
return (intraFlag !== undefined && !isWord) ? intraFlag.findClipboardTags(Traverse.children(container)) : Option.none();
|
||
};
|
||
|
||
|
||
var events = Events.create({
|
||
paste: Event(['elements', 'assets']),
|
||
error: Event(['message'])
|
||
});
|
||
|
||
var fakeSelecton = {
|
||
// dupe from hare.selection.Selection
|
||
set: function (win, range) {
|
||
WindowSelection.setExact(win, range.startContainer(), range.startOffset(), range.endContainer(), range.endOffset());
|
||
}
|
||
};
|
||
|
||
// TBIO-2019: scrollbar lock on paste.
|
||
// When using DivSwitch for inline editing, FF & webkit browsers will lock the scrollbar after paste
|
||
// This is because the the offscreen div was not used and hence no filtration was run and the scrollbar unlock code never got called
|
||
// To verify this paste formatted html and see the formatting unchanged <span style="color:red">test</span>
|
||
// DivSwitch calls focus on the offscreen div, FF & Webkit do not set selection on focus,
|
||
// so inserting into offscreen div fails, bypassing the rest of the past process.
|
||
// It works in IE because amazingly IE sets selection on focus.
|
||
// Calling BodySwitch with IE inline mode paste fails altogether, the cause of the failure is unknown
|
||
var switchF = platform.browser.isIE() && Node.name(body) !== 'body' ? DivSwitch: BodySwitch;
|
||
var switcher = switchF(fakeSelecton);
|
||
var documentElement = Traverse.owner(body);
|
||
var proxyPaste = ProxyPaste(switcher, body);
|
||
var backgroundAssets = Option.none();
|
||
|
||
proxyPaste.events.after.bind(function (event) {
|
||
var container = event.container();
|
||
switcher.toOn(body, container);
|
||
|
||
// Run a paste filter over the off-screen div.
|
||
prePasteFilter(container);
|
||
|
||
Class.add(container, CementConstants.binStyle());
|
||
var isWord = Sources.isWord(platform, container);
|
||
|
||
var pasteImpl = function (pasteSettings) {
|
||
var merging = (isWord && pasteSettings.mergeOfficeStyles) === true || (!isWord && pasteSettings.mergeHtmlStyles === true);
|
||
|
||
try {
|
||
var dump = RunPaste.go(documentElement, platform, container, merging, isWord);
|
||
if (dump !== undefined && dump !== null && dump.length > 0) {
|
||
var elements = Elements.fromHtml(dump);
|
||
|
||
backgroundAssets.fold(function () {
|
||
events.trigger.paste(elements, []);
|
||
}, function (future) {
|
||
future.get(function (assets) {
|
||
events.trigger.paste(elements, assets);
|
||
});
|
||
});
|
||
backgroundAssets = Option.none();
|
||
} else {
|
||
// This is required to stop the scroll blocking. (TBIO-2440)
|
||
events.trigger.paste([], []);
|
||
}
|
||
} catch (e) {
|
||
console.error(e);
|
||
events.trigger.error('errors.paste.process.failure');
|
||
}
|
||
};
|
||
|
||
// This potentially prompts the user, so it needs to be a callback
|
||
var normalPaste = Fun.curry(mergeSettings.get, isWord, pasteImpl);
|
||
|
||
// Temporary hack until we restructure in TBIO-1515
|
||
findClipboardTags(container, isWord).fold(normalPaste, function (tags) {
|
||
Arr.each(tags, Remove.remove);
|
||
// making sure it's asynchronous in both scenarios
|
||
setTimeout(function () {
|
||
// No need to call mergeSettings.get, we're just hard coding true
|
||
pasteImpl({ mergeHtmlStyles: true });
|
||
}, 0);
|
||
});
|
||
});
|
||
|
||
var destroy = function () {
|
||
proxyPaste.destroy();
|
||
};
|
||
|
||
var handler = function (raw) {
|
||
try {
|
||
backgroundAssets = IeBlob.convert(raw);
|
||
var instance = proxyPaste.instance();
|
||
instance();
|
||
} catch (e) {
|
||
console.error(e);
|
||
events.trigger.error('errors.paste.process.failure');
|
||
}
|
||
};
|
||
|
||
return {
|
||
handler: handler,
|
||
isSupported: Fun.constant(true),
|
||
events: events.registry,
|
||
destroy: destroy
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.smartpaste.PasteHandlers',
|
||
|
||
[
|
||
'ephox.cement.html.HtmlPaste',
|
||
'ephox.cement.images.ImagePaste',
|
||
'ephox.cement.pastiche.Pastiche',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.scullion.Struct',
|
||
'ephox.violin.Strings'
|
||
],
|
||
|
||
function (HtmlPaste, ImagePaste, Pastiche, Event, Events, Struct, Strings) {
|
||
var result = Struct.immutable('captured');
|
||
|
||
var dataContainsMicrosoftOfficeUrn = function (data) {
|
||
// copied from ELJ, this logic doesn't exist in Tord and Pastiche's version isn't good enough
|
||
return Strings.contains(data, '<html') &&
|
||
(Strings.contains(data, 'xmlns:o="urn:schemas-microsoft-com:office:office"') ||
|
||
Strings.contains(data, 'xmlns:x="urn:schemas-microsoft-com:office:excel"'));
|
||
};
|
||
|
||
return function (prePasteFilter, body, mergeSettings, pasteSettings) {
|
||
// Temporary hack until we restructure in TBIO-1515
|
||
var intraFlag = pasteSettings.intraFlag;
|
||
|
||
var htmlPaste = HtmlPaste(mergeSettings, pasteSettings);
|
||
var imagePaste = ImagePaste(pasteSettings);
|
||
var fallback = Pastiche(prePasteFilter, body, mergeSettings, intraFlag);
|
||
|
||
var events = Events.create({
|
||
paste: Event(['elements', 'assets']),
|
||
error: Event(['message']),
|
||
cancel: Event([])
|
||
});
|
||
|
||
var bindEvents = function (source) {
|
||
// why can't this be easy
|
||
source.events.error.bind(function (event) {
|
||
events.trigger.error(event.message());
|
||
});
|
||
source.events.paste.bind(function (event) {
|
||
var elements = event.elements();
|
||
var assets = event.assets();
|
||
|
||
if (elements.length === 0 && assets.length === 0)
|
||
events.trigger.cancel();
|
||
else events.trigger.paste(elements, assets);
|
||
});
|
||
};
|
||
|
||
bindEvents(htmlPaste);
|
||
bindEvents(imagePaste);
|
||
bindEvents(fallback);
|
||
|
||
var htmlRawImpl = function (raw, preferredType) {
|
||
var data = raw.clipboardData.getData(preferredType);
|
||
// the decision was made that non-word HTML paste uses pastiche,
|
||
// rather than run pastiche after tord
|
||
var captured = dataContainsMicrosoftOfficeUrn(data) ?
|
||
htmlPaste.handler(data) :
|
||
execFallback(raw, preferredType);
|
||
|
||
return result(captured);
|
||
};
|
||
|
||
var imageRawImpl = function (raw, _) {
|
||
var captured = imagePaste.handler(raw.clipboardData.items);
|
||
return result(captured);
|
||
};
|
||
|
||
// has it's own function to ensure when the arguments change both places are updated
|
||
var execFallback = function (raw, _) {
|
||
fallback.handler(raw);
|
||
return false;
|
||
};
|
||
|
||
var fallbackImpl = function (raw, preferredType) {
|
||
var captured = execFallback(raw, preferredType);
|
||
return result(captured);
|
||
};
|
||
|
||
// this must contain everything from the priority list in PasteBroker
|
||
var handlers = {
|
||
'html': htmlRawImpl,
|
||
'image': imagePaste.isSupported() ? imageRawImpl : fallbackImpl,
|
||
'files': imagePaste.isSupported() ? imageRawImpl : fallbackImpl,
|
||
'fallback': fallbackImpl
|
||
};
|
||
|
||
// Temporary hack until we restructure in TBIO-1515
|
||
if (intraFlag !== undefined) {
|
||
handlers[intraFlag.clipboardType()] = fallbackImpl;
|
||
}
|
||
|
||
var destroy = function () {
|
||
fallback.destroy();
|
||
};
|
||
|
||
return {
|
||
events: events.registry,
|
||
handlers: handlers,
|
||
destroy: destroy
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.smartpaste.PasteBroker',
|
||
|
||
[
|
||
'ephox.cement.smartpaste.Inspection',
|
||
'ephox.cement.smartpaste.PasteHandlers',
|
||
'ephox.perhaps.Option',
|
||
'ephox.perhaps.Options',
|
||
'global!RegExp'
|
||
],
|
||
|
||
function (Inspection, PasteHandlers, Option, Options, RegExp) {
|
||
// these must match the list of handler functions in PasteHandlers
|
||
var defaultPriority = ['html', 'image', 'files'];
|
||
|
||
var unknownFlavor = { flavor: 'fallback' };
|
||
|
||
var getPreferredFlavor = function (priority, data) {
|
||
if (!Inspection.isValidData(data)) return unknownFlavor;
|
||
var types = data.types;
|
||
|
||
var r = Options.findMap(priority, function (p) {
|
||
// case insensitive match on priority text (e.g. 'html' matches 'text/html')
|
||
var pmatch = new RegExp(p, 'i');
|
||
return Options.findMap(types, function (t) {
|
||
return t.match(pmatch) !== null ?
|
||
Option.some({ type: t, flavor: p }) :
|
||
Option.none();
|
||
});
|
||
});
|
||
return r.getOr(unknownFlavor);
|
||
};
|
||
|
||
// extracted to enforce separation of concerns between events, priority order and handling functions
|
||
var createHandler = function (intraFlag, handlers) {
|
||
|
||
// Temporary hack until we restructure in TBIO-1515
|
||
var priority = intraFlag === undefined ? defaultPriority : [ intraFlag.clipboardType() ].concat(defaultPriority);
|
||
|
||
return function (raw) {
|
||
var preferred = getPreferredFlavor(priority, raw.clipboardData);
|
||
var impl = handlers[preferred.flavor];
|
||
var result = impl(raw, preferred.type);
|
||
if (result.captured()) {
|
||
raw.preventDefault();
|
||
}
|
||
};
|
||
};
|
||
|
||
return function (prePasteFilter, body, mergeSettings, pasteSettings) {
|
||
var pasteHandlers = PasteHandlers(prePasteFilter, body, mergeSettings, pasteSettings);
|
||
|
||
var handlePaste = createHandler(pasteSettings.intraFlag, pasteHandlers.handlers);
|
||
|
||
var destroy = function () {
|
||
pasteHandlers.destroy();
|
||
};
|
||
|
||
return {
|
||
events: pasteHandlers.events,
|
||
handlePaste: handlePaste,
|
||
destroy: destroy
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.porkbun.SourceEvent',
|
||
|
||
[
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.porkbun.Event'
|
||
],
|
||
|
||
function (Arr, Fun, Event) {
|
||
|
||
/** An event sourced from another event.
|
||
|
||
:: ([String], {bind: ..., unbind: ...}) -> SourceEvent
|
||
*/
|
||
return function (fields, source) {
|
||
var mine = Event(fields);
|
||
var numHandlers = 0;
|
||
|
||
var triggerer = function(evt) {
|
||
// yay! Let's unbox this event, convert it to a varargs, so it can be re-boxed!
|
||
var args = Arr.map(fields, function (field) {
|
||
return evt[field]();
|
||
});
|
||
mine.trigger.apply(null, args);
|
||
};
|
||
|
||
var bind = function (handler) {
|
||
mine.bind(handler);
|
||
numHandlers++;
|
||
if (numHandlers === 1) {
|
||
source.bind(triggerer);
|
||
}
|
||
};
|
||
|
||
var unbind = function (handler) {
|
||
mine.unbind(handler);
|
||
numHandlers--;
|
||
if (numHandlers === 0) {
|
||
source.unbind(triggerer);
|
||
}
|
||
};
|
||
|
||
return {
|
||
bind: bind,
|
||
unbind: unbind,
|
||
trigger: Fun.die("Cannot trigger a source event.")
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.cement.api.Cement',
|
||
|
||
[
|
||
'ephox.cement.flash.Flash',
|
||
'ephox.cement.smartpaste.MergeSettings',
|
||
'ephox.cement.smartpaste.PasteBroker',
|
||
'ephox.limbo.api.RtfImage',
|
||
'ephox.plumber.tap.function.BlockTap',
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.porkbun.SourceEvent',
|
||
'ephox.sloth.api.Paste',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.InsertAll'
|
||
],
|
||
|
||
function (Flash, MergeSettings, PasteBroker, RtfImage, BlockTap, Event, Events, SourceEvent, Paste, Element, InsertAll) {
|
||
return function (body, createDialog, prePasteFilter, cementConfig) {
|
||
var flash = Flash(createDialog, cementConfig);
|
||
var mergeSettings = MergeSettings(createDialog, cementConfig);
|
||
var pasteSettings = { baseUrl: cementConfig.baseUrl, allowLocalImages: cementConfig.allowLocalImages, intraFlag: cementConfig.intraFlag };
|
||
var pasteBroker = PasteBroker(prePasteFilter, body, mergeSettings, pasteSettings);
|
||
|
||
var events = Events.create({
|
||
cancel: SourceEvent([], mergeSettings.events.cancel), // only merge settings can cancel paste, not flash
|
||
error: Event(['message']),
|
||
insert: Event(['elements', 'assets'])
|
||
});
|
||
|
||
var insert = function (event) {
|
||
pasteTap.control.unblock();
|
||
events.trigger.insert(event.elements(), event.assets());
|
||
};
|
||
|
||
flash.events.insert.bind(insert);
|
||
|
||
var pasteTap = BlockTap.tap(function (nativeEvent) {
|
||
if (Paste.willBlock()) {
|
||
/*
|
||
On IE10, a second paste is required. That happens synchronously, before we can
|
||
return anything that says "block the tap".
|
||
In order to make this code reentrant, we need to eagerly block.
|
||
*/
|
||
pasteTap.control.block();
|
||
|
||
/*
|
||
We then need to cancel the native event, because due to reentrancy the "is blocked"
|
||
check below actually returns false. If we don't prevent default here, we allow the
|
||
default paste to complete on the initial paste event.
|
||
*/
|
||
nativeEvent.preventDefault();
|
||
}
|
||
|
||
pasteBroker.handlePaste(nativeEvent);
|
||
|
||
// If dialogs are opened, we set the block and need to prevent default
|
||
if (pasteTap.control.isBlocked()) nativeEvent.preventDefault();
|
||
});
|
||
|
||
// block the broker from receiving paste events while the merge window is open.
|
||
mergeSettings.events.open.bind(pasteTap.control.block);
|
||
mergeSettings.events.close.bind(pasteTap.control.unblock);
|
||
|
||
pasteBroker.events.paste.bind(function (event) {
|
||
var elements = event.elements();
|
||
var content = Element.fromTag('div');
|
||
InsertAll.append(content, elements);
|
||
|
||
if (RtfImage.exists(content)) {
|
||
// block the broker from receiving paste events while the flash window is open.
|
||
pasteTap.control.block();
|
||
flash.gordon(content, event.assets());
|
||
} else {
|
||
insert(event);
|
||
}
|
||
});
|
||
|
||
var destroy = function () {
|
||
pasteBroker.destroy();
|
||
};
|
||
|
||
var passThroughError = function (event) {
|
||
pasteTap.control.unblock();
|
||
events.trigger.error(event.message());
|
||
};
|
||
flash.events.error.bind(passThroughError);
|
||
pasteBroker.events.error.bind(passThroughError);
|
||
|
||
return {
|
||
paste: pasteTap.instance,
|
||
isBlocked: pasteTap.control.isBlocked,
|
||
destroy: destroy,
|
||
events: events.registry
|
||
};
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.settings.Defaults',
|
||
|
||
[
|
||
|
||
],
|
||
|
||
function() {
|
||
return {
|
||
officeStyles: 'prompt',
|
||
htmlStyles: 'clean'
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.styles.Styles',
|
||
|
||
[
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorExists',
|
||
'ephox.sugar.api.SelectorFind',
|
||
'global!document'
|
||
],
|
||
|
||
function(Attr, Element, Insert, Remove, SelectorExists, SelectorFind, document) {
|
||
var styleid = 'powerpaste-styles';
|
||
var styleidselector = '#' + styleid;
|
||
|
||
var injectStyles = function(url) {
|
||
if (!SelectorExists.any(styleidselector)){
|
||
var htmlString =
|
||
'<style>' +
|
||
'.ephox-cement-flashbin-helpcopy-kbd ' +
|
||
'{font-size: 3em !important; text-align: center !important; vertical-align: middle !important; margin: .5em 0} ' +
|
||
'.ephox-cement-flashbin-helpcopy-kbd .ephox-polish-help-kbd ' +
|
||
'{font-size: 3em !important; vertical-align: middle !important;} ' +
|
||
'.ephox-cement-flashbin-helpcopy a ' +
|
||
'{text-decoration: underline} ' +
|
||
'.ephox-cement-flashbin-loading-spinner ' +
|
||
'{background-image: url(' + url + ') !important; width: 96px !important; height:96px !important; display: block; margin-left: auto !important; margin-right: auto !important; margin-top: 2em !important; margin-bottom: 2em !important;} ' +
|
||
'.ephox-cement-flashbin-loading p ' +
|
||
'{text-align: center !important; margin: 2em 0 !important} ' +
|
||
'.ephox-cement-flashbin-target ' +
|
||
'{height: 1px !important;} ' +
|
||
'.ephox-cement-flashbin-target.ephox-cement-flash-activate ' +
|
||
'{height: 150px !important; width: 100% !important;} ' +
|
||
'.ephox-cement-flashbin-target object ' +
|
||
'{height: 1px !important;} ' +
|
||
'.ephox-cement-flashbin-target.ephox-cement-flash-activate object ' +
|
||
'{height: 150px !important; width: 100% !important;} ' +
|
||
'</style>';
|
||
|
||
var style = Element.fromHtml(htmlString);
|
||
Attr.set(style, 'type', 'text/css');
|
||
Attr.set(style, 'id', styleid);
|
||
|
||
var head = SelectorFind.first('head').getOrDie('Head element could not be found.');
|
||
|
||
Insert.append(head, style);
|
||
|
||
}
|
||
};
|
||
|
||
var removeStyles = function() {
|
||
if (SelectorExists.any(styleidselector)) {
|
||
|
||
var head = SelectorFind.first('head').getOrDie('Head element could not be found.');
|
||
var style = SelectorFind.descendant(head, styleidselector).getOrDie('The style element could not be removed');
|
||
|
||
Remove.remove(style);
|
||
|
||
}
|
||
};
|
||
|
||
return {
|
||
injectStyles: injectStyles,
|
||
removeStyles: removeStyles
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.ModernTinyDialog',
|
||
|
||
[
|
||
'ephox.porkbun.Event',
|
||
'ephox.porkbun.Events',
|
||
'ephox.powerpaste.util.NodeUtil',
|
||
'ephox.sugar.api.Attr',
|
||
'ephox.sugar.api.Element',
|
||
'ephox.sugar.api.Insert',
|
||
'ephox.sugar.api.Remove',
|
||
'ephox.sugar.api.SelectorFind'
|
||
],
|
||
|
||
function(Event, Events, NodeUtil, Attr, Element, Insert, Remove, SelectorFind) {
|
||
return function(editor) {
|
||
var createDialog = function() {
|
||
var win, title = "", content = "", controls = [], dialogContent = null;
|
||
|
||
var events = Events.create({
|
||
close: Event([])
|
||
});
|
||
|
||
var setTitle = function(label) {
|
||
title = label;
|
||
};
|
||
|
||
var setContent = function(c) {
|
||
if (tinymce.Env.safari) {
|
||
|
||
}
|
||
var contentString = NodeUtil.nodeToString(c.dom());
|
||
content = [{
|
||
type: 'container',
|
||
html: contentString
|
||
}];
|
||
dialogContent = c;
|
||
};
|
||
|
||
var setButtons = function(buttons) {
|
||
var tinyButtons = [];
|
||
|
||
buttons.forEach(function(element, index, array){
|
||
//Convert cement buttons into tiny buttons
|
||
tinyButtons.push({
|
||
text: element.text,
|
||
onclick: element.click
|
||
});
|
||
});
|
||
|
||
controls = tinyButtons;
|
||
|
||
};
|
||
|
||
var winCloseEvent = function(e) {
|
||
events.trigger.close();
|
||
};
|
||
|
||
var programmaticWinClose = function() {
|
||
//Unbind the close event, as the dialog close event has already triggered and doesn't need to be triggered again
|
||
win.off('close', winCloseEvent);
|
||
win.close('close');
|
||
};
|
||
|
||
var show = function() {
|
||
//If we don't have any buttons, we need to add one (even if it just closes the dialog)
|
||
if (controls.length === 0) {
|
||
//This gives us back the capability to hit esc to close the dialog & the dialog doesn't take focus away from the editor
|
||
controls = [{
|
||
text: 'Close',
|
||
onclick: function() {
|
||
win.close();
|
||
}
|
||
}];
|
||
}
|
||
|
||
var winSettings = {
|
||
title: title,
|
||
spacing: 10,
|
||
padding: 10,
|
||
items: content,
|
||
buttons: controls
|
||
};
|
||
|
||
win = editor.windowManager.open(winSettings);
|
||
|
||
var tinyWindow = Element.fromDom(win.getEl());
|
||
var proxy = SelectorFind.descendant(tinyWindow, '.' + Attr.get(dialogContent, 'class')).getOrDie('We must find this element or we cannot continue');
|
||
Insert.before(proxy, dialogContent);
|
||
Remove.remove(proxy);
|
||
|
||
win.on('close', winCloseEvent);
|
||
|
||
};
|
||
|
||
var hide = function() {
|
||
programmaticWinClose();
|
||
};
|
||
|
||
var destroy = function() {
|
||
programmaticWinClose();
|
||
};
|
||
|
||
var reflow = function() {
|
||
//(this doesn't work, reflow doesn't calc based on what's actually there, it works it out based on what's in the container on tiny's side)
|
||
//So we could update the items, but for now the dialog sizes match so...
|
||
};
|
||
|
||
return {
|
||
events: events.registry,
|
||
setTitle: setTitle,
|
||
setContent: setContent,
|
||
setButtons: setButtons,
|
||
show: show,
|
||
hide: hide,
|
||
destroy: destroy,
|
||
reflow: reflow
|
||
};
|
||
};
|
||
|
||
return {
|
||
createDialog: createDialog
|
||
};
|
||
};
|
||
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.ModernPowerPaste',
|
||
[
|
||
'ephox.cement.api.Cement',
|
||
'ephox.compass.Arr',
|
||
'ephox.peanut.Fun',
|
||
'ephox.powerpaste.i18n.I18n',
|
||
'ephox.powerpaste.settings.Defaults',
|
||
'ephox.powerpaste.styles.Styles',
|
||
'ephox.powerpaste.tinymce.ErrorDialog',
|
||
'ephox.powerpaste.tinymce.ModernTinyDialog',
|
||
'ephox.powerpaste.util.NodeUtil',
|
||
'ephox.sugar.api.DomEvent',
|
||
'ephox.sugar.api.Element',
|
||
'global!setTimeout',
|
||
'global!tinymce'
|
||
],
|
||
function (Cement, Arr, Fun, I18n, Defaults, Styles, ErrorDialog, ModernTinyDialog, NodeUtil, DomEvent, Element, setTimeout, tinymce) {
|
||
return function (editor, url, settings, uploader) {
|
||
|
||
var bm, swfUrl, imgUrl, cssUrl, jsUrl;
|
||
|
||
jsUrl = (settings ? settings.swfUrl : url) + '/js';
|
||
swfUrl = (settings ? settings.swfUrl : url) + '/flash/textboxpaste.swf';
|
||
imgUrl = (settings ? settings.imgUrl : url) + '/img/spinner_96.gif';
|
||
cssUrl = (settings ? settings.cssUrl : url) + '/css/editorcss.css';
|
||
|
||
editor.on('init', function(e) {
|
||
//Inject the styles for our dialog into the page
|
||
Styles.injectStyles(imgUrl);
|
||
|
||
//Inject css into editor
|
||
editor.dom.loadCSS(cssUrl);
|
||
|
||
var cementSettings = {
|
||
baseUrl: jsUrl,
|
||
swf: swfUrl,
|
||
officeStyles: editor.settings.powerpaste_word_import || Defaults.officeStyles,
|
||
htmlStyles: editor.settings.powerpaste_html_import || Defaults.htmlStyles,
|
||
translations: I18n.translate,
|
||
allowLocalImages: editor.settings.powerpaste_allow_local_images
|
||
};
|
||
|
||
var tinyDialog = ModernTinyDialog(editor);
|
||
var ed = Element.fromDom(editor.getBody());
|
||
var cement = Cement(ed, tinyDialog.createDialog, Fun.noop, cementSettings);
|
||
|
||
cement.events.cancel.bind(function() {
|
||
bm = null;
|
||
});
|
||
|
||
cement.events.error.bind(function(event) {
|
||
|
||
bm = null;
|
||
|
||
ErrorDialog.showDialog(editor,
|
||
I18n.translate(
|
||
event.message()
|
||
)
|
||
);
|
||
});
|
||
|
||
cement.events.insert.bind(function(event) {
|
||
|
||
var stringHTML = Arr.map(event.elements(), function (element) {
|
||
return NodeUtil.nodeToString(element.dom());
|
||
}).join('');
|
||
|
||
//This code was taken from tiny4
|
||
if (editor.hasEventListeners('PastePostProcess')) {
|
||
// We need to attach the element to the DOM so Sizzle selectors work on the contents
|
||
var tempBody = editor.dom.add(editor.getBody(), 'div', {style: 'display:none'}, stringHTML);
|
||
stringHTML = editor.fire('PastePostProcess', {node: tempBody}).node.innerHTML;
|
||
editor.dom.remove(tempBody);
|
||
}
|
||
|
||
//Ensure the editor has focus
|
||
editor.focus();
|
||
|
||
//Wait for focus to come back (ie10)
|
||
setTimeout(function(){
|
||
|
||
//Once we've got the html we want to insert and have performed post processing, return the
|
||
editor.selection.moveToBookmark(bm); //the selection to where it was
|
||
|
||
//Delete the bookmark reference so we can do it all again
|
||
bm = null;
|
||
|
||
editor.undoManager.transact(function(){
|
||
//Content insertion
|
||
editor.insertContent(stringHTML, {merge: editor.settings.paste_merge_formats !== false});
|
||
|
||
uploader.prepareImages(event.assets());
|
||
});
|
||
|
||
uploader.uploadImages(event.assets());
|
||
|
||
}, 1);
|
||
|
||
|
||
});
|
||
|
||
DomEvent.bind(ed, 'paste', function (e) {
|
||
//We need to bookmark the selection before we paste the content
|
||
//So that it knows where to place it back in to the editor when we insert from cement.
|
||
|
||
if (!bm) {
|
||
//Since ie pastes twice, we need to get the bookmark once and ignore the second
|
||
bm = editor.selection.getBookmark();
|
||
}
|
||
|
||
cement.paste(e.raw());
|
||
|
||
//IE appears to require that we blur the iframe
|
||
setTimeout(function() {
|
||
if (editor.windowManager.windows[0]) {
|
||
editor.windowManager.windows[0].getEl().focus();
|
||
}
|
||
}, 1);
|
||
});
|
||
|
||
});
|
||
|
||
editor.on('remove', function(e) {
|
||
//When we're removing the last editor, we need to remove our injected styles
|
||
if (tinymce.editors.length === 1) {
|
||
Styles.removeStyles();
|
||
}
|
||
});
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.tinymce.TinyPowerPaste',
|
||
[
|
||
'ephox.powerpaste.imageupload.UploaderFactory',
|
||
'ephox.powerpaste.tinymce.LegacyPowerPaste',
|
||
'ephox.powerpaste.tinymce.ModernPowerDrop',
|
||
'ephox.powerpaste.tinymce.ModernPowerPaste',
|
||
'global!tinymce'
|
||
],
|
||
|
||
function (UploaderFactory, LegacyPowerPaste, ModernPowerDrop, ModernPowerPaste, tinymce) {
|
||
/*jshint jquery:true */
|
||
return function (settings) {
|
||
|
||
return function (editor, url) {
|
||
|
||
var setupModern = function () {
|
||
var uploader = UploaderFactory(editor);
|
||
|
||
ModernPowerPaste(editor, url, settings, uploader);
|
||
|
||
if (!editor.settings.powerpaste_block_drop) {
|
||
ModernPowerDrop(editor, url, settings, uploader);
|
||
}
|
||
};
|
||
|
||
var setupLegacy = function () {
|
||
LegacyPowerPaste(editor, settings);
|
||
};
|
||
|
||
if (tinymce.Env.ie && tinymce.Env.ie < 10) {
|
||
setupLegacy();
|
||
} else {
|
||
setupModern();
|
||
}
|
||
|
||
var blockDragEvents = function (element) {
|
||
editor.dom.bind(element, 'drop dragstart dragend dragover dragenter dragleave dragdrop draggesture', function(e) {
|
||
return tinymce.dom.Event.cancel(e);
|
||
});
|
||
};
|
||
|
||
if (editor.settings.powerpaste_block_drop) {
|
||
editor.on('init', function(e) {
|
||
blockDragEvents(editor.getBody());
|
||
blockDragEvents(editor.getDoc());
|
||
});
|
||
}
|
||
|
||
if (editor.settings.paste_postprocess) {
|
||
editor.on('PastePostProcess', function(e) {
|
||
editor.settings.paste_postprocess.call(this, this, e);
|
||
});
|
||
}
|
||
};
|
||
};
|
||
}
|
||
);
|
||
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
(function (define, require, demand) {
|
||
define(
|
||
'ephox.powerpaste.PowerPastePlugin',
|
||
[
|
||
'ephox.powerpaste.tinymce.TinyPowerPaste',
|
||
'global!tinymce'
|
||
],
|
||
function(TinyPowerPaste, tinymce) {
|
||
return function(settings) {
|
||
tinymce.PluginManager.requireLangPack('powerpaste', 'ar,ca,cs,da,de,el,es,fa,fi,fr_FR,he_IL,hr,hu_HU,it,ja,kk,ko_KR,nb_NO,nl,pl,pt_BR,pt_PT,ro,ru,sk,sl_SI,sv_SE,th_TH,tr,uk,zh_CN,zh_TW');
|
||
tinymce.PluginManager.add('powerpaste', TinyPowerPaste(settings));
|
||
|
||
};
|
||
}
|
||
);
|
||
})(ephox.bolt.module.api.define, ephox.bolt.module.api.require, ephox.bolt.module.api.demand);
|
||
|
||
dem('ephox.powerpaste.PowerPastePlugin')();
|
||
if (this.ephox && this.ephox.bolt)
|
||
this.ephox.bolt = old;
|
||
})();
|