diff --git a/demo/demo.js b/demo/demo.js
index dadff3ec6..1c751f968 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -2,8 +2,8 @@
'use strict';
-exports.ContentKitDemo = {
- toggleCodePane: function(editor) {
+var ContentKitDemo = exports.ContentKitDemo = {
+ toggleCodePane: function() {
if(document.body.className === 'code-pane-open') {
this.closeCodePane();
} else {
@@ -11,25 +11,26 @@ exports.ContentKitDemo = {
}
},
- openCodePane: function(editor) {
- this.syncCodePane(editor);
+ openCodePane: function() {
window.getSelection().removeAllRanges();
document.body.className = 'code-pane-open';
+ location.hash = 'code';
},
closeCodePane: function() {
window.getSelection().removeAllRanges();
document.body.className = '';
+ location.hash = '';
},
syncCodePane: function(editor) {
var codePaneJSON = document.getElementById('code-json');
var codePaneHTML = document.getElementById('code-html');
var json = editor.model;
- var html = editor.compiler.render(json);
+ //var html = editor.compiler.render(json);
codePaneJSON.innerHTML = this.syntaxHighlight(json);
- codePaneHTML.textContent = this.formatXML(html);
+ //codePaneHTML.textContent = this.formatXML(html);
},
formatXML: function(xml) {
@@ -91,4 +92,12 @@ exports.ContentKitDemo = {
};
+// Initialize
+if (editor) {
+ ContentKitDemo.syncCodePane(editor);
+}
+if (location.hash === '#code') {
+ ContentKitDemo.openCodePane();
+}
+
}(this, document));
diff --git a/demo/index.html b/demo/index.html
index 66e072abc..92842fe66 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -16,7 +16,7 @@
diff --git a/dist/content-kit-editor.js b/dist/content-kit-editor.js
index 3b3013f58..89c70e30c 100755
--- a/dist/content-kit-editor.js
+++ b/dist/content-kit-editor.js
@@ -3,7 +3,7 @@
* @version 0.1.0
* @author Garth Poitras (http://garthpoitras.com/)
* @license MIT
- * Last modified: Aug 22, 2014
+ * Last modified: Aug 25, 2014
*/
(function(exports, document) {
@@ -11,7 +11,7 @@
'use strict';
define("content-kit",
- ["./content-kit-compiler/types/type","./content-kit-compiler/models/block","./content-kit-compiler/models/text","./content-kit-compiler/models/image","./content-kit-compiler/models/embed","./content-kit-compiler/compiler","./content-kit-compiler/parsers/html-parser","./content-kit-compiler/renderers/html-renderer","./content-kit-editor/editor-factory","exports"],
+ ["./content-kit-compiler/types/type","./content-kit-compiler/models/block","./content-kit-compiler/models/text","./content-kit-compiler/models/image","./content-kit-compiler/models/embed","./content-kit-compiler/compiler","./content-kit-compiler/parsers/html-parser","./content-kit-compiler/renderers/html-renderer","./content-kit-editor/editor/editor-factory","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) {
"use strict";
var Type = __dependency1__["default"];
@@ -39,6 +39,75 @@ define("content-kit",
__exports__["default"] = ContentKit;
});
+define("content-kit-compiler/compiler",
+ ["./parsers/html-parser","./renderers/html-renderer","./types/default-types","../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var HTMLParser = __dependency1__["default"];
+ var HTMLRenderer = __dependency2__["default"];
+ var DefaultBlockTypeSet = __dependency3__.DefaultBlockTypeSet;
+ var DefaultMarkupTypeSet = __dependency3__.DefaultMarkupTypeSet;
+ var mergeWithOptions = __dependency4__.mergeWithOptions;
+
+ /**
+ * @class Compiler
+ * @constructor
+ * @param options
+ */
+ function Compiler(options) {
+ var parser = new HTMLParser();
+ var renderer = new HTMLRenderer();
+ var defaults = {
+ parser : parser,
+ renderer : renderer,
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ includeTypeNames : false // true will output type_name: 'TEXT' etc. when parsing for easier debugging
+ };
+ mergeWithOptions(this, defaults, options);
+
+ // Reference the compiler settings
+ parser.blockTypes = renderer.blockTypes = this.blockTypes;
+ parser.markupTypes = renderer.markupTypes = this.markupTypes;
+ parser.includeTypeNames = this.includeTypeNames;
+ }
+
+ /**
+ * @method parse
+ * @param input
+ * @return Object
+ */
+ Compiler.prototype.parse = function(input) {
+ return this.parser.parse(input);
+ };
+
+ /**
+ * @method render
+ * @param data
+ * @return Object
+ */
+ Compiler.prototype.render = function(data) {
+ return this.renderer.render(data);
+ };
+
+ /**
+ * @method registerBlockType
+ * @param {Type} type
+ */
+ Compiler.prototype.registerBlockType = function(type) {
+ return this.blockTypes.addType(type);
+ };
+
+ /**
+ * @method registerMarkupType
+ * @param {Type} type
+ */
+ Compiler.prototype.registerMarkupType = function(type) {
+ return this.markupTypes.addType(type);
+ };
+
+ __exports__["default"] = Compiler;
+ });
define("content-kit-editor/constants",
["exports"],
function(__exports__) {
@@ -92,616 +161,228 @@ define("content-kit-editor/constants",
__exports__.Tags = Tags;
__exports__.RootTags = RootTags;
});
-define("content-kit-editor/editor-factory",
- ["./editor","./commands/commands","./constants","../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+define("content-kit-utils/array-utils",
+ ["exports"],
+ function(__exports__) {
"use strict";
- var Editor = __dependency1__["default"];
- var TextFormatCommands = __dependency2__.TextFormatCommands;
- var EmbedCommands = __dependency2__.EmbedCommands;
- var Tags = __dependency3__.Tags;
- var merge = __dependency4__.merge;
-
- var defaults = {
- defaultFormatter: Tags.PARAGRAPH,
- placeholder: 'Write here...',
- spellcheck: true,
- autofocus: true,
- textFormatCommands: TextFormatCommands.all,
- embedCommands: EmbedCommands.all
- };
-
/**
- * Publically expose this class which sets up indiviual `Editor` classes
- * depending if user passes string selector, Node, or NodeList
+ * Converts an array-like object (i.e. NodeList) to Array
+ * Note: could just use Array.prototype.slice but does not work in IE <= 8
*/
- function EditorFactory(element, options) {
- var editors = [];
- var elements, elementsLen, i;
-
- if (typeof element === 'string') {
- elements = document.querySelectorAll(element);
- } else if (element && element.length) {
- elements = element;
- } else if (element) {
- elements = [element];
+ function toArray(obj) {
+ var array = [];
+ var i = obj && obj.length >>> 0; // cast to Uint32
+ while (i--) {
+ array[i] = obj[i];
}
+ return array;
+ }
- if (elements) {
- options = merge(defaults, options);
- elementsLen = elements.length;
- for (i = 0; i < elementsLen; i++) {
- editors.push(new Editor(elements[i], options));
+ /**
+ * Computes the sum of values in a (sparse) array
+ */
+ function sumSparseArray(array) {
+ var sum = 0, i;
+ for (i in array) { // 'for in' is better for sparse arrays
+ if (array.hasOwnProperty(i)) {
+ sum += array[i];
}
}
-
- return editors.length > 1 ? editors : editors[0];
+ return sum;
}
- __exports__["default"] = EditorFactory;
+ __exports__.toArray = toArray;
+ __exports__.sumSparseArray = sumSparseArray;
});
-define("content-kit-editor/editor-html-renderer",
- ["../content-kit-compiler/renderers/html-renderer","../content-kit-compiler/types/type","../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+define("content-kit-utils/node-utils",
+ ["./string-utils","./array-utils","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var HTMLRenderer = __dependency1__["default"];
- var Type = __dependency2__["default"];
- var inherit = __dependency3__.inherit;
-
- function embedRenderer(model) {
- var embedAttrs = model.attributes;
- var isVideo = embedAttrs.embed_type === 'video';
- return '' +
- '
' +
- '
';
- }
+ var sanitizeWhitespace = __dependency1__.sanitizeWhitespace;
+ var toArray = __dependency2__.toArray;
- function imageRenderer(model) {
- return '' +
- '' +
- '
';
- }
+ /**
+ * A document instance separate from the page's document. (if browser supports it)
+ * Prevents images, scripts, and styles from executing while parsing nodes.
+ */
+ var standaloneDocument = (function() {
+ var implementation = document.implementation;
+ var createHTMLDocument = implementation.createHTMLDocument;
- var typeRenderers = {};
- typeRenderers[Type.EMBED.id] = embedRenderer;
- typeRenderers[Type.IMAGE.id] = imageRenderer;
+ if (createHTMLDocument) {
+ return createHTMLDocument.call(implementation, '');
+ }
+ return document;
+ })();
/**
- * @class EditorHTMLRenderer
- * @constructor
- * Subclass of HTMLRenderer specifically for the Editor
- * Wraps interactive elements to add functionality
+ * document.createElement with our lean, standalone document
*/
- function EditorHTMLRenderer() {
- HTMLRenderer.call(this, {
- typeRenderers: typeRenderers
- });
+ function createElement(type) {
+ return standaloneDocument.createElement(type);
}
- inherit(EditorHTMLRenderer, HTMLRenderer);
- __exports__["default"] = EditorHTMLRenderer;
- });
-define("content-kit-editor/editor",
- ["./views/text-format-toolbar","./views/tooltip","./views/embed-intent","./commands/unordered-list","./commands/ordered-list","./commands/text-format","./constants","./utils/selection-utils","../content-kit-compiler/compiler","../content-kit-compiler/models/text","../content-kit-compiler/types/type","../content-kit-utils/array-utils","../content-kit-utils/object-utils","./editor-html-renderer","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) {
- "use strict";
- var TextFormatToolbar = __dependency1__["default"];
- var Tooltip = __dependency2__["default"];
- var EmbedIntent = __dependency3__["default"];
- var UnorderedListCommand = __dependency4__["default"];
- var OrderedListCommand = __dependency5__["default"];
- var TextFormatCommand = __dependency6__["default"];
- var Tags = __dependency7__.Tags;
- var RootTags = __dependency7__.RootTags;
- var Keycodes = __dependency7__.Keycodes;
- var RegEx = __dependency7__.RegEx;
- var moveCursorToBeginningOfSelection = __dependency8__.moveCursorToBeginningOfSelection;
- var getSelectionTagName = __dependency8__.getSelectionTagName;
- var getSelectionBlockElement = __dependency8__.getSelectionBlockElement;
- var getSelectionBlockTagName = __dependency8__.getSelectionBlockTagName;
- var Compiler = __dependency9__["default"];
- var TextModel = __dependency10__["default"];
- var Type = __dependency11__["default"];
- var toArray = __dependency12__.toArray;
- var merge = __dependency13__.merge;
- var EditorHTMLRenderer = __dependency14__["default"];
+ /**
+ * A reusable DOM Node for parsing html content.
+ */
+ var DOMParsingNode = createElement('div');
- var editorClassName = 'ck-editor';
- var editorClassNameRegExp = new RegExp(editorClassName);
+ /**
+ * Returns plain-text of a `Node`
+ */
+ function textOfNode(node) {
+ var text = node.textContent || node.innerText;
+ return text ? sanitizeWhitespace(text) : '';
+ }
- function plainTextToBlocks(plainText, blockTag) {
- var blocks = plainText.split(RegEx.NEWLINE),
- len = blocks.length,
- block, openTag, closeTag, content, i;
- if(len < 2) {
- return plainText;
- } else {
- content = '';
- openTag = '<' + blockTag + '>';
- closeTag = '' + blockTag + '>';
- for(i=0; i>> 0; // cast to Uint32
- while (i--) {
- array[i] = obj[i];
- }
- return array;
- }
-
- /**
- * Computes the sum of values in a (sparse) array
- */
- function sumSparseArray(array) {
- var sum = 0, i;
- for (i in array) { // 'for in' is better for sparse arrays
- if (array.hasOwnProperty(i)) {
- sum += array[i];
- }
- }
- return sum;
- }
-
- __exports__.toArray = toArray;
- __exports__.sumSparseArray = sumSparseArray;
- });
-define("content-kit-utils/node-utils",
- ["./string-utils","./array-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var sanitizeWhitespace = __dependency1__.sanitizeWhitespace;
- var toArray = __dependency2__.toArray;
-
- /**
- * A document instance separate from the page's document. (if browser supports it)
- * Prevents images, scripts, and styles from executing while parsing nodes.
- */
- var standaloneDocument = (function() {
- var implementation = document.implementation;
- var createHTMLDocument = implementation.createHTMLDocument;
-
- if (createHTMLDocument) {
- return createHTMLDocument.call(implementation, '');
- }
- return document;
- })();
-
- /**
- * document.createElement with our lean, standalone document
- */
- function createElement(type) {
- return standaloneDocument.createElement(type);
- }
-
- /**
- * A reusable DOM Node for parsing html content.
- */
- var DOMParsingNode = createElement('div');
-
- /**
- * Returns plain-text of a `Node`
- */
- function textOfNode(node) {
- var text = node.textContent || node.innerText;
- return text ? sanitizeWhitespace(text) : '';
- }
-
- /**
- * Replaces a `Node` with its children
- */
- function unwrapNode(node) {
- var children = toArray(node.childNodes);
- var len = children.length;
- var parent = node.parentNode, i;
- for (i = 0; i < len; i++) {
- parent.insertBefore(children[i], node);
- }
- }
-
- /**
- * Extracts attributes of a `Node` to a hash of key/value pairs
- */
- function attributesForNode(node /*,blacklist*/) {
- var attrs = node.attributes;
- var len = attrs && attrs.length;
- var i, attr, name, hash;
-
- for (i = 0; i < len; i++) {
- attr = attrs[i];
- name = attr.name;
- if (attr.specified) {
- //if (blacklist && name in blacklist)) { continue; }
- hash = hash || {};
- hash[name] = attr.value;
- }
- }
- return hash;
- }
-
- __exports__.createElement = createElement;
- __exports__.DOMParsingNode = DOMParsingNode;
- __exports__.textOfNode = textOfNode;
- __exports__.unwrapNode = unwrapNode;
- __exports__.attributesForNode = attributesForNode;
- });
-define("content-kit-utils/object-utils",
- ["exports"],
- function(__exports__) {
- "use strict";
- /**
- * Merges defaults/options into an Object
- * Useful for constructors
- */
- function mergeWithOptions(original, updates, options) {
- options = options || {};
- for(var prop in updates) {
- if (options.hasOwnProperty(prop)) {
- original[prop] = options[prop];
- } else if (updates.hasOwnProperty(prop)) {
- original[prop] = updates[prop];
- }
- }
- return original;
- }
-
- /**
- * Merges properties of one object into another
- */
- function merge(original, updates) {
- return mergeWithOptions(original, updates);
- }
-
- /**
- * Prototype inheritance helper
- */
- function inherit(Subclass, Superclass) {
- if (typeof Object.create === 'function') {
- Subclass._super = Superclass;
- Subclass.prototype = Object.create(Superclass.prototype, {
- constructor: {
- value: Subclass,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
- } else {
- for (var key in Superclass) {
- if (Superclass.hasOwnProperty(key)) {
- Subclass[key] = Superclass[key];
- }
- }
- Subclass.prototype = new Superclass();
- Subclass.constructor = Subclass;
- }
- }
-
- __exports__.mergeWithOptions = mergeWithOptions;
- __exports__.merge = merge;
- __exports__.inherit = inherit;
- });
-define("content-kit-utils/string-utils",
- ["exports"],
- function(__exports__) {
- "use strict";
- var RegExpTrim = /^\s+|\s+$/g;
- var RegExpTrimLeft = /^\s+/;
- var RegExpWSChars = /(\r\n|\n|\r|\t|\u00A0)/gm;
- var RegExpMultiWS = /\s+/g;
- var RegExpNonAlphaNum = /[^a-zA-Z\d]/g;
-
- /**
- * String.prototype.trim polyfill
- * Removes whitespace at beginning and end of string
- */
- function trim(string) {
- return string ? (string + '').replace(RegExpTrim, '') : '';
- }
-
- /**
- * String.prototype.trimLeft polyfill
- * Removes whitespace at beginning of string
- */
- function trimLeft(string) {
- return string ? (string + '').replace(RegExpTrimLeft, '') : '';
- }
-
- /**
- * Replaces non-alphanumeric chars with underscores
- */
- function underscore(string) {
- return string ? trim(string + '').replace(RegExpNonAlphaNum, '_') : '';
- }
-
- /**
- * Cleans line breaks, tabs, non-breaking spaces, then multiple occuring whitespaces.
- */
- function sanitizeWhitespace(string) {
- return string ? (string + '').replace(RegExpWSChars, '').replace(RegExpMultiWS, ' ') : '';
- }
-
- /**
- * Injects a string into another string at the index specified
- */
- function injectIntoString(string, injection, index) {
- return string.substr(0, index) + injection + string.substr(index);
- }
-
- __exports__.trim = trim;
- __exports__.trimLeft = trimLeft;
- __exports__.underscore = underscore;
- __exports__.sanitizeWhitespace = sanitizeWhitespace;
- __exports__.injectIntoString = injectIntoString;
- });
-define("ext/content-kit-services",
+define("ext/content-kit-services",
["exports"],
function(__exports__) {
"use strict";
@@ -954,190 +635,915 @@ define("ext/loader",
};
})();
});
-define("content-kit-editor/commands/base",
- ["exports"],
- function(__exports__) {
+define("content-kit-compiler/models/block",
+ ["./model","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
- function Command(options) {
- var command = this;
- var name = options.name;
- var prompt = options.prompt;
- command.name = name;
- command.button = options.button || name;
- command.editorContext = null;
- if (prompt) { command.prompt = prompt; }
+ var Model = __dependency1__["default"];
+
+ /**
+ * Ensures block markups at the same index are always in a specific order.
+ * For example, so all bold links are consistently marked up
+ * as text instead of text
+ */
+ function sortBlockMarkups(markups) {
+ return markups.sort(function(a, b) {
+ if (a.start === b.start && a.end === b.end) {
+ return b.type - a.type;
+ }
+ return 0;
+ });
}
- Command.prototype.exec = function(){};
+ /**
+ * @class BlockModel
+ * @constructor
+ * @extends Model
+ */
+ function BlockModel(options) {
+ options = options || {};
+ Model.call(this, options);
+ this.value = options.value || '';
+ this.markup = sortBlockMarkups(options.markup || []);
+ }
- __exports__["default"] = Command;
+ __exports__["default"] = BlockModel;
});
-define("content-kit-editor/commands/bold",
- ["./text-format","../../content-kit-utils/object-utils","../constants","../utils/selection-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+define("content-kit-compiler/models/embed",
+ ["../models/model","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var TextFormatCommand = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
- var Tags = __dependency3__.Tags;
- var RegEx = __dependency3__.RegEx;
- var getSelectionBlockTagName = __dependency4__.getSelectionBlockTagName;
+ var Model = __dependency1__["default"];
+ var Type = __dependency2__["default"];
- function BoldCommand() {
- TextFormatCommand.call(this, {
- name: 'bold',
- tag: Tags.BOLD,
- button: ''
+ /**
+ * @class EmbedModel
+ * @constructor
+ * @extends Model
+ * Massages data from an oEmbed response into an EmbedModel
+ */
+ function EmbedModel(options) {
+ if (!options) { return null; }
+
+ Model.call(this, {
+ type: Type.EMBED.id,
+ type_name: Type.EMBED.name,
+ attributes: {}
});
- }
- inherit(BoldCommand, TextFormatCommand);
- BoldCommand.prototype.exec = function() {
- // Don't allow executing bold command on heading tags
- if (!RegEx.HEADING_TAG.test(getSelectionBlockTagName())) {
- BoldCommand._super.prototype.exec.call(this);
+ var attributes = this.attributes;
+ var embedType = options.type;
+ var providerName = options.provider_name;
+ var embedUrl = options.url;
+ var embedTitle = options.title;
+ var embedThumbnail = options.thumbnail_url;
+ var embedHtml = options.html;
+
+ if (embedType) { attributes.embed_type = embedType; }
+ if (providerName) { attributes.provider_name = providerName; }
+ if (embedUrl) { attributes.url = embedUrl; }
+ if (embedTitle) { attributes.title = embedTitle; }
+
+ if (embedType === 'photo') {
+ attributes.thumbnail = options.media_url || embedUrl;
+ } else if (embedThumbnail) {
+ attributes.thumbnail = embedThumbnail;
}
- };
- __exports__["default"] = BoldCommand;
+ if (embedHtml && embedType === 'rich') {
+ attributes.html = embedHtml;
+ }
+ }
+
+ __exports__["default"] = EmbedModel;
});
-define("content-kit-editor/commands/commands",
- ["./bold","./italic","./link","./quote","./heading","./subheading","./image","./embed","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) {
+define("content-kit-compiler/models/image",
+ ["./block","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- // TODO: eliminate this file
- var BoldCommand = __dependency1__["default"];
- var ItalicCommand = __dependency2__["default"];
- var LinkCommand = __dependency3__["default"];
- var QuoteCommand = __dependency4__["default"];
- var HeadingCommand = __dependency5__["default"];
- var SubheadingCommand = __dependency6__["default"];
- var ImageCommand = __dependency7__["default"];
- var EmbedCommand = __dependency8__["default"];
+ var BlockModel = __dependency1__["default"];
+ var Type = __dependency2__["default"];
- function createCommandIndex(commands) {
- var index = {};
- var len = commands.length, i, command;
- for(i = 0; i < len; i++) {
- command = commands[i];
- index[command.name] = command;
+ /**
+ * @class ImageModel
+ * @constructor
+ * @extends BlockModel
+ * A simple BlockModel subclass representing an image
+ */
+ function ImageModel(options) {
+ options = options || {};
+ options.type = Type.IMAGE.id;
+ options.type_name = Type.IMAGE.name;
+ if (options.src) {
+ options.attributes = { src: options.src };
}
- return index;
+ BlockModel.call(this, options);
}
- var TextFormatCommands = {};
- TextFormatCommands.all = [
- new BoldCommand(),
- new ItalicCommand(),
- new LinkCommand(),
- new QuoteCommand(),
- new HeadingCommand(),
- new SubheadingCommand()
- ];
-
- TextFormatCommands.index = createCommandIndex(TextFormatCommands.all);
-
- var EmbedCommands = {};
- EmbedCommands.all = [
- new ImageCommand(),
- new EmbedCommand()
- ];
- EmbedCommands.index = createCommandIndex(EmbedCommands.all);
-
- __exports__.TextFormatCommands = TextFormatCommands;
- __exports__.EmbedCommands = EmbedCommands;
+ __exports__["default"] = ImageModel;
});
-define("content-kit-editor/commands/embed",
- ["./base","../views/prompt","../views/message","../../content-kit-compiler/models/embed","../../content-kit-utils/object-utils","../constants","../../ext/content-kit-services","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
+define("content-kit-compiler/models/markup",
+ ["./model","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
- var Command = __dependency1__["default"];
- var Prompt = __dependency2__["default"];
- var Message = __dependency3__["default"];
- var EmbedModel = __dependency4__["default"];
- var inherit = __dependency5__.inherit;
- var RegEx = __dependency6__.RegEx;
- var OEmbedder = __dependency7__.OEmbedder;
+ var Model = __dependency1__["default"];
- function loadTwitterWidgets(element) {
- if (window.twttr) {
- window.twttr.widgets.load(element);
- } else {
- var script = document.createElement('script');
- script.async = true;
- script.src = 'http://platform.twitter.com/widgets.js';
- document.head.appendChild(script);
- }
+ /**
+ * @class MarkupModel
+ * @constructor
+ * @extends Model
+ */
+ function MarkupModel(options) {
+ options = options || {};
+ Model.call(this, options);
+ this.start = options.start || 0;
+ this.end = options.end || 0;
}
- function EmbedCommand(options) {
- Command.call(this, {
- name: 'embed',
- button: '',
- prompt: new Prompt({
- command: this,
- placeholder: 'Paste a YouTube or Twitter url...'
- })
- });
+ __exports__["default"] = MarkupModel;
+ });
+define("content-kit-compiler/models/model",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ /**
+ * @class Model
+ * @constructor
+ * @private
+ */
+ function Model(options) {
+ options = options || {};
+ var type_name = options.type_name;
+ var attributes = options.attributes;
- this.embedService = new OEmbedder({ url: '/embed' });
+ this.type = options.type || null;
+ if (type_name) {
+ this.type_name = type_name;
+ }
+ if (attributes) {
+ this.attributes = attributes;
+ }
}
- inherit(EmbedCommand, Command);
-
- EmbedCommand.prototype.exec = function(url) {
- var command = this;
- var editorContext = command.editorContext;
- var embedIntent = command.embedIntent;
- var index = editorContext.getCurrentBlockIndex();
-
- embedIntent.showLoading();
- this.embedService.fetch({
- url: url,
- complete: function(response, error) {
- embedIntent.hideLoading();
- if (error) {
- var errorMsg = error;
- if (error.target && error.target.status === 0) {
- errorMsg = 'Could not connect to embed service';
- } else if (typeof error !== 'string') {
- errorMsg = 'Embed error';
- }
- new Message().show(errorMsg);
- } else {
- var embedModel = new EmbedModel(response);
- editorContext.insertBlockAt(embedModel, index);
- editorContext.syncVisualAt(index);
- if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
- loadTwitterWidgets(editorContext.element);
- }
- }
- }
- });
- };
- __exports__["default"] = EmbedCommand;
+ __exports__["default"] = Model;
});
-define("content-kit-editor/commands/format-block",
- ["./text-format","../constants","../../content-kit-utils/object-utils","../utils/selection-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+define("content-kit-compiler/models/text",
+ ["./block","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var TextFormatCommand = __dependency1__["default"];
- var Tags = __dependency2__.Tags;
- var inherit = __dependency3__.inherit;
- var getSelectionBlockElement = __dependency4__.getSelectionBlockElement;
- var selectNode = __dependency4__.selectNode;
+ var BlockModel = __dependency1__["default"];
+ var Type = __dependency2__["default"];
- function FormatBlockCommand(options) {
- options.action = 'formatBlock';
- TextFormatCommand.call(this, options);
+ /**
+ * @class TextModel
+ * @constructor
+ * @extends BlockModel
+ * A simple BlockModel subclass representing a paragraph of text
+ */
+ function TextModel(options) {
+ options = options || {};
+ options.type = Type.TEXT.id;
+ options.type_name = Type.TEXT.name;
+ BlockModel.call(this, options);
}
- inherit(FormatBlockCommand, TextFormatCommand);
- FormatBlockCommand.prototype.exec = function() {
- var tag = this.tag;
- // Brackets neccessary for certain browsers
- var value = '<' + tag + '>';
- var blockElement = getSelectionBlockElement();
- // Allow block commands to be toggled back to a paragraph
+ __exports__["default"] = TextModel;
+ });
+define("content-kit-compiler/parsers/html-parser",
+ ["../models/block","../models/markup","../types/default-types","../../content-kit-utils/object-utils","../../content-kit-utils/array-utils","../../content-kit-utils/string-utils","../../content-kit-utils/node-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
+ "use strict";
+ var BlockModel = __dependency1__["default"];
+ var MarkupModel = __dependency2__["default"];
+ var DefaultBlockTypeSet = __dependency3__.DefaultBlockTypeSet;
+ var DefaultMarkupTypeSet = __dependency3__.DefaultMarkupTypeSet;
+ var mergeWithOptions = __dependency4__.mergeWithOptions;
+ var toArray = __dependency5__.toArray;
+ var trim = __dependency6__.trim;
+ var trimLeft = __dependency6__.trimLeft;
+ var sanitizeWhitespace = __dependency6__.sanitizeWhitespace;
+ var createElement = __dependency7__.createElement;
+ var DOMParsingNode = __dependency7__.DOMParsingNode;
+ var textOfNode = __dependency7__.textOfNode;
+ var unwrapNode = __dependency7__.unwrapNode;
+ var attributesForNode = __dependency7__.attributesForNode;
+
+ /**
+ * Gets the last block in the set or creates and return a default block if none exist yet.
+ */
+ function getLastBlockOrCreate(parser, blocks) {
+ var block;
+ if (blocks.length) {
+ block = blocks[blocks.length - 1];
+ } else {
+ block = parser.parseBlock(createElement(DefaultBlockTypeSet.TEXT.tag));
+ blocks.push(block);
+ }
+ return block;
+ }
+
+ /**
+ * Helper to retain stray elements at the root of the html that aren't blocks
+ */
+ function handleNonBlockElementAtRoot(parser, elementNode, blocks) {
+ var block = getLastBlockOrCreate(parser, blocks),
+ markup = parser.parseElementMarkup(elementNode, block.value.length);
+ if (markup) {
+ block.markup.push(markup);
+ }
+ block.value += textOfNode(elementNode);
+ }
+
+ /**
+ * @class HTMLParser
+ * @constructor
+ */
+ function HTMLParser(options) {
+ var defaults = {
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ includeTypeNames : false
+ };
+ mergeWithOptions(this, defaults, options);
+ }
+
+ /**
+ * @method parse
+ * @param html String of HTML content
+ * @return Array Parsed JSON content array
+ */
+ HTMLParser.prototype.parse = function(html) {
+ DOMParsingNode.innerHTML = sanitizeWhitespace(html);
+
+ var children = toArray(DOMParsingNode.childNodes),
+ len = children.length,
+ blocks = [],
+ i, currentNode, block, text;
+
+ for (i = 0; i < len; i++) {
+ currentNode = children[i];
+ // All top level nodes *should be* `Element` nodes and supported block types.
+ // We'll handle some cases if it isn't so we don't lose any content when parsing.
+ // Parser assumes sane input (such as from the ContentKit Editor) and is not intended to be a full html sanitizer.
+ if (currentNode.nodeType === 1) {
+ block = this.parseBlock(currentNode);
+ if (block) {
+ blocks.push(block);
+ } else {
+ handleNonBlockElementAtRoot(this, currentNode, blocks);
+ }
+ } else if (currentNode.nodeType === 3) {
+ text = currentNode.nodeValue;
+ if (trim(text)) {
+ block = getLastBlockOrCreate(this, blocks);
+ block.value += text;
+ }
+ }
+ }
+
+ return blocks;
+ };
+
+ /**
+ * @method parseBlock
+ * @param node DOM node to parse
+ * @return {BlockModel} parsed block model
+ * Parses a single block type node into a model
+ */
+ HTMLParser.prototype.parseBlock = function(node) {
+ var type = this.blockTypes.findByNode(node);
+ if (type) {
+ return new BlockModel({
+ type : type.id,
+ type_name : this.includeTypeNames && type.name,
+ value : trim(textOfNode(node)),
+ attributes : attributesForNode(node),
+ markup : this.parseBlockMarkup(node)
+ });
+ }
+ };
+
+ /**
+ * @method parseBlockMarkup
+ * @param node DOM node to parse
+ * @return {Array} parsed markups
+ * Parses a single block type node's markup
+ */
+ HTMLParser.prototype.parseBlockMarkup = function(node) {
+ var processedText = '',
+ markups = [],
+ index = 0,
+ currentNode, markup;
+
+ // Clone the node since it will be recursively torn down
+ node = node.cloneNode(true);
+
+ while (node.hasChildNodes()) {
+ currentNode = node.firstChild;
+ if (currentNode.nodeType === 1) {
+ markup = this.parseElementMarkup(currentNode, processedText.length);
+ if (markup) {
+ markups.push(markup);
+ }
+ // unwrap the element so we can process any children
+ if (currentNode.hasChildNodes()) {
+ unwrapNode(currentNode);
+ }
+ } else if (currentNode.nodeType === 3) {
+ var text = sanitizeWhitespace(currentNode.nodeValue);
+ if (index === 0) { text = trimLeft(text); }
+ if (text) { processedText += text; }
+ }
+
+ // node has been processed, remove it
+ currentNode.parentNode.removeChild(currentNode);
+ index++;
+ }
+
+ return markups;
+ };
+
+ /**
+ * @method parseElementMarkup
+ * @param node DOM node to parse
+ * @param startIndex DOM node to parse
+ * @return {MarkupModel} parsed markup model
+ * Parses markup of a single html element node
+ */
+ HTMLParser.prototype.parseElementMarkup = function(node, startIndex) {
+ var type = this.markupTypes.findByNode(node),
+ selfClosing, endIndex;
+
+ if (type) {
+ selfClosing = type.selfClosing;
+ if (!selfClosing && !node.hasChildNodes()) { return; } // check for empty nodes
+
+ endIndex = startIndex + (selfClosing ? 0 : textOfNode(node).length);
+ if (endIndex > startIndex || (selfClosing && endIndex === startIndex)) { // check for empty nodes
+ return new MarkupModel({
+ type : type.id,
+ type_name : this.includeTypeNames && type.name,
+ start : startIndex,
+ end : endIndex,
+ attributes : attributesForNode(node)
+ });
+ }
+ }
+ };
+
+ __exports__["default"] = HTMLParser;
+ });
+define("content-kit-compiler/renderers/html-element-renderer",
+ ["../../content-kit-utils/string-utils","../../content-kit-utils/array-utils","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+ var injectIntoString = __dependency1__.injectIntoString;
+ var sumSparseArray = __dependency2__.sumSparseArray;
+
+ /**
+ * Builds an opening html tag. i.e. ''
+ */
+ function createOpeningTag(tagName, attributes, selfClosing /*,blacklist*/) {
+ var tag = '<' + tagName;
+ for (var attr in attributes) {
+ if (attributes.hasOwnProperty(attr)) {
+ //if (blacklist && attr in blacklist) { continue; }
+ tag += ' ' + attr + '="' + attributes[attr] + '"';
+ }
+ }
+ if (selfClosing) { tag += '/'; }
+ tag += '>';
+ return tag;
+ }
+
+ /**
+ * Builds a closing html tag. i.e. '
'
+ */
+ function createCloseTag(tagName) {
+ return '' + tagName + '>';
+ }
+
+ /**
+ * @class HTMLElementRenderer
+ * @constructor
+ */
+ function HTMLElementRenderer(options) {
+ options = options || {};
+ this.type = options.type;
+ this.markupTypes = options.markupTypes;
+ }
+
+ /**
+ * @method render
+ * @param model a block model
+ * @return String html
+ * Renders a block model into a HTML string.
+ */
+ HTMLElementRenderer.prototype.render = function(model) {
+ var html = '';
+ var type = this.type;
+ var tagName = type.tag;
+ var selfClosing = type.selfClosing;
+
+ if (tagName) {
+ html += createOpeningTag(tagName, model.attributes, selfClosing);
+ }
+ if (!selfClosing) {
+ html += this.renderMarkup(model.value, model.markup);
+ if (tagName) {
+ html += createCloseTag(tagName);
+ }
+ }
+ return html;
+ };
+
+ /**
+ * @method renderMarkup
+ * @param text plain text to apply markup to
+ * @param markup an array of markup models
+ * @return String html
+ * Renders a markup model into a HTML string.
+ */
+ HTMLElementRenderer.prototype.renderMarkup = function(text, markups) {
+ var parsedTagsIndexes = [],
+ len = markups && markups.length, i;
+
+ for (i = 0; i < len; i++) {
+ var markup = markups[i],
+ markupMeta = this.markupTypes.findById(markup.type),
+ tagName = markupMeta.tag,
+ selfClosing = markupMeta.selfClosing,
+ start = markup.start,
+ end = markup.end,
+ openTag = createOpeningTag(tagName, markup.attributes, selfClosing),
+ parsedTagLengthAtIndex = parsedTagsIndexes[start] || 0,
+ parsedTagLengthBeforeIndex = sumSparseArray(parsedTagsIndexes.slice(0, start + 1));
+
+ text = injectIntoString(text, openTag, start + parsedTagLengthBeforeIndex);
+ parsedTagsIndexes[start] = parsedTagLengthAtIndex + openTag.length;
+
+ if (!selfClosing) {
+ var closeTag = createCloseTag(tagName);
+ parsedTagLengthAtIndex = parsedTagsIndexes[end] || 0;
+ parsedTagLengthBeforeIndex = sumSparseArray(parsedTagsIndexes.slice(0, end));
+ text = injectIntoString(text, closeTag, end + parsedTagLengthBeforeIndex);
+ parsedTagsIndexes[end] = parsedTagLengthAtIndex + closeTag.length;
+ }
+ }
+
+ return text;
+ };
+
+ __exports__["default"] = HTMLElementRenderer;
+ });
+define("content-kit-compiler/renderers/html-embed-renderer",
+ ["./embeds/youtube","./embeds/twitter","./embeds/instagram","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var YouTubeRenderer = __dependency1__["default"];
+ var TwitterRenderer = __dependency2__["default"];
+ var InstagramRenderer = __dependency3__["default"];
+
+ /**
+ * A dictionary of supported embed services
+ */
+ var services = {
+ YOUTUBE : {
+ id: 1,
+ renderer: new YouTubeRenderer()
+ },
+ TWITTER : {
+ id: 2,
+ renderer: new TwitterRenderer()
+ },
+ INSTAGRAM : {
+ id: 3,
+ renderer: new InstagramRenderer()
+ }
+ };
+
+ /**
+ * @class EmbedRenderer
+ * @constructor
+ */
+ function EmbedRenderer() {}
+
+ /**
+ * @method render
+ * @param model
+ * @return String html
+ */
+ EmbedRenderer.prototype.render = function(model) {
+ var renderer = this.rendererFor(model);
+ if (renderer) {
+ return renderer.render(model);
+ }
+ var attrs = model.attributes;
+ return attrs && attrs.html || '';
+ };
+
+ /**
+ * @method rendererFor
+ * @param model
+ * @return service renderer
+ */
+ EmbedRenderer.prototype.rendererFor = function(model) {
+ var provider = model.attributes.provider_name;
+ var providerKey = provider && provider.toUpperCase();
+ var service = services[providerKey];
+ return service && service.renderer;
+ };
+
+ __exports__["default"] = EmbedRenderer;
+ });
+define("content-kit-compiler/renderers/html-renderer",
+ ["../types/type","./html-element-renderer","./html-embed-renderer","../types/default-types","../../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
+ var Type = __dependency1__["default"];
+ var HTMLElementRenderer = __dependency2__["default"];
+ var HTMLEmbedRenderer = __dependency3__["default"];
+ var DefaultBlockTypeSet = __dependency4__.DefaultBlockTypeSet;
+ var DefaultMarkupTypeSet = __dependency4__.DefaultMarkupTypeSet;
+ var mergeWithOptions = __dependency5__.mergeWithOptions;
+
+ /**
+ * @class HTMLRenderer
+ * @constructor
+ */
+ function HTMLRenderer(options) {
+ var defaults = {
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ typeRenderers : {}
+ };
+ mergeWithOptions(this, defaults, options);
+ }
+
+ /**
+ * @method willRenderType
+ * @param type {Number|Type}
+ * @param renderer the rendering function that returns a string of html
+ * Registers custom rendering hooks for a type
+ */
+ HTMLRenderer.prototype.willRenderType = function(type, renderer) {
+ if ('number' !== typeof type) {
+ type = type.id;
+ }
+ this.typeRenderers[type] = renderer;
+ };
+
+ /**
+ * @method rendererFor
+ * @param model
+ * @returns renderer
+ * Returns an instance of a renderer for supplied model
+ */
+ HTMLRenderer.prototype.rendererFor = function(model) {
+ var type = this.blockTypes.findById(model.type);
+ if (type === Type.EMBED) {
+ return new HTMLEmbedRenderer();
+ }
+ return new HTMLElementRenderer({ type: type, markupTypes: this.markupTypes });
+ };
+
+ /**
+ * @method render
+ * @param model
+ * @return String html
+ */
+ HTMLRenderer.prototype.render = function(model) {
+ var html = '';
+ var len = model && model.length;
+ var i, item, renderer, renderHook, itemHtml;
+
+ for (i = 0; i < len; i++) {
+ item = model[i];
+ renderer = this.rendererFor(item);
+ renderHook = this.typeRenderers[item.type];
+ itemHtml = renderHook ? renderHook.call(renderer, item) : renderer.render(item);
+ if (itemHtml) { html += itemHtml; }
+ }
+ return html;
+ };
+
+ __exports__["default"] = HTMLRenderer;
+ });
+define("content-kit-compiler/types/default-types",
+ ["./type-set","./type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+ var TypeSet = __dependency1__["default"];
+ var Type = __dependency2__["default"];
+
+ /**
+ * Default supported block types
+ */
+ var DefaultBlockTypeSet = new TypeSet([
+ new Type({ tag: 'p', name: 'text' }),
+ new Type({ tag: 'h2', name: 'heading' }),
+ new Type({ tag: 'h3', name: 'subheading' }),
+ new Type({ tag: 'img', name: 'image' }),
+ new Type({ tag: 'blockquote', name: 'quote' }),
+ new Type({ tag: 'ul', name: 'list' }),
+ new Type({ tag: 'ol', name: 'ordered list' }),
+ new Type({ name: 'embed' })
+ ]);
+
+ /**
+ * Default supported markup types
+ */
+ var DefaultMarkupTypeSet = new TypeSet([
+ new Type({ tag: 'b', name: 'bold' }),
+ new Type({ tag: 'i', name: 'italic' }),
+ new Type({ tag: 'u', name: 'underline' }),
+ new Type({ tag: 'a', name: 'link' }),
+ new Type({ tag: 'br', name: 'break' }),
+ new Type({ tag: 'li', name: 'list item' }),
+ new Type({ tag: 'sub', name: 'subscript' }),
+ new Type({ tag: 'sup', name: 'superscript' })
+ ]);
+
+ __exports__.DefaultBlockTypeSet = DefaultBlockTypeSet;
+ __exports__.DefaultMarkupTypeSet = DefaultMarkupTypeSet;
+ });
+define("content-kit-compiler/types/type-set",
+ ["./type","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var Type = __dependency1__["default"];
+
+ /**
+ * @class TypeSet
+ * @private
+ * @constructor
+ * A Set of Types
+ */
+ function TypeSet(types) {
+ var len = types && types.length, i;
+
+ this._autoId = 1; // Auto-increment id counter
+ this.idLookup = {}; // Hash cache for finding by id
+ this.tagLookup = {}; // Hash cache for finding by tag
+
+ for (i = 0; i < len; i++) {
+ this.addType(types[i]);
+ }
+ }
+
+ TypeSet.prototype = {
+ /**
+ * Adds a type to the set
+ */
+ addType: function(type) {
+ if (type instanceof Type) {
+ this[type.name] = type;
+ if (type.id === undefined) {
+ type.id = this._autoId++;
+ }
+ this.idLookup[type.id] = type;
+ if (type.tag) {
+ this.tagLookup[type.tag] = type;
+ }
+ return type;
+ }
+ },
+
+ /**
+ * Returns type info for a given Node
+ */
+ findByNode: function(node) {
+ return this.findByTag(node.tagName);
+ },
+ /**
+ * Returns type info for a given tag
+ */
+ findByTag: function(tag) {
+ return this.tagLookup[tag.toLowerCase()];
+ },
+ /**
+ * Returns type info for a given id
+ */
+ findById: function(id) {
+ return this.idLookup[id];
+ }
+ };
+
+ __exports__["default"] = TypeSet;
+ });
+define("content-kit-compiler/types/type",
+ ["../../content-kit-utils/string-utils","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var underscore = __dependency1__.underscore;
+
+ /**
+ * @class Type
+ * @constructor
+ * Contains meta info about a node type (id, name, tag, etc).
+ */
+ function Type(options) {
+ if (options) {
+ this.name = underscore(options.name || options.tag).toUpperCase();
+ if (options.id !== undefined) {
+ this.id = options.id;
+ }
+ if (options.tag) {
+ this.tag = options.tag.toLowerCase();
+ this.selfClosing = /^(br|img|hr|meta|link|embed)$/i.test(this.tag);
+ }
+
+ // Register the type as constant
+ Type[this.name] = this;
+ }
+ }
+
+ __exports__["default"] = Type;
+ });
+define("content-kit-editor/commands/base",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ function Command(options) {
+ var command = this;
+ var name = options.name;
+ var prompt = options.prompt;
+ command.name = name;
+ command.button = options.button || name;
+ command.editorContext = null;
+ if (prompt) { command.prompt = prompt; }
+ }
+
+ Command.prototype.exec = function(){};
+
+ __exports__["default"] = Command;
+ });
+define("content-kit-editor/commands/bold",
+ ["./text-format","../../content-kit-utils/object-utils","../constants","../utils/selection-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var TextFormatCommand = __dependency1__["default"];
+ var inherit = __dependency2__.inherit;
+ var Tags = __dependency3__.Tags;
+ var RegEx = __dependency3__.RegEx;
+ var getSelectionBlockTagName = __dependency4__.getSelectionBlockTagName;
+
+ function BoldCommand() {
+ TextFormatCommand.call(this, {
+ name: 'bold',
+ tag: Tags.BOLD,
+ button: ''
+ });
+ }
+ inherit(BoldCommand, TextFormatCommand);
+
+ BoldCommand.prototype.exec = function() {
+ // Don't allow executing bold command on heading tags
+ if (!RegEx.HEADING_TAG.test(getSelectionBlockTagName())) {
+ BoldCommand._super.prototype.exec.call(this);
+ }
+ };
+
+ __exports__["default"] = BoldCommand;
+ });
+define("content-kit-editor/commands/commands",
+ ["./bold","./italic","./link","./quote","./heading","./subheading","./image","./embed","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) {
+ "use strict";
+ // TODO: eliminate this file
+ var BoldCommand = __dependency1__["default"];
+ var ItalicCommand = __dependency2__["default"];
+ var LinkCommand = __dependency3__["default"];
+ var QuoteCommand = __dependency4__["default"];
+ var HeadingCommand = __dependency5__["default"];
+ var SubheadingCommand = __dependency6__["default"];
+ var ImageCommand = __dependency7__["default"];
+ var EmbedCommand = __dependency8__["default"];
+
+ function createCommandIndex(commands) {
+ var index = {};
+ var len = commands.length, i, command;
+ for(i = 0; i < len; i++) {
+ command = commands[i];
+ index[command.name] = command;
+ }
+ return index;
+ }
+
+ var TextFormatCommands = {};
+ TextFormatCommands.all = [
+ new BoldCommand(),
+ new ItalicCommand(),
+ new LinkCommand(),
+ new QuoteCommand(),
+ new HeadingCommand(),
+ new SubheadingCommand()
+ ];
+
+ TextFormatCommands.index = createCommandIndex(TextFormatCommands.all);
+
+ var EmbedCommands = {};
+ EmbedCommands.all = [
+ new ImageCommand(),
+ new EmbedCommand()
+ ];
+ EmbedCommands.index = createCommandIndex(EmbedCommands.all);
+
+ __exports__.TextFormatCommands = TextFormatCommands;
+ __exports__.EmbedCommands = EmbedCommands;
+ });
+define("content-kit-editor/commands/embed",
+ ["./base","../views/prompt","../views/message","../../content-kit-compiler/models/embed","../../content-kit-utils/object-utils","../constants","../../ext/content-kit-services","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
+ "use strict";
+ var Command = __dependency1__["default"];
+ var Prompt = __dependency2__["default"];
+ var Message = __dependency3__["default"];
+ var EmbedModel = __dependency4__["default"];
+ var inherit = __dependency5__.inherit;
+ var RegEx = __dependency6__.RegEx;
+ var OEmbedder = __dependency7__.OEmbedder;
+
+ function loadTwitterWidgets(element) {
+ if (window.twttr) {
+ window.twttr.widgets.load(element);
+ } else {
+ var script = document.createElement('script');
+ script.async = true;
+ script.src = 'http://platform.twitter.com/widgets.js';
+ document.head.appendChild(script);
+ }
+ }
+
+ function EmbedCommand(options) {
+ Command.call(this, {
+ name: 'embed',
+ button: '',
+ prompt: new Prompt({
+ command: this,
+ placeholder: 'Paste a YouTube or Twitter url...'
+ })
+ });
+
+ this.embedService = new OEmbedder({ url: '/embed' });
+ }
+ inherit(EmbedCommand, Command);
+
+ EmbedCommand.prototype.exec = function(url) {
+ var command = this;
+ var editorContext = command.editorContext;
+ var embedIntent = command.embedIntent;
+ var index = editorContext.getCurrentBlockIndex();
+
+ embedIntent.showLoading();
+ this.embedService.fetch({
+ url: url,
+ complete: function(response, error) {
+ embedIntent.hideLoading();
+ if (error) {
+ var errorMsg = error;
+ if (error.target && error.target.status === 0) {
+ errorMsg = 'Could not connect to embed service';
+ } else if (typeof error !== 'string') {
+ errorMsg = 'Embed error';
+ }
+ new Message().show(errorMsg);
+ } else {
+ var embedModel = new EmbedModel(response);
+ editorContext.insertBlockAt(embedModel, index);
+ editorContext.syncVisualAt(index);
+ if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
+ loadTwitterWidgets(editorContext.element);
+ }
+ }
+ }
+ });
+ };
+
+ __exports__["default"] = EmbedCommand;
+ });
+define("content-kit-editor/commands/format-block",
+ ["./text-format","../constants","../../content-kit-utils/object-utils","../utils/selection-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var TextFormatCommand = __dependency1__["default"];
+ var Tags = __dependency2__.Tags;
+ var inherit = __dependency3__.inherit;
+ var getSelectionBlockElement = __dependency4__.getSelectionBlockElement;
+ var selectNode = __dependency4__.selectNode;
+
+ function FormatBlockCommand(options) {
+ options.action = 'formatBlock';
+ TextFormatCommand.call(this, options);
+ }
+ inherit(FormatBlockCommand, TextFormatCommand);
+
+ FormatBlockCommand.prototype.exec = function() {
+ var tag = this.tag;
+ // Brackets neccessary for certain browsers
+ var value = '<' + tag + '>';
+ var blockElement = getSelectionBlockElement();
+ // Allow block commands to be toggled back to a paragraph
if(tag === blockElement.tagName) {
value = Tags.PARAGRAPH;
} else {
@@ -1394,1560 +1800,1174 @@ define("content-kit-editor/commands/text-format",
function TextFormatCommand(options) {
Command.call(this, options);
this.tag = options.tag.toUpperCase();
- this.action = options.action || this.name;
- this.removeAction = options.removeAction || this.action;
- }
- inherit(TextFormatCommand, Command);
-
- TextFormatCommand.prototype = {
- exec: function(value) {
- document.execCommand(this.action, false, value || null);
- },
- unexec: function(value) {
- document.execCommand(this.removeAction, false, value || null);
- }
- };
-
- __exports__["default"] = TextFormatCommand;
- });
-define("content-kit-editor/commands/unordered-list",
- ["./list","../constants","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- var ListCommand = __dependency1__["default"];
- var Tags = __dependency2__.Tags;
- var inherit = __dependency3__.inherit;
-
- function UnorderedListCommand() {
- ListCommand.call(this, {
- name: 'list',
- tag: Tags.LIST,
- action: 'insertUnorderedList'
- });
- }
- inherit(UnorderedListCommand, ListCommand);
-
- __exports__["default"] = UnorderedListCommand;
- });
-define("content-kit-editor/utils/element-utils",
- ["exports"],
- function(__exports__) {
- "use strict";
- function createDiv(className) {
- var div = document.createElement('div');
- if (className) {
- div.className = className;
- }
- return div;
- }
-
- function hideElement(element) {
- element.style.display = 'none';
- }
-
- function showElement(element) {
- element.style.display = 'block';
- }
-
- function swapElements(elementToShow, elementToHide) {
- hideElement(elementToHide);
- showElement(elementToShow);
- }
-
- function getEventTargetMatchingTag(tag, target, container) {
- // Traverses up DOM from an event target to find the node matching specifed tag
- while (target && target !== container) {
- if (target.tagName === tag) {
- return target;
- }
- target = target.parentNode;
- }
- }
-
- function nodeIsDescendantOfElement(node, element) {
- var parentNode = node.parentNode;
- while(parentNode) {
- if (parentNode === element) {
- return true;
- }
- parentNode = parentNode.parentNode;
- }
- return false;
- }
-
- function getElementRelativeOffset(element) {
- var offset = { left: 0, top: -window.pageYOffset };
- var offsetParent = element.offsetParent;
- var offsetParentPosition = window.getComputedStyle(offsetParent).position;
- var offsetParentRect;
-
- if (offsetParentPosition === 'relative') {
- offsetParentRect = offsetParent.getBoundingClientRect();
- offset.left = offsetParentRect.left;
- offset.top = offsetParentRect.top;
- }
- return offset;
- }
-
- function getElementComputedStyleNumericProp(element, prop) {
- return parseFloat(window.getComputedStyle(element)[prop]);
- }
-
- function positionElementToRect(element, rect, topOffset, leftOffset) {
- var relativeOffset = getElementRelativeOffset(element);
- var style = element.style;
- var round = Math.round;
-
- topOffset = topOffset || 0;
- leftOffset = leftOffset || 0;
- style.left = round(rect.left - relativeOffset.left - leftOffset) + 'px';
- style.top = round(rect.top - relativeOffset.top - topOffset) + 'px';
- }
-
- function positionElementHorizontallyCenteredToRect(element, rect, topOffset) {
- var horizontalCenter = (element.offsetWidth / 2) - (rect.width / 2);
- positionElementToRect(element, rect, topOffset, horizontalCenter);
- }
-
- function positionElementCenteredAbove(element, aboveElement) {
- var elementMargin = getElementComputedStyleNumericProp(element, 'marginBottom');
- positionElementHorizontallyCenteredToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin);
- }
-
- function positionElementCenteredBelow(element, belowElement) {
- var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop');
- positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin);
- }
-
- function positionElementCenteredIn(element, inElement) {
- var verticalCenter = (inElement.offsetHeight / 2) - (element.offsetHeight / 2);
- positionElementHorizontallyCenteredToRect(element, inElement.getBoundingClientRect(), -verticalCenter);
- }
-
- function positionElementToLeftOf(element, leftOfElement) {
- var verticalCenter = (leftOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
- var elementMargin = getElementComputedStyleNumericProp(element, 'marginRight');
- positionElementToRect(element, leftOfElement.getBoundingClientRect(), -verticalCenter, element.offsetWidth + elementMargin);
- }
-
- function positionElementToRightOf(element, rightOfElement) {
- var verticalCenter = (rightOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
- var elementMargin = getElementComputedStyleNumericProp(element, 'marginLeft');
- var rightOfElementRect = rightOfElement.getBoundingClientRect();
- positionElementToRect(element, rightOfElementRect, -verticalCenter, -rightOfElement.offsetWidth - elementMargin);
- }
-
- __exports__.createDiv = createDiv;
- __exports__.hideElement = hideElement;
- __exports__.showElement = showElement;
- __exports__.swapElements = swapElements;
- __exports__.getEventTargetMatchingTag = getEventTargetMatchingTag;
- __exports__.nodeIsDescendantOfElement = nodeIsDescendantOfElement;
- __exports__.getElementRelativeOffset = getElementRelativeOffset;
- __exports__.getElementComputedStyleNumericProp = getElementComputedStyleNumericProp;
- __exports__.positionElementToRect = positionElementToRect;
- __exports__.positionElementHorizontallyCenteredToRect = positionElementHorizontallyCenteredToRect;
- __exports__.positionElementCenteredAbove = positionElementCenteredAbove;
- __exports__.positionElementCenteredBelow = positionElementCenteredBelow;
- __exports__.positionElementCenteredIn = positionElementCenteredIn;
- __exports__.positionElementToLeftOf = positionElementToLeftOf;
- __exports__.positionElementToRightOf = positionElementToRightOf;
- });
-define("content-kit-editor/utils/selection-utils",
- ["../constants","./element-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var SelectionDirection = __dependency1__.SelectionDirection;
- var RootTags = __dependency1__.RootTags;
- var nodeIsDescendantOfElement = __dependency2__.nodeIsDescendantOfElement;
-
- function getDirectionOfSelection(selection) {
- var node = selection.anchorNode;
- var position = node && node.compareDocumentPosition(selection.focusNode);
- if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
- return SelectionDirection.LEFT_TO_RIGHT;
- } else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
- return SelectionDirection.RIGHT_TO_LEFT;
- }
- return SelectionDirection.SAME_NODE;
- }
-
- function getSelectionElement(selection) {
- selection = selection || window.getSelection();
- var node = getDirectionOfSelection(selection) === SelectionDirection.LEFT_TO_RIGHT ? selection.anchorNode : selection.focusNode;
- return node && (node.nodeType === 3 ? node.parentNode : node);
+ this.action = options.action || this.name;
+ this.removeAction = options.removeAction || this.action;
}
+ inherit(TextFormatCommand, Command);
- function getSelectionBlockElement(selection) {
- selection = selection || window.getSelection();
- var element = getSelectionElement();
- var tag = element && element.tagName;
- while (tag && RootTags.indexOf(tag) === -1) {
- if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
- element = element.parentNode;
- tag = element.tagName;
+ TextFormatCommand.prototype = {
+ exec: function(value) {
+ document.execCommand(this.action, false, value || null);
+ this.editorContext.syncModelAt(this.editorContext.getCurrentBlockIndex());
+ },
+ unexec: function(value) {
+ document.execCommand(this.removeAction, false, value || null);
}
- return element;
- }
+ };
- function getSelectionTagName() {
- var element = getSelectionElement();
- return element ? element.tagName : null;
- }
+ __exports__["default"] = TextFormatCommand;
+ });
+define("content-kit-editor/commands/unordered-list",
+ ["./list","../constants","../../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var ListCommand = __dependency1__["default"];
+ var Tags = __dependency2__.Tags;
+ var inherit = __dependency3__.inherit;
- function getSelectionBlockTagName() {
- var element = getSelectionBlockElement();
- return element ? element.tagName : null;
+ function UnorderedListCommand() {
+ ListCommand.call(this, {
+ name: 'list',
+ tag: Tags.LIST,
+ action: 'insertUnorderedList'
+ });
}
+ inherit(UnorderedListCommand, ListCommand);
- function tagsInSelection(selection) {
- var element = getSelectionElement(selection);
- var tags = [];
- if (!selection.isCollapsed) {
- while(element) {
- if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
- if (element.tagName) {
- tags.push(element.tagName);
- }
- element = element.parentNode;
- }
- }
- return tags;
- }
+ __exports__["default"] = UnorderedListCommand;
+ });
+define("content-kit-editor/editor/editor-factory",
+ ["./editor","../commands/commands","../constants","../../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var Editor = __dependency1__["default"];
+ var TextFormatCommands = __dependency2__.TextFormatCommands;
+ var EmbedCommands = __dependency2__.EmbedCommands;
+ var Tags = __dependency3__.Tags;
+ var merge = __dependency4__.merge;
- function selectionIsInElement(selection, element) {
- var node = selection.anchorNode;
- return node && nodeIsDescendantOfElement(node, element);
- }
+ var defaults = {
+ defaultFormatter: Tags.PARAGRAPH,
+ placeholder: 'Write here...',
+ spellcheck: true,
+ autofocus: true,
+ textFormatCommands: TextFormatCommands.all,
+ embedCommands: EmbedCommands.all
+ };
- function selectionIsEditable(selection) {
- var el = getSelectionBlockElement(selection);
- return el.isContentEditable;
- }
+ /**
+ * Publically expose this class which sets up indiviual `Editor` classes
+ * depending if user passes string selector, Node, or NodeList
+ */
+ function EditorFactory(element, options) {
+ var editors = [];
+ var elements, elementsLen, i;
- /*
- function saveSelection() {
- var sel = window.getSelection();
- var ranges = [], i;
- if (sel.rangeCount) {
- var rangeCount = sel.rangeCount;
- for (i = 0; i < rangeCount; i++) {
- ranges.push(sel.getRangeAt(i));
- }
+ if (typeof element === 'string') {
+ elements = document.querySelectorAll(element);
+ } else if (element && element.length) {
+ elements = element;
+ } else if (element) {
+ elements = [element];
}
- return ranges;
- }
- function restoreSelection(savedSelection) {
- var sel = window.getSelection();
- var len = savedSelection.length, i;
- sel.removeAllRanges();
- for (i = 0; i < len; i++) {
- sel.addRange(savedSelection[i]);
+ if (elements) {
+ options = merge(defaults, options);
+ elementsLen = elements.length;
+ for (i = 0; i < elementsLen; i++) {
+ editors.push(new Editor(elements[i], options));
+ }
}
+
+ return editors.length > 1 ? editors : editors[0];
}
- */
- function moveCursorToBeginningOfSelection(selection) {
- var range = document.createRange();
- var node = selection.anchorNode;
- range.setStart(node, 0);
- range.setEnd(node, 0);
- selection.removeAllRanges();
- selection.addRange(range);
+ __exports__["default"] = EditorFactory;
+ });
+define("content-kit-editor/editor/editor-html-renderer",
+ ["../../content-kit-compiler/renderers/html-renderer","../../content-kit-compiler/types/type","../../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var HTMLRenderer = __dependency1__["default"];
+ var Type = __dependency2__["default"];
+ var inherit = __dependency3__.inherit;
+
+ function embedRenderer(model) {
+ var embedAttrs = model.attributes;
+ var isVideo = embedAttrs.embed_type === 'video';
+ return '' +
+ '
' +
+ '
';
}
- function restoreRange(range) {
- var selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
+ function imageRenderer(model) {
+ return '' +
+ '' +
+ '
';
}
- function selectNode(node) {
- var range = document.createRange();
- var selection = window.getSelection();
- range.setStart(node, 0);
- range.setEnd(node, node.length);
- selection.removeAllRanges();
- selection.addRange(range);
+ var typeRenderers = {};
+ typeRenderers[Type.EMBED.id] = embedRenderer;
+ typeRenderers[Type.IMAGE.id] = imageRenderer;
+
+ /**
+ * @class EditorHTMLRenderer
+ * @constructor
+ * Subclass of HTMLRenderer specifically for the Editor
+ * Wraps interactive elements to add functionality
+ */
+ function EditorHTMLRenderer() {
+ HTMLRenderer.call(this, {
+ typeRenderers: typeRenderers
+ });
}
+ inherit(EditorHTMLRenderer, HTMLRenderer);
- __exports__.getDirectionOfSelection = getDirectionOfSelection;
- __exports__.getSelectionElement = getSelectionElement;
- __exports__.getSelectionBlockElement = getSelectionBlockElement;
- __exports__.getSelectionTagName = getSelectionTagName;
- __exports__.getSelectionBlockTagName = getSelectionBlockTagName;
- __exports__.tagsInSelection = tagsInSelection;
- __exports__.selectionIsInElement = selectionIsInElement;
- __exports__.selectionIsEditable = selectionIsEditable;
- __exports__.moveCursorToBeginningOfSelection = moveCursorToBeginningOfSelection;
- __exports__.restoreRange = restoreRange;
- __exports__.selectNode = selectNode;
+ __exports__["default"] = EditorHTMLRenderer;
});
-define("content-kit-editor/views/embed-intent",
- ["./view","./toolbar","../../content-kit-utils/object-utils","../utils/selection-utils","../utils/element-utils","../constants","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
+define("content-kit-editor/editor/editor",
+ ["./editor-html-renderer","../views/text-format-toolbar","../views/tooltip","../views/embed-intent","../commands/unordered-list","../commands/ordered-list","../commands/text-format","../constants","../utils/selection-utils","../utils/paste-utils","../../content-kit-compiler/compiler","../../content-kit-compiler/models/text","../../content-kit-compiler/types/type","../../content-kit-utils/array-utils","../../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) {
"use strict";
- var View = __dependency1__["default"];
- var Toolbar = __dependency2__["default"];
- var inherit = __dependency3__.inherit;
- var getSelectionBlockElement = __dependency4__.getSelectionBlockElement;
- var positionElementToLeftOf = __dependency5__.positionElementToLeftOf;
- var positionElementCenteredIn = __dependency5__.positionElementCenteredIn;
- var ToolbarDirection = __dependency6__.ToolbarDirection;
- var Keycodes = __dependency6__.Keycodes;
- var nodeIsDescendantOfElement = __dependency5__.nodeIsDescendantOfElement;
- var createDiv = __dependency5__.createDiv;
+ var EditorHTMLRenderer = __dependency1__["default"];
+ var TextFormatToolbar = __dependency2__["default"];
+ var Tooltip = __dependency3__["default"];
+ var EmbedIntent = __dependency4__["default"];
+ var UnorderedListCommand = __dependency5__["default"];
+ var OrderedListCommand = __dependency6__["default"];
+ var TextFormatCommand = __dependency7__["default"];
+ var Tags = __dependency8__.Tags;
+ var RootTags = __dependency8__.RootTags;
+ var Keycodes = __dependency8__.Keycodes;
+ var RegEx = __dependency8__.RegEx;
+ var moveCursorToBeginningOfSelection = __dependency9__.moveCursorToBeginningOfSelection;
+ var getSelectionTagName = __dependency9__.getSelectionTagName;
+ var getSelectionBlockElement = __dependency9__.getSelectionBlockElement;
+ var getSelectionBlockTagName = __dependency9__.getSelectionBlockTagName;
+ var cleanPastedContent = __dependency10__.cleanPastedContent;
+ var Compiler = __dependency11__["default"];
+ var TextModel = __dependency12__["default"];
+ var Type = __dependency13__["default"];
+ var toArray = __dependency14__.toArray;
+ var merge = __dependency15__.merge;
- function EmbedIntent(options) {
- var embedIntent = this;
- var rootElement = options.rootElement;
- options.tagName = 'button';
- options.classNames = ['ck-embed-intent-btn'];
- View.call(embedIntent, options);
- embedIntent.editorContext = options.editorContext;
- embedIntent.loadingIndicator = createDiv('ck-embed-loading');
- embedIntent.element.title = 'Insert image or embed...';
- embedIntent.element.addEventListener('mouseup', function(e) {
- if (embedIntent.isActive) {
- embedIntent.deactivate();
- } else {
- embedIntent.activate();
- }
- e.stopPropagation();
- });
+ var editorClassName = 'ck-editor';
+ var editorClassNameRegExp = new RegExp(editorClassName);
- embedIntent.toolbar = new Toolbar({ embedIntent: embedIntent, editor: embedIntent.editorContext, commands: options.commands, direction: ToolbarDirection.RIGHT });
- embedIntent.isActive = false;
+ function bindTypingEvents(editor) {
+ var editorEl = editor.element;
- function embedIntentHandler() {
- var blockElement = getSelectionBlockElement();
- var blockElementContent = blockElement && blockElement.innerHTML;
- if (blockElementContent === '' || blockElementContent === '
') {
- embedIntent.showAt(blockElement);
- } else {
- embedIntent.hide();
+ // Breaks out of blockquotes when pressing enter.
+ editorEl.addEventListener('keyup', function(e) {
+ if(!e.shiftKey && e.which === Keycodes.ENTER) {
+ if(Tags.QUOTE === getSelectionBlockTagName()) {
+ document.execCommand('formatBlock', false, editor.defaultFormatter);
+ e.stopPropagation();
+ }
}
- }
+ });
- rootElement.addEventListener('keyup', embedIntentHandler);
+ // Creates unordered list when block starts with '- ', or ordered if starts with '1. '
+ editorEl.addEventListener('keyup', function(e) {
+ var selection = window.getSelection();
+ var selectionNode = selection.anchorNode;
+ if (!selectionNode) { return; }
- document.addEventListener('mouseup', function(e) {
- setTimeout(function() {
- if (!nodeIsDescendantOfElement(e.target, embedIntent.toolbar.element)) {
- embedIntentHandler();
+ var selectedText = selectionNode.textContent;
+ var command, replaceRegex;
+
+ if (Tags.LIST_ITEM !== getSelectionTagName()) {
+ if (RegEx.UL_START.test(selectedText)) {
+ command = new UnorderedListCommand();
+ replaceRegex = RegEx.UL_START;
+ } else if (RegEx.OL_START.test(selectedText)) {
+ command = new OrderedListCommand();
+ replaceRegex = RegEx.OL_START;
}
- });
- });
- document.addEventListener('keyup', function(e) {
- if (e.keyCode === Keycodes.ESC) {
- embedIntent.hide();
+ if (command) {
+ command.editorContext = editor;
+ command.exec();
+ selection = window.getSelection();
+ selection.anchorNode.textContent = selectedText.replace(replaceRegex, '');
+ moveCursorToBeginningOfSelection(selection);
+ e.stopPropagation();
+ }
}
});
- window.addEventListener('resize', function() {
- if(embedIntent.isShowing) {
- positionElementToLeftOf(embedIntent.element, embedIntent.atNode);
- if (embedIntent.toolbar.isShowing) {
- embedIntent.toolbar.positionToContent(embedIntent.element);
- }
+ // Assure there is always a supported root tag, and not empty text nodes or divs.
+ editorEl.addEventListener('keyup', function() {
+ if (this.innerHTML.length && RootTags.indexOf(getSelectionBlockTagName()) === -1) {
+ document.execCommand('formatBlock', false, editor.defaultFormatter);
}
});
- }
- inherit(EmbedIntent, View);
-
- EmbedIntent.prototype.hide = function() {
- if (EmbedIntent._super.prototype.hide.call(this)) {
- this.deactivate();
- }
- };
- EmbedIntent.prototype.showAt = function(node) {
- this.show();
- this.deactivate();
- this.atNode = node;
- positionElementToLeftOf(this.element, node);
- };
+ // Experimental: Live update - sync model with textual content as you type
+ editorEl.addEventListener('keyup', function() {
+ var index = editor.getCurrentBlockIndex();
+ editor.syncModelAt(index);
+ });
+ }
- EmbedIntent.prototype.activate = function() {
- if (!this.isActive) {
- this.addClass('activated');
- this.toolbar.show();
- this.toolbar.positionToContent(this.element);
- this.isActive = true;
- }
- };
+ /**
+ * @class Editor
+ * An individual Editor
+ * @param element `Element` node
+ * @param options hash of options
+ */
+ function Editor(element, options) {
+ var editor = this;
+ merge(editor, options);
- EmbedIntent.prototype.deactivate = function() {
- if (this.isActive) {
- this.removeClass('activated');
- this.toolbar.hide();
- this.isActive = false;
- }
- };
+ if (element) {
+ var className = element.className;
+ var dataset = element.dataset;
- EmbedIntent.prototype.showLoading = function() {
- var embedIntent = this;
- var loadingIndicator = embedIntent.loadingIndicator;
- embedIntent.hide();
- embedIntent.container.appendChild(loadingIndicator);
- positionElementCenteredIn(loadingIndicator, embedIntent.atNode);
- };
+ if (!editorClassNameRegExp.test(className)) {
+ className += (className ? ' ' : '') + editorClassName;
+ }
+ element.className = className;
- EmbedIntent.prototype.hideLoading = function() {
- this.container.removeChild(this.loadingIndicator);
- };
+ if (!dataset.placeholder) {
+ dataset.placeholder = editor.placeholder;
+ }
+ if(!editor.spellcheck) {
+ element.spellcheck = false;
+ }
- __exports__["default"] = EmbedIntent;
- });
-define("content-kit-editor/views/message",
- ["./view","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var View = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
+ element.setAttribute('contentEditable', true);
+ editor.element = element;
- function Message(options) {
- options = options || {};
- options.classNames = ['ck-message'];
- View.call(this, options);
- }
- inherit(Message, View);
+ var compiler = editor.compiler = options.compiler || new Compiler({
+ includeTypeNames: true, // output type names for easier debugging
+ renderer: new EditorHTMLRenderer()
+ });
+ editor.syncModel();
- Message.prototype.show = function(message) {
- var messageView = this;
- messageView.element.innerHTML = message;
- Message._super.prototype.show.call(messageView);
- setTimeout(function() {
- messageView.hide();
- }, 3000);
- };
+ bindTypingEvents(editor);
+ editor.element.addEventListener('paste', function(e) {
+ var cleanedContent = cleanPastedContent(e, editor.defaultFormatter);
+ if (cleanedContent) {
+ document.execCommand('insertHTML', false, cleanedContent);
+ editor.syncModel(); // TODO: can optimize to just sync to index range
+ }
+ });
- __exports__["default"] = Message;
- });
-define("content-kit-editor/views/prompt",
- ["./view","../../content-kit-utils/object-utils","../utils/selection-utils","../utils/element-utils","../constants","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
- "use strict";
- var View = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
- var inherit = __dependency2__.inherit;
- var restoreRange = __dependency3__.restoreRange;
- var createDiv = __dependency4__.createDiv;
- var positionElementToRect = __dependency4__.positionElementToRect;
- var Keycodes = __dependency5__.Keycodes;
+ editor.textFormatToolbar = new TextFormatToolbar({ rootElement: element, editor: editor, commands: editor.textFormatCommands });
+ var linkTooltips = new Tooltip({ rootElement: element, showForTag: Tags.LINK });
- var container = document.body;
- var hiliter = createDiv('ck-editor-hilite');
+ if(editor.embedCommands) {
+ // NOTE: must come after bindTypingEvents so those keyup handlers are executed first.
+ // TODO: manage event listener order
+ var embedIntent = new EmbedIntent({
+ editorContext: editor,
+ commands: editor.embedCommands,
+ rootElement: element
+ });
- function positionHiliteRange(range) {
- var rect = range.getBoundingClientRect();
- var style = hiliter.style;
- style.width = rect.width + 'px';
- style.height = rect.height + 'px';
- positionElementToRect(hiliter, rect);
+ if (editor.imageServiceUrl) {
+ // TODO: lookup by name
+ editor.embedCommands[0].uploader.url = editor.imageServiceUrl;
+ }
+ if (editor.embedServiceUrl) {
+ // TODO: lookup by name
+ editor.embedCommands[1].embedService.url = editor.embedServiceUrl;
+ }
+ }
+
+ if(editor.autofocus) { element.focus(); }
+ }
}
- function Prompt(options) {
- var prompt = this;
- options.tagName = 'input';
- View.call(prompt, options);
-
- prompt.command = options.command;
- prompt.element.placeholder = options.placeholder || '';
- prompt.element.addEventListener('mouseup', function(e) { e.stopPropagation(); }); // prevents closing prompt when clicking input
- prompt.element.addEventListener('keyup', function(e) {
- var entry = this.value;
- if(entry && prompt.range && !e.shiftKey && e.which === Keycodes.ENTER) {
- restoreRange(prompt.range);
- prompt.command.exec(entry);
- if (prompt.onComplete) { prompt.onComplete(); }
- }
- });
+ Editor.prototype.syncModel = function() {
+ this.model = this.compiler.parse(this.element.innerHTML);
+ };
- window.addEventListener('resize', function() {
- var activeHilite = hiliter.parentNode;
- var range = prompt.range;
- if(activeHilite && range) {
- positionHiliteRange(range);
- }
- });
- }
- inherit(Prompt, View);
+ Editor.prototype.syncModelAt = function(index) {
+ if (index > -1) {
+ var blockElements = toArray(this.element.children);
+ var parsedBlockModel = this.compiler.parser.parseBlock(blockElements[index]);
+ this.model[index] = parsedBlockModel;
- Prompt.prototype.show = function(callback) {
- var prompt = this;
- var element = prompt.element;
- var selection = window.getSelection();
- var range = selection && selection.rangeCount && selection.getRangeAt(0);
- element.value = null;
- prompt.range = range || null;
- if (range) {
- container.appendChild(hiliter);
- positionHiliteRange(prompt.range);
- setTimeout(function(){ element.focus(); }); // defer focus (disrupts mouseup events)
- if (callback) { prompt.onComplete = callback; }
+ // TODO: event subscription
+ ContentKitDemo.syncCodePane(this);
}
};
- Prompt.prototype.hide = function() {
- if (hiliter.parentNode) {
- container.removeChild(hiliter);
+ Editor.prototype.syncVisualAt = function(index) {
+ if (index > -1) {
+ var blockModel = this.model[index];
+ var html = this.compiler.render([blockModel]);
+ var blockElements = toArray(this.element.children);
+ var element = blockElements[index];
+ element.innerHTML = html;
}
};
- __exports__["default"] = Prompt;
- });
-define("content-kit-editor/views/text-format-toolbar",
- ["./toolbar","../../content-kit-utils/object-utils","../utils/selection-utils","../constants","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
- "use strict";
- var Toolbar = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
- var selectionIsEditable = __dependency3__.selectionIsEditable;
- var selectionIsInElement = __dependency3__.selectionIsInElement;
- var Keycodes = __dependency4__.Keycodes;
-
- function TextFormatToolbar(options) {
- var toolbar = this;
- Toolbar.call(this, options);
- toolbar.rootElement = options.rootElement;
- toolbar.rootElement.addEventListener('keyup', function() { toolbar.handleTextSelection(); });
-
- document.addEventListener('keyup', function(e) {
- if (e.keyCode === Keycodes.ESC) {
- toolbar.hide();
- }
- });
+ Editor.prototype.getCurrentBlockIndex = function() {
+ var selectionEl = getSelectionBlockElement();
+ var blockElements = toArray(this.element.children);
+ return blockElements.indexOf(selectionEl);
+ };
- document.addEventListener('mouseup', function() {
- setTimeout(function() { toolbar.handleTextSelection(); });
- });
+ Editor.prototype.insertBlock = function(model) {
+ this.insertBlockAt(model, this.getCurrentBlockIndex());
+ };
- window.addEventListener('resize', function() {
- if(toolbar.isShowing) {
- var activePromptRange = toolbar.activePrompt && toolbar.activePrompt.range;
- toolbar.positionToContent(activePromptRange ? activePromptRange : window.getSelection().getRangeAt(0));
- }
- });
- }
- inherit(TextFormatToolbar, Toolbar);
+ Editor.prototype.insertBlockAt = function(model, index) {
+ model = model || new TextModel();
+ this.model.splice(index, 0, model);
+ };
- TextFormatToolbar.prototype.handleTextSelection = function() {
- var toolbar = this;
- var selection = window.getSelection();
- if (selection.isCollapsed || !selectionIsEditable(selection) || selection.toString().trim() === '' || !selectionIsInElement(selection, toolbar.rootElement)) {
- toolbar.hide();
- } else {
- toolbar.updateForSelection(selection);
- }
+ Editor.prototype.addTextFormat = function(opts) {
+ var command = new TextFormatCommand(opts);
+ this.compiler.registerMarkupType(new Type({
+ name : opts.name,
+ tag : opts.tag || opts.name
+ }));
+ this.textFormatCommands.push(command);
+ this.textFormatToolbar.addCommand(command);
};
- __exports__["default"] = TextFormatToolbar;
+ __exports__["default"] = Editor;
});
-define("content-kit-editor/views/toolbar-button",
- ["../commands/base","exports"],
- function(__dependency1__, __exports__) {
+define("content-kit-editor/utils/element-utils",
+ ["exports"],
+ function(__exports__) {
"use strict";
- var Command = __dependency1__["default"];
-
- var buttonClassName = 'ck-toolbar-btn';
+ function createDiv(className) {
+ var div = document.createElement('div');
+ if (className) {
+ div.className = className;
+ }
+ return div;
+ }
- function ToolbarButton(options) {
- var button = this;
- var toolbar = options.toolbar;
- var command = options.command;
- var prompt = command.prompt;
- var element = document.createElement('button');
+ function hideElement(element) {
+ element.style.display = 'none';
+ }
- if(typeof command === 'string') {
- command = Command.index[command];
- }
+ function showElement(element) {
+ element.style.display = 'block';
+ }
- button.element = element;
- button.command = command;
- button.isActive = false;
+ function swapElements(elementToShow, elementToHide) {
+ hideElement(elementToHide);
+ showElement(elementToShow);
+ }
- element.title = command.name;
- element.className = buttonClassName;
- element.innerHTML = command.button;
- element.addEventListener('click', function(e) {
- if (!button.isActive && prompt) {
- toolbar.displayPrompt(prompt);
- } else {
- command.exec();
+ function getEventTargetMatchingTag(tag, target, container) {
+ // Traverses up DOM from an event target to find the node matching specifed tag
+ while (target && target !== container) {
+ if (target.tagName === tag) {
+ return target;
}
- });
+ target = target.parentNode;
+ }
}
- ToolbarButton.prototype = {
- setActive: function() {
- var button = this;
- if (!button.isActive) {
- button.element.className = buttonClassName + ' active';
- button.isActive = true;
- }
- },
- setInactive: function() {
- var button = this;
- if (button.isActive) {
- button.element.className = buttonClassName;
- button.isActive = false;
+ function nodeIsDescendantOfElement(node, element) {
+ var parentNode = node.parentNode;
+ while(parentNode) {
+ if (parentNode === element) {
+ return true;
}
+ parentNode = parentNode.parentNode;
}
- };
-
- __exports__["default"] = ToolbarButton;
- });
-define("content-kit-editor/views/toolbar",
- ["./view","./toolbar-button","../../content-kit-utils/object-utils","../utils/selection-utils","../constants","../utils/element-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
- "use strict";
- var View = __dependency1__["default"];
- var ToolbarButton = __dependency2__["default"];
- var inherit = __dependency3__.inherit;
- var tagsInSelection = __dependency4__.tagsInSelection;
- var ToolbarDirection = __dependency5__.ToolbarDirection;
- var createDiv = __dependency6__.createDiv;
- var swapElements = __dependency6__.swapElements;
- var positionElementToRightOf = __dependency6__.positionElementToRightOf;
- var positionElementCenteredAbove = __dependency6__.positionElementCenteredAbove;
+ return false;
+ }
- function updateButtonsForSelection(buttons, selection) {
- var selectedTags = tagsInSelection(selection),
- len = buttons.length,
- i, button;
+ function getElementRelativeOffset(element) {
+ var offset = { left: 0, top: -window.pageYOffset };
+ var offsetParent = element.offsetParent;
+ var offsetParentPosition = window.getComputedStyle(offsetParent).position;
+ var offsetParentRect;
- for (i = 0; i < len; i++) {
- button = buttons[i];
- if (selectedTags.indexOf(button.command.tag) > -1) {
- button.setActive();
- } else {
- button.setInactive();
- }
+ if (offsetParentPosition === 'relative') {
+ offsetParentRect = offsetParent.getBoundingClientRect();
+ offset.left = offsetParentRect.left;
+ offset.top = offsetParentRect.top;
}
+ return offset;
}
- function Toolbar(options) {
- var toolbar = this;
- var commands = options.commands;
- var commandCount = commands && commands.length;
- var i, button, command;
- toolbar.editor = options.editor || null;
- toolbar.embedIntent = options.embedIntent || null;
- toolbar.direction = options.direction || ToolbarDirection.TOP;
- options.classNames = ['ck-toolbar'];
- if (toolbar.direction === ToolbarDirection.RIGHT) {
- options.classNames.push('right');
- }
+ function getElementComputedStyleNumericProp(element, prop) {
+ return parseFloat(window.getComputedStyle(element)[prop]);
+ }
- View.call(toolbar, options);
+ function positionElementToRect(element, rect, topOffset, leftOffset) {
+ var relativeOffset = getElementRelativeOffset(element);
+ var style = element.style;
+ var round = Math.round;
- toolbar.activePrompt = null;
- toolbar.buttons = [];
+ topOffset = topOffset || 0;
+ leftOffset = leftOffset || 0;
+ style.left = round(rect.left - relativeOffset.left - leftOffset) + 'px';
+ style.top = round(rect.top - relativeOffset.top - topOffset) + 'px';
+ }
- toolbar.promptContainerElement = createDiv('ck-toolbar-prompt');
- toolbar.buttonContainerElement = createDiv('ck-toolbar-buttons');
- toolbar.element.appendChild(toolbar.promptContainerElement);
- toolbar.element.appendChild(toolbar.buttonContainerElement);
+ function positionElementHorizontallyCenteredToRect(element, rect, topOffset) {
+ var horizontalCenter = (element.offsetWidth / 2) - (rect.width / 2);
+ positionElementToRect(element, rect, topOffset, horizontalCenter);
+ }
- for(i = 0; i < commandCount; i++) {
- this.addCommand(commands[i]);
- }
+ function positionElementCenteredAbove(element, aboveElement) {
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginBottom');
+ positionElementHorizontallyCenteredToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin);
+ }
- // Closes prompt if displayed when changing selection
- document.addEventListener('mouseup', function() {
- toolbar.dismissPrompt();
- });
+ function positionElementCenteredBelow(element, belowElement) {
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop');
+ positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin);
}
- inherit(Toolbar, View);
- Toolbar.prototype.hide = function() {
- if (Toolbar._super.prototype.hide.call(this)) {
- var style = this.element.style;
- style.left = '';
- style.top = '';
- this.dismissPrompt();
- }
- };
+ function positionElementCenteredIn(element, inElement) {
+ var verticalCenter = (inElement.offsetHeight / 2) - (element.offsetHeight / 2);
+ positionElementHorizontallyCenteredToRect(element, inElement.getBoundingClientRect(), -verticalCenter);
+ }
- Toolbar.prototype.addCommand = function(command) {
- command.editorContext = this.editor;
- command.embedIntent = this.embedIntent;
- var button = new ToolbarButton({ command: command, toolbar: this });
- this.buttons.push(button);
- this.buttonContainerElement.appendChild(button.element);
- };
+ function positionElementToLeftOf(element, leftOfElement) {
+ var verticalCenter = (leftOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginRight');
+ positionElementToRect(element, leftOfElement.getBoundingClientRect(), -verticalCenter, element.offsetWidth + elementMargin);
+ }
- Toolbar.prototype.displayPrompt = function(prompt) {
- var toolbar = this;
- swapElements(toolbar.promptContainerElement, toolbar.buttonContainerElement);
- toolbar.promptContainerElement.appendChild(prompt.element);
- prompt.show(function() {
- toolbar.dismissPrompt();
- toolbar.updateForSelection(window.getSelection());
- });
- toolbar.activePrompt = prompt;
- };
+ function positionElementToRightOf(element, rightOfElement) {
+ var verticalCenter = (rightOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginLeft');
+ var rightOfElementRect = rightOfElement.getBoundingClientRect();
+ positionElementToRect(element, rightOfElementRect, -verticalCenter, -rightOfElement.offsetWidth - elementMargin);
+ }
- Toolbar.prototype.dismissPrompt = function() {
- var toolbar = this;
- var activePrompt = toolbar.activePrompt;
- if (activePrompt) {
- activePrompt.hide();
- swapElements(toolbar.buttonContainerElement, toolbar.promptContainerElement);
- toolbar.activePrompt = null;
- }
- };
+ __exports__.createDiv = createDiv;
+ __exports__.hideElement = hideElement;
+ __exports__.showElement = showElement;
+ __exports__.swapElements = swapElements;
+ __exports__.getEventTargetMatchingTag = getEventTargetMatchingTag;
+ __exports__.nodeIsDescendantOfElement = nodeIsDescendantOfElement;
+ __exports__.getElementRelativeOffset = getElementRelativeOffset;
+ __exports__.getElementComputedStyleNumericProp = getElementComputedStyleNumericProp;
+ __exports__.positionElementToRect = positionElementToRect;
+ __exports__.positionElementHorizontallyCenteredToRect = positionElementHorizontallyCenteredToRect;
+ __exports__.positionElementCenteredAbove = positionElementCenteredAbove;
+ __exports__.positionElementCenteredBelow = positionElementCenteredBelow;
+ __exports__.positionElementCenteredIn = positionElementCenteredIn;
+ __exports__.positionElementToLeftOf = positionElementToLeftOf;
+ __exports__.positionElementToRightOf = positionElementToRightOf;
+ });
+define("content-kit-editor/utils/paste-utils",
+ ["../constants","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var RegEx = __dependency1__.RegEx;
- Toolbar.prototype.updateForSelection = function(selection) {
- var toolbar = this;
- if (selection.isCollapsed) {
- toolbar.hide();
+ function plainTextToBlocks(plainText, tag) {
+ var blocks = plainText.split(RegEx.NEWLINE),
+ len = blocks.length,
+ block, openTag, closeTag, content, i;
+ if(len < 2) {
+ return plainText;
} else {
- toolbar.show();
- toolbar.positionToContent(selection.getRangeAt(0));
- updateButtonsForSelection(toolbar.buttons, selection);
+ content = '';
+ openTag = '<' + tag + '>';
+ closeTag = '' + tag + '>';
+ for(i = 0; i < len; ++i) {
+ block = blocks[i];
+ if(block !== '') {
+ content += openTag + block + closeTag;
+ }
+ }
+ return content;
}
- };
+ }
- Toolbar.prototype.positionToContent = function(content) {
- var directions = ToolbarDirection;
- var positioningMethod;
- switch(this.direction) {
- case directions.RIGHT:
- positioningMethod = positionElementToRightOf;
- break;
- default:
- positioningMethod = positionElementCenteredAbove;
+ function cleanPastedContent(event, defaultBlockTag) {
+ event.preventDefault();
+ var data = event.clipboardData, plainText;
+ if(data && data.getData) {
+ plainText = data.getData('text/plain');
+ return plainTextToBlocks(plainText, defaultBlockTag);
}
- positioningMethod(this.element, content);
- };
+ }
- __exports__["default"] = Toolbar;
+ __exports__.cleanPastedContent = cleanPastedContent;
});
-define("content-kit-editor/views/tooltip",
- ["./view","../../content-kit-utils/object-utils","../utils/element-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+define("content-kit-editor/utils/selection-utils",
+ ["../constants","./element-utils","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var View = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
- var positionElementCenteredBelow = __dependency3__.positionElementCenteredBelow;
- var getEventTargetMatchingTag = __dependency3__.getEventTargetMatchingTag;
+ var SelectionDirection = __dependency1__.SelectionDirection;
+ var RootTags = __dependency1__.RootTags;
+ var nodeIsDescendantOfElement = __dependency2__.nodeIsDescendantOfElement;
- function Tooltip(options) {
- var tooltip = this;
- var rootElement = options.rootElement;
- var delay = options.delay || 200;
- var timeout;
- options.classNames = ['ck-tooltip'];
- View.call(tooltip, options);
+ function getDirectionOfSelection(selection) {
+ var node = selection.anchorNode;
+ var position = node && node.compareDocumentPosition(selection.focusNode);
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
+ return SelectionDirection.LEFT_TO_RIGHT;
+ } else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
+ return SelectionDirection.RIGHT_TO_LEFT;
+ }
+ return SelectionDirection.SAME_NODE;
+ }
- rootElement.addEventListener('mouseover', function(e) {
- var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement);
- if (target && target.isContentEditable) {
- timeout = setTimeout(function() {
- tooltip.showLink(target.href, target);
- }, delay);
- }
- });
-
- rootElement.addEventListener('mouseout', function(e) {
- clearTimeout(timeout);
- var toElement = e.toElement || e.relatedTarget;
- if (toElement && toElement.className !== tooltip.element.className) {
- tooltip.hide();
- }
- });
+ function getSelectionElement(selection) {
+ selection = selection || window.getSelection();
+ var node = getDirectionOfSelection(selection) === SelectionDirection.LEFT_TO_RIGHT ? selection.anchorNode : selection.focusNode;
+ return node && (node.nodeType === 3 ? node.parentNode : node);
}
- inherit(Tooltip, View);
- Tooltip.prototype.showMessage = function(message, element) {
- var tooltip = this;
- var tooltipElement = tooltip.element;
- tooltipElement.innerHTML = message;
- tooltip.show();
- positionElementCenteredBelow(tooltipElement, element);
- };
+ function getSelectionBlockElement(selection) {
+ selection = selection || window.getSelection();
+ var element = getSelectionElement();
+ var tag = element && element.tagName;
+ while (tag && RootTags.indexOf(tag) === -1) {
+ if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
+ element = element.parentNode;
+ tag = element.tagName;
+ }
+ return element;
+ }
- Tooltip.prototype.showLink = function(link, element) {
- var message = '' + link + '';
- this.showMessage(message, element);
- };
+ function getSelectionTagName() {
+ var element = getSelectionElement();
+ return element ? element.tagName : null;
+ }
- __exports__["default"] = Tooltip;
- });
-define("content-kit-editor/views/view",
- ["exports"],
- function(__exports__) {
- "use strict";
- function View(options) {
- this.tagName = options.tagName || 'div';
- this.classNames = options.classNames || [];
- this.element = document.createElement(this.tagName);
- this.element.className = this.classNames.join(' ');
- this.container = options.container || document.body;
- this.isShowing = false;
+ function getSelectionBlockTagName() {
+ var element = getSelectionBlockElement();
+ return element ? element.tagName : null;
}
- View.prototype = {
- show: function() {
- var view = this;
- if(!view.isShowing) {
- view.container.appendChild(view.element);
- view.isShowing = true;
- return true;
+ function tagsInSelection(selection) {
+ var element = getSelectionElement(selection);
+ var tags = [];
+ if (!selection.isCollapsed) {
+ while(element) {
+ if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
+ if (element.tagName) {
+ tags.push(element.tagName);
+ }
+ element = element.parentNode;
}
- },
- hide: function() {
- var view = this;
- if(view.isShowing) {
- view.container.removeChild(view.element);
- view.isShowing = false;
- return true;
+ }
+ return tags;
+ }
+
+ function selectionIsInElement(selection, element) {
+ var node = selection.anchorNode;
+ return node && nodeIsDescendantOfElement(node, element);
+ }
+
+ function selectionIsEditable(selection) {
+ var el = getSelectionBlockElement(selection);
+ return el.isContentEditable;
+ }
+
+ /*
+ function saveSelection() {
+ var sel = window.getSelection();
+ var ranges = [], i;
+ if (sel.rangeCount) {
+ var rangeCount = sel.rangeCount;
+ for (i = 0; i < rangeCount; i++) {
+ ranges.push(sel.getRangeAt(i));
}
- },
- focus: function() {
- this.element.focus();
- },
- addClass: function(className) {
- this.classNames.push(className);
- this.element.className = this.classNames.join(' ');
- },
- removeClass: function(className) {
- this.classNames.splice(this.classNames.indexOf(className), 1);
- this.element.className = this.classNames.join(' ');
}
- };
+ return ranges;
+ }
- __exports__["default"] = View;
+ function restoreSelection(savedSelection) {
+ var sel = window.getSelection();
+ var len = savedSelection.length, i;
+ sel.removeAllRanges();
+ for (i = 0; i < len; i++) {
+ sel.addRange(savedSelection[i]);
+ }
+ }
+ */
+
+ function moveCursorToBeginningOfSelection(selection) {
+ var range = document.createRange();
+ var node = selection.anchorNode;
+ range.setStart(node, 0);
+ range.setEnd(node, 0);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ function restoreRange(range) {
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ function selectNode(node) {
+ var range = document.createRange();
+ var selection = window.getSelection();
+ range.setStart(node, 0);
+ range.setEnd(node, node.length);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ __exports__.getDirectionOfSelection = getDirectionOfSelection;
+ __exports__.getSelectionElement = getSelectionElement;
+ __exports__.getSelectionBlockElement = getSelectionBlockElement;
+ __exports__.getSelectionTagName = getSelectionTagName;
+ __exports__.getSelectionBlockTagName = getSelectionBlockTagName;
+ __exports__.tagsInSelection = tagsInSelection;
+ __exports__.selectionIsInElement = selectionIsInElement;
+ __exports__.selectionIsEditable = selectionIsEditable;
+ __exports__.moveCursorToBeginningOfSelection = moveCursorToBeginningOfSelection;
+ __exports__.restoreRange = restoreRange;
+ __exports__.selectNode = selectNode;
});
-define("content-kit-compiler/models/block",
- ["./model","exports"],
- function(__dependency1__, __exports__) {
+define("content-kit-editor/views/embed-intent",
+ ["./view","./toolbar","../../content-kit-utils/object-utils","../utils/selection-utils","../utils/element-utils","../constants","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
"use strict";
- var Model = __dependency1__["default"];
+ var View = __dependency1__["default"];
+ var Toolbar = __dependency2__["default"];
+ var inherit = __dependency3__.inherit;
+ var getSelectionBlockElement = __dependency4__.getSelectionBlockElement;
+ var positionElementToLeftOf = __dependency5__.positionElementToLeftOf;
+ var positionElementCenteredIn = __dependency5__.positionElementCenteredIn;
+ var ToolbarDirection = __dependency6__.ToolbarDirection;
+ var Keycodes = __dependency6__.Keycodes;
+ var nodeIsDescendantOfElement = __dependency5__.nodeIsDescendantOfElement;
+ var createDiv = __dependency5__.createDiv;
- /**
- * Ensures block markups at the same index are always in a specific order.
- * For example, so all bold links are consistently marked up
- * as text instead of text
- */
- function sortBlockMarkups(markups) {
- return markups.sort(function(a, b) {
- if (a.start === b.start && a.end === b.end) {
- return b.type - a.type;
+ function EmbedIntent(options) {
+ var embedIntent = this;
+ var rootElement = options.rootElement;
+ options.tagName = 'button';
+ options.classNames = ['ck-embed-intent-btn'];
+ View.call(embedIntent, options);
+
+ embedIntent.editorContext = options.editorContext;
+ embedIntent.loadingIndicator = createDiv('ck-embed-loading');
+ embedIntent.element.title = 'Insert image or embed...';
+ embedIntent.element.addEventListener('mouseup', function(e) {
+ if (embedIntent.isActive) {
+ embedIntent.deactivate();
+ } else {
+ embedIntent.activate();
}
- return 0;
+ e.stopPropagation();
});
- }
- /**
- * @class BlockModel
- * @constructor
- * @extends Model
- */
- function BlockModel(options) {
- options = options || {};
- Model.call(this, options);
- this.value = options.value || '';
- this.markup = sortBlockMarkups(options.markup || []);
- }
+ embedIntent.toolbar = new Toolbar({ embedIntent: embedIntent, editor: embedIntent.editorContext, commands: options.commands, direction: ToolbarDirection.RIGHT });
+ embedIntent.isActive = false;
- __exports__["default"] = BlockModel;
- });
-define("content-kit-compiler/models/embed",
- ["../models/model","../types/type","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var Model = __dependency1__["default"];
- var Type = __dependency2__["default"];
+ function embedIntentHandler() {
+ var blockElement = getSelectionBlockElement();
+ var blockElementContent = blockElement && blockElement.innerHTML;
+ if (blockElementContent === '' || blockElementContent === '
') {
+ embedIntent.showAt(blockElement);
+ } else {
+ embedIntent.hide();
+ }
+ }
- /**
- * @class EmbedModel
- * @constructor
- * @extends Model
- * Massages data from an oEmbed response into an EmbedModel
- */
- function EmbedModel(options) {
- if (!options) { return null; }
+ rootElement.addEventListener('keyup', embedIntentHandler);
- Model.call(this, {
- type: Type.EMBED.id,
- type_name: Type.EMBED.name,
- attributes: {}
+ document.addEventListener('mouseup', function(e) {
+ setTimeout(function() {
+ if (!nodeIsDescendantOfElement(e.target, embedIntent.toolbar.element)) {
+ embedIntentHandler();
+ }
+ });
});
- var attributes = this.attributes;
- var embedType = options.type;
- var providerName = options.provider_name;
- var embedUrl = options.url;
- var embedTitle = options.title;
- var embedThumbnail = options.thumbnail_url;
- var embedHtml = options.html;
-
- if (embedType) { attributes.embed_type = embedType; }
- if (providerName) { attributes.provider_name = providerName; }
- if (embedUrl) { attributes.url = embedUrl; }
- if (embedTitle) { attributes.title = embedTitle; }
+ document.addEventListener('keyup', function(e) {
+ if (e.keyCode === Keycodes.ESC) {
+ embedIntent.hide();
+ }
+ });
- if (embedType === 'photo') {
- attributes.thumbnail = options.media_url || embedUrl;
- } else if (embedThumbnail) {
- attributes.thumbnail = embedThumbnail;
- }
+ window.addEventListener('resize', function() {
+ if(embedIntent.isShowing) {
+ positionElementToLeftOf(embedIntent.element, embedIntent.atNode);
+ if (embedIntent.toolbar.isShowing) {
+ embedIntent.toolbar.positionToContent(embedIntent.element);
+ }
+ }
+ });
+ }
+ inherit(EmbedIntent, View);
- if (embedHtml && embedType === 'rich') {
- attributes.html = embedHtml;
+ EmbedIntent.prototype.hide = function() {
+ if (EmbedIntent._super.prototype.hide.call(this)) {
+ this.deactivate();
}
- }
+ };
- __exports__["default"] = EmbedModel;
- });
-define("content-kit-compiler/models/image",
- ["./block","../types/type","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var BlockModel = __dependency1__["default"];
- var Type = __dependency2__["default"];
+ EmbedIntent.prototype.showAt = function(node) {
+ this.show();
+ this.deactivate();
+ this.atNode = node;
+ positionElementToLeftOf(this.element, node);
+ };
- /**
- * @class ImageModel
- * @constructor
- * @extends BlockModel
- * A simple BlockModel subclass representing an image
- */
- function ImageModel(options) {
- options = options || {};
- options.type = Type.IMAGE.id;
- options.type_name = Type.IMAGE.name;
- if (options.src) {
- options.attributes = { src: options.src };
+ EmbedIntent.prototype.activate = function() {
+ if (!this.isActive) {
+ this.addClass('activated');
+ this.toolbar.show();
+ this.toolbar.positionToContent(this.element);
+ this.isActive = true;
}
- BlockModel.call(this, options);
- }
-
- __exports__["default"] = ImageModel;
- });
-define("content-kit-compiler/models/markup",
- ["./model","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Model = __dependency1__["default"];
+ };
- /**
- * @class MarkupModel
- * @constructor
- * @extends Model
- */
- function MarkupModel(options) {
- options = options || {};
- Model.call(this, options);
- this.start = options.start || 0;
- this.end = options.end || 0;
- }
+ EmbedIntent.prototype.deactivate = function() {
+ if (this.isActive) {
+ this.removeClass('activated');
+ this.toolbar.hide();
+ this.isActive = false;
+ }
+ };
- __exports__["default"] = MarkupModel;
- });
-define("content-kit-compiler/models/model",
- ["exports"],
- function(__exports__) {
- "use strict";
- /**
- * @class Model
- * @constructor
- * @private
- */
- function Model(options) {
- options = options || {};
- var type_name = options.type_name;
- var attributes = options.attributes;
+ EmbedIntent.prototype.showLoading = function() {
+ var embedIntent = this;
+ var loadingIndicator = embedIntent.loadingIndicator;
+ embedIntent.hide();
+ embedIntent.container.appendChild(loadingIndicator);
+ positionElementCenteredIn(loadingIndicator, embedIntent.atNode);
+ };
- this.type = options.type || null;
- if (type_name) {
- this.type_name = type_name;
- }
- if (attributes) {
- this.attributes = attributes;
- }
- }
+ EmbedIntent.prototype.hideLoading = function() {
+ this.container.removeChild(this.loadingIndicator);
+ };
- __exports__["default"] = Model;
+ __exports__["default"] = EmbedIntent;
});
-define("content-kit-compiler/models/text",
- ["./block","../types/type","exports"],
+define("content-kit-editor/views/message",
+ ["./view","../../content-kit-utils/object-utils","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var BlockModel = __dependency1__["default"];
- var Type = __dependency2__["default"];
+ var View = __dependency1__["default"];
+ var inherit = __dependency2__.inherit;
- /**
- * @class TextModel
- * @constructor
- * @extends BlockModel
- * A simple BlockModel subclass representing a paragraph of text
- */
- function TextModel(options) {
+ function Message(options) {
options = options || {};
- options.type = Type.TEXT.id;
- options.type_name = Type.TEXT.name;
- BlockModel.call(this, options);
- }
-
- __exports__["default"] = TextModel;
- });
-define("content-kit-compiler/parsers/html-parser",
- ["../models/block","../models/markup","../types/default-types","../../content-kit-utils/object-utils","../../content-kit-utils/array-utils","../../content-kit-utils/string-utils","../../content-kit-utils/node-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
- "use strict";
- var BlockModel = __dependency1__["default"];
- var MarkupModel = __dependency2__["default"];
- var DefaultBlockTypeSet = __dependency3__.DefaultBlockTypeSet;
- var DefaultMarkupTypeSet = __dependency3__.DefaultMarkupTypeSet;
- var mergeWithOptions = __dependency4__.mergeWithOptions;
- var toArray = __dependency5__.toArray;
- var trim = __dependency6__.trim;
- var trimLeft = __dependency6__.trimLeft;
- var sanitizeWhitespace = __dependency6__.sanitizeWhitespace;
- var createElement = __dependency7__.createElement;
- var DOMParsingNode = __dependency7__.DOMParsingNode;
- var textOfNode = __dependency7__.textOfNode;
- var unwrapNode = __dependency7__.unwrapNode;
- var attributesForNode = __dependency7__.attributesForNode;
-
- /**
- * Gets the last block in the set or creates and return a default block if none exist yet.
- */
- function getLastBlockOrCreate(parser, blocks) {
- var block;
- if (blocks.length) {
- block = blocks[blocks.length - 1];
- } else {
- block = parser.parseBlock(createElement(DefaultBlockTypeSet.TEXT.tag));
- blocks.push(block);
- }
- return block;
- }
-
- /**
- * Helper to retain stray elements at the root of the html that aren't blocks
- */
- function handleNonBlockElementAtRoot(parser, elementNode, blocks) {
- var block = getLastBlockOrCreate(parser, blocks),
- markup = parser.parseElementMarkup(elementNode, block.value.length);
- if (markup) {
- block.markup.push(markup);
- }
- block.value += textOfNode(elementNode);
+ options.classNames = ['ck-message'];
+ View.call(this, options);
}
+ inherit(Message, View);
- /**
- * @class HTMLParser
- * @constructor
- */
- function HTMLParser(options) {
- var defaults = {
- blockTypes : DefaultBlockTypeSet,
- markupTypes : DefaultMarkupTypeSet,
- includeTypeNames : false
- };
- mergeWithOptions(this, defaults, options);
+ Message.prototype.show = function(message) {
+ var messageView = this;
+ messageView.element.innerHTML = message;
+ Message._super.prototype.show.call(messageView);
+ setTimeout(function() {
+ messageView.hide();
+ }, 3000);
+ };
+
+ __exports__["default"] = Message;
+ });
+define("content-kit-editor/views/prompt",
+ ["./view","../../content-kit-utils/object-utils","../utils/selection-utils","../utils/element-utils","../constants","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
+ var View = __dependency1__["default"];
+ var inherit = __dependency2__.inherit;
+ var inherit = __dependency2__.inherit;
+ var restoreRange = __dependency3__.restoreRange;
+ var createDiv = __dependency4__.createDiv;
+ var positionElementToRect = __dependency4__.positionElementToRect;
+ var Keycodes = __dependency5__.Keycodes;
+
+ var container = document.body;
+ var hiliter = createDiv('ck-editor-hilite');
+
+ function positionHiliteRange(range) {
+ var rect = range.getBoundingClientRect();
+ var style = hiliter.style;
+ style.width = rect.width + 'px';
+ style.height = rect.height + 'px';
+ positionElementToRect(hiliter, rect);
}
- /**
- * @method parse
- * @param html String of HTML content
- * @return Array Parsed JSON content array
- */
- HTMLParser.prototype.parse = function(html) {
- DOMParsingNode.innerHTML = sanitizeWhitespace(html);
+ function Prompt(options) {
+ var prompt = this;
+ options.tagName = 'input';
+ View.call(prompt, options);
- var children = toArray(DOMParsingNode.childNodes),
- len = children.length,
- blocks = [],
- i, currentNode, block, text;
+ prompt.command = options.command;
+ prompt.element.placeholder = options.placeholder || '';
+ prompt.element.addEventListener('mouseup', function(e) { e.stopPropagation(); }); // prevents closing prompt when clicking input
+ prompt.element.addEventListener('keyup', function(e) {
+ var entry = this.value;
+ if(entry && prompt.range && !e.shiftKey && e.which === Keycodes.ENTER) {
+ restoreRange(prompt.range);
+ prompt.command.exec(entry);
+ if (prompt.onComplete) { prompt.onComplete(); }
+ }
+ });
- for (i = 0; i < len; i++) {
- currentNode = children[i];
- // All top level nodes *should be* `Element` nodes and supported block types.
- // We'll handle some cases if it isn't so we don't lose any content when parsing.
- // Parser assumes sane input (such as from the ContentKit Editor) and is not intended to be a full html sanitizer.
- if (currentNode.nodeType === 1) {
- block = this.parseBlock(currentNode);
- if (block) {
- blocks.push(block);
- } else {
- handleNonBlockElementAtRoot(this, currentNode, blocks);
- }
- } else if (currentNode.nodeType === 3) {
- text = currentNode.nodeValue;
- if (trim(text)) {
- block = getLastBlockOrCreate(this, blocks);
- block.value += text;
- }
+ window.addEventListener('resize', function() {
+ var activeHilite = hiliter.parentNode;
+ var range = prompt.range;
+ if(activeHilite && range) {
+ positionHiliteRange(range);
}
- }
+ });
+ }
+ inherit(Prompt, View);
- return blocks;
+ Prompt.prototype.show = function(callback) {
+ var prompt = this;
+ var element = prompt.element;
+ var selection = window.getSelection();
+ var range = selection && selection.rangeCount && selection.getRangeAt(0);
+ element.value = null;
+ prompt.range = range || null;
+ if (range) {
+ container.appendChild(hiliter);
+ positionHiliteRange(prompt.range);
+ setTimeout(function(){ element.focus(); }); // defer focus (disrupts mouseup events)
+ if (callback) { prompt.onComplete = callback; }
+ }
};
- /**
- * @method parseBlock
- * @param node DOM node to parse
- * @return {BlockModel} parsed block model
- * Parses a single block type node into a model
- */
- HTMLParser.prototype.parseBlock = function(node) {
- var type = this.blockTypes.findByNode(node);
- if (type) {
- return new BlockModel({
- type : type.id,
- type_name : this.includeTypeNames && type.name,
- value : trim(textOfNode(node)),
- attributes : attributesForNode(node),
- markup : this.parseBlockMarkup(node)
- });
+ Prompt.prototype.hide = function() {
+ if (hiliter.parentNode) {
+ container.removeChild(hiliter);
}
};
- /**
- * @method parseBlockMarkup
- * @param node DOM node to parse
- * @return {Array} parsed markups
- * Parses a single block type node's markup
- */
- HTMLParser.prototype.parseBlockMarkup = function(node) {
- var processedText = '',
- markups = [],
- index = 0,
- currentNode, markup;
-
- // Clone the node since it will be recursively torn down
- node = node.cloneNode(true);
-
- while (node.hasChildNodes()) {
- currentNode = node.firstChild;
- if (currentNode.nodeType === 1) {
- markup = this.parseElementMarkup(currentNode, processedText.length);
- if (markup) {
- markups.push(markup);
- }
- // unwrap the element so we can process any children
- if (currentNode.hasChildNodes()) {
- unwrapNode(currentNode);
- }
- } else if (currentNode.nodeType === 3) {
- var text = sanitizeWhitespace(currentNode.nodeValue);
- if (index === 0) { text = trimLeft(text); }
- if (text) { processedText += text; }
- }
+ __exports__["default"] = Prompt;
+ });
+define("content-kit-editor/views/text-format-toolbar",
+ ["./toolbar","../../content-kit-utils/object-utils","../utils/selection-utils","../constants","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var Toolbar = __dependency1__["default"];
+ var inherit = __dependency2__.inherit;
+ var selectionIsEditable = __dependency3__.selectionIsEditable;
+ var selectionIsInElement = __dependency3__.selectionIsInElement;
+ var Keycodes = __dependency4__.Keycodes;
- // node has been processed, remove it
- currentNode.parentNode.removeChild(currentNode);
- index++;
+ function handleTextSelection(toolbar) {
+ var selection = window.getSelection();
+ if (selection.isCollapsed || !selectionIsEditable(selection) || selection.toString().trim() === '' || !selectionIsInElement(selection, toolbar.rootElement)) {
+ toolbar.hide();
+ } else {
+ toolbar.updateForSelection(selection);
}
+ }
- return markups;
- };
+ function TextFormatToolbar(options) {
+ var toolbar = this;
+ Toolbar.call(this, options);
+ toolbar.rootElement = options.rootElement;
+ toolbar.rootElement.addEventListener('keyup', function() { handleTextSelection(toolbar); });
- /**
- * @method parseElementMarkup
- * @param node DOM node to parse
- * @param startIndex DOM node to parse
- * @return {MarkupModel} parsed markup model
- * Parses markup of a single html element node
- */
- HTMLParser.prototype.parseElementMarkup = function(node, startIndex) {
- var type = this.markupTypes.findByNode(node),
- selfClosing, endIndex;
+ document.addEventListener('keyup', function(e) {
+ if (e.keyCode === Keycodes.ESC) {
+ toolbar.hide();
+ }
+ });
- if (type) {
- selfClosing = type.selfClosing;
- if (!selfClosing && !node.hasChildNodes()) { return; } // check for empty nodes
+ document.addEventListener('mouseup', function() {
+ setTimeout(function() { handleTextSelection(toolbar); });
+ });
- endIndex = startIndex + (selfClosing ? 0 : textOfNode(node).length);
- if (endIndex > startIndex || (selfClosing && endIndex === startIndex)) { // check for empty nodes
- return new MarkupModel({
- type : type.id,
- type_name : this.includeTypeNames && type.name,
- start : startIndex,
- end : endIndex,
- attributes : attributesForNode(node)
- });
+ window.addEventListener('resize', function() {
+ if(toolbar.isShowing) {
+ var activePromptRange = toolbar.activePrompt && toolbar.activePrompt.range;
+ toolbar.positionToContent(activePromptRange ? activePromptRange : window.getSelection().getRangeAt(0));
}
- }
- };
+ });
+ }
+ inherit(TextFormatToolbar, Toolbar);
- __exports__["default"] = HTMLParser;
+ __exports__["default"] = TextFormatToolbar;
});
-define("content-kit-compiler/renderers/html-element-renderer",
- ["../../content-kit-utils/string-utils","../../content-kit-utils/array-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
+define("content-kit-editor/views/toolbar-button",
+ ["../commands/base","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
- var injectIntoString = __dependency1__.injectIntoString;
- var sumSparseArray = __dependency2__.sumSparseArray;
+ var Command = __dependency1__["default"];
- /**
- * Builds an opening html tag. i.e. ''
- */
- function createOpeningTag(tagName, attributes, selfClosing /*,blacklist*/) {
- var tag = '<' + tagName;
- for (var attr in attributes) {
- if (attributes.hasOwnProperty(attr)) {
- //if (blacklist && attr in blacklist) { continue; }
- tag += ' ' + attr + '="' + attributes[attr] + '"';
- }
+ var buttonClassName = 'ck-toolbar-btn';
+
+ function ToolbarButton(options) {
+ var button = this;
+ var toolbar = options.toolbar;
+ var command = options.command;
+ var prompt = command.prompt;
+ var element = document.createElement('button');
+
+ if(typeof command === 'string') {
+ command = Command.index[command];
}
- if (selfClosing) { tag += '/'; }
- tag += '>';
- return tag;
- }
- /**
- * Builds a closing html tag. i.e. ''
- */
- function createCloseTag(tagName) {
- return '' + tagName + '>';
- }
+ button.element = element;
+ button.command = command;
+ button.isActive = false;
- /**
- * @class HTMLElementRenderer
- * @constructor
- */
- function HTMLElementRenderer(options) {
- options = options || {};
- this.type = options.type;
- this.markupTypes = options.markupTypes;
+ element.title = command.name;
+ element.className = buttonClassName;
+ element.innerHTML = command.button;
+ element.addEventListener('click', function(e) {
+ if (!button.isActive && prompt) {
+ toolbar.displayPrompt(prompt);
+ } else {
+ command.exec();
+ }
+ });
}
- /**
- * @method render
- * @param model a block model
- * @return String html
- * Renders a block model into a HTML string.
- */
- HTMLElementRenderer.prototype.render = function(model) {
- var html = '';
- var type = this.type;
- var tagName = type.tag;
- var selfClosing = type.selfClosing;
-
- if (tagName) {
- html += createOpeningTag(tagName, model.attributes, selfClosing);
- }
- if (!selfClosing) {
- html += this.renderMarkup(model.value, model.markup);
- if (tagName) {
- html += createCloseTag(tagName);
+ ToolbarButton.prototype = {
+ setActive: function() {
+ var button = this;
+ if (!button.isActive) {
+ button.element.className = buttonClassName + ' active';
+ button.isActive = true;
+ }
+ },
+ setInactive: function() {
+ var button = this;
+ if (button.isActive) {
+ button.element.className = buttonClassName;
+ button.isActive = false;
}
}
- return html;
};
- /**
- * @method renderMarkup
- * @param text plain text to apply markup to
- * @param markup an array of markup models
- * @return String html
- * Renders a markup model into a HTML string.
- */
- HTMLElementRenderer.prototype.renderMarkup = function(text, markups) {
- var parsedTagsIndexes = [],
- len = markups && markups.length, i;
-
- for (i = 0; i < len; i++) {
- var markup = markups[i],
- markupMeta = this.markupTypes.findById(markup.type),
- tagName = markupMeta.tag,
- selfClosing = markupMeta.selfClosing,
- start = markup.start,
- end = markup.end,
- openTag = createOpeningTag(tagName, markup.attributes, selfClosing),
- parsedTagLengthAtIndex = parsedTagsIndexes[start] || 0,
- parsedTagLengthBeforeIndex = sumSparseArray(parsedTagsIndexes.slice(0, start + 1));
+ __exports__["default"] = ToolbarButton;
+ });
+define("content-kit-editor/views/toolbar",
+ ["./view","./toolbar-button","../../content-kit-utils/object-utils","../utils/selection-utils","../constants","../utils/element-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
+ "use strict";
+ var View = __dependency1__["default"];
+ var ToolbarButton = __dependency2__["default"];
+ var inherit = __dependency3__.inherit;
+ var tagsInSelection = __dependency4__.tagsInSelection;
+ var ToolbarDirection = __dependency5__.ToolbarDirection;
+ var createDiv = __dependency6__.createDiv;
+ var swapElements = __dependency6__.swapElements;
+ var positionElementToRightOf = __dependency6__.positionElementToRightOf;
+ var positionElementCenteredAbove = __dependency6__.positionElementCenteredAbove;
- text = injectIntoString(text, openTag, start + parsedTagLengthBeforeIndex);
- parsedTagsIndexes[start] = parsedTagLengthAtIndex + openTag.length;
+ function updateButtonsForSelection(buttons, selection) {
+ var selectedTags = tagsInSelection(selection),
+ len = buttons.length,
+ i, button;
- if (!selfClosing) {
- var closeTag = createCloseTag(tagName);
- parsedTagLengthAtIndex = parsedTagsIndexes[end] || 0;
- parsedTagLengthBeforeIndex = sumSparseArray(parsedTagsIndexes.slice(0, end));
- text = injectIntoString(text, closeTag, end + parsedTagLengthBeforeIndex);
- parsedTagsIndexes[end] = parsedTagLengthAtIndex + closeTag.length;
+ for (i = 0; i < len; i++) {
+ button = buttons[i];
+ if (selectedTags.indexOf(button.command.tag) > -1) {
+ button.setActive();
+ } else {
+ button.setInactive();
}
}
+ }
- return text;
- };
+ function Toolbar(options) {
+ var toolbar = this;
+ var commands = options.commands;
+ var commandCount = commands && commands.length;
+ var i, button, command;
+ toolbar.editor = options.editor || null;
+ toolbar.embedIntent = options.embedIntent || null;
+ toolbar.direction = options.direction || ToolbarDirection.TOP;
+ options.classNames = ['ck-toolbar'];
+ if (toolbar.direction === ToolbarDirection.RIGHT) {
+ options.classNames.push('right');
+ }
- __exports__["default"] = HTMLElementRenderer;
- });
-define("content-kit-compiler/renderers/html-embed-renderer",
- ["./embeds/youtube","./embeds/twitter","./embeds/instagram","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- var YouTubeRenderer = __dependency1__["default"];
- var TwitterRenderer = __dependency2__["default"];
- var InstagramRenderer = __dependency3__["default"];
+ View.call(toolbar, options);
- /**
- * A dictionary of supported embed services
- */
- var services = {
- YOUTUBE : {
- id: 1,
- renderer: new YouTubeRenderer()
- },
- TWITTER : {
- id: 2,
- renderer: new TwitterRenderer()
- },
- INSTAGRAM : {
- id: 3,
- renderer: new InstagramRenderer()
+ toolbar.activePrompt = null;
+ toolbar.buttons = [];
+
+ toolbar.promptContainerElement = createDiv('ck-toolbar-prompt');
+ toolbar.buttonContainerElement = createDiv('ck-toolbar-buttons');
+ toolbar.element.appendChild(toolbar.promptContainerElement);
+ toolbar.element.appendChild(toolbar.buttonContainerElement);
+
+ for(i = 0; i < commandCount; i++) {
+ this.addCommand(commands[i]);
}
- };
- /**
- * @class EmbedRenderer
- * @constructor
- */
- function EmbedRenderer() {}
+ // Closes prompt if displayed when changing selection
+ document.addEventListener('mouseup', function() {
+ toolbar.dismissPrompt();
+ });
+ }
+ inherit(Toolbar, View);
- /**
- * @method render
- * @param model
- * @return String html
- */
- EmbedRenderer.prototype.render = function(model) {
- var renderer = this.rendererFor(model);
- if (renderer) {
- return renderer.render(model);
+ Toolbar.prototype.hide = function() {
+ if (Toolbar._super.prototype.hide.call(this)) {
+ var style = this.element.style;
+ style.left = '';
+ style.top = '';
+ this.dismissPrompt();
}
- var attrs = model.attributes;
- return attrs && attrs.html || '';
};
- /**
- * @method rendererFor
- * @param model
- * @return service renderer
- */
- EmbedRenderer.prototype.rendererFor = function(model) {
- var provider = model.attributes.provider_name;
- var providerKey = provider && provider.toUpperCase();
- var service = services[providerKey];
- return service && service.renderer;
+ Toolbar.prototype.addCommand = function(command) {
+ command.editorContext = this.editor;
+ command.embedIntent = this.embedIntent;
+ var button = new ToolbarButton({ command: command, toolbar: this });
+ this.buttons.push(button);
+ this.buttonContainerElement.appendChild(button.element);
};
- __exports__["default"] = EmbedRenderer;
- });
-define("content-kit-compiler/renderers/html-renderer",
- ["../types/type","./html-element-renderer","./html-embed-renderer","../types/default-types","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
- "use strict";
- var Type = __dependency1__["default"];
- var HTMLElementRenderer = __dependency2__["default"];
- var HTMLEmbedRenderer = __dependency3__["default"];
- var DefaultBlockTypeSet = __dependency4__.DefaultBlockTypeSet;
- var DefaultMarkupTypeSet = __dependency4__.DefaultMarkupTypeSet;
- var mergeWithOptions = __dependency5__.mergeWithOptions;
-
- /**
- * @class HTMLRenderer
- * @constructor
- */
- function HTMLRenderer(options) {
- var defaults = {
- blockTypes : DefaultBlockTypeSet,
- markupTypes : DefaultMarkupTypeSet,
- typeRenderers : {}
- };
- mergeWithOptions(this, defaults, options);
- }
-
- /**
- * @method willRenderType
- * @param type {Number|Type}
- * @param renderer the rendering function that returns a string of html
- * Registers custom rendering hooks for a type
- */
- HTMLRenderer.prototype.willRenderType = function(type, renderer) {
- if ('number' !== typeof type) {
- type = type.id;
- }
- this.typeRenderers[type] = renderer;
+ Toolbar.prototype.displayPrompt = function(prompt) {
+ var toolbar = this;
+ swapElements(toolbar.promptContainerElement, toolbar.buttonContainerElement);
+ toolbar.promptContainerElement.appendChild(prompt.element);
+ prompt.show(function() {
+ toolbar.dismissPrompt();
+ toolbar.updateForSelection(window.getSelection());
+ });
+ toolbar.activePrompt = prompt;
};
- /**
- * @method rendererFor
- * @param model
- * @returns renderer
- * Returns an instance of a renderer for supplied model
- */
- HTMLRenderer.prototype.rendererFor = function(model) {
- var type = this.blockTypes.findById(model.type);
- if (type === Type.EMBED) {
- return new HTMLEmbedRenderer();
+ Toolbar.prototype.dismissPrompt = function() {
+ var toolbar = this;
+ var activePrompt = toolbar.activePrompt;
+ if (activePrompt) {
+ activePrompt.hide();
+ swapElements(toolbar.buttonContainerElement, toolbar.promptContainerElement);
+ toolbar.activePrompt = null;
}
- return new HTMLElementRenderer({ type: type, markupTypes: this.markupTypes });
};
- /**
- * @method render
- * @param model
- * @return String html
- */
- HTMLRenderer.prototype.render = function(model) {
- var html = '';
- var len = model && model.length;
- var i, item, renderer, renderHook, itemHtml;
-
- for (i = 0; i < len; i++) {
- item = model[i];
- renderer = this.rendererFor(item);
- renderHook = this.typeRenderers[item.type];
- itemHtml = renderHook ? renderHook.call(renderer, item) : renderer.render(item);
- if (itemHtml) { html += itemHtml; }
+ Toolbar.prototype.updateForSelection = function(selection) {
+ var toolbar = this;
+ if (selection.isCollapsed) {
+ toolbar.hide();
+ } else {
+ toolbar.show();
+ toolbar.positionToContent(selection.getRangeAt(0));
+ updateButtonsForSelection(toolbar.buttons, selection);
}
- return html;
};
- __exports__["default"] = HTMLRenderer;
- });
-define("content-kit-compiler/types/default-types",
- ["./type-set","./type","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var TypeSet = __dependency1__["default"];
- var Type = __dependency2__["default"];
-
- /**
- * Default supported block types
- */
- var DefaultBlockTypeSet = new TypeSet([
- new Type({ tag: 'p', name: 'text' }),
- new Type({ tag: 'h2', name: 'heading' }),
- new Type({ tag: 'h3', name: 'subheading' }),
- new Type({ tag: 'img', name: 'image' }),
- new Type({ tag: 'blockquote', name: 'quote' }),
- new Type({ tag: 'ul', name: 'list' }),
- new Type({ tag: 'ol', name: 'ordered list' }),
- new Type({ name: 'embed' })
- ]);
-
- /**
- * Default supported markup types
- */
- var DefaultMarkupTypeSet = new TypeSet([
- new Type({ tag: 'b', name: 'bold' }),
- new Type({ tag: 'i', name: 'italic' }),
- new Type({ tag: 'u', name: 'underline' }),
- new Type({ tag: 'a', name: 'link' }),
- new Type({ tag: 'br', name: 'break' }),
- new Type({ tag: 'li', name: 'list item' }),
- new Type({ tag: 'sub', name: 'subscript' }),
- new Type({ tag: 'sup', name: 'superscript' })
- ]);
+ Toolbar.prototype.positionToContent = function(content) {
+ var directions = ToolbarDirection;
+ var positioningMethod;
+ switch(this.direction) {
+ case directions.RIGHT:
+ positioningMethod = positionElementToRightOf;
+ break;
+ default:
+ positioningMethod = positionElementCenteredAbove;
+ }
+ positioningMethod(this.element, content);
+ };
- __exports__.DefaultBlockTypeSet = DefaultBlockTypeSet;
- __exports__.DefaultMarkupTypeSet = DefaultMarkupTypeSet;
+ __exports__["default"] = Toolbar;
});
-define("content-kit-compiler/types/type-set",
- ["./type","exports"],
- function(__dependency1__, __exports__) {
+define("content-kit-editor/views/tooltip",
+ ["./view","../../content-kit-utils/object-utils","../utils/element-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
- var Type = __dependency1__["default"];
-
- /**
- * @class TypeSet
- * @private
- * @constructor
- * A Set of Types
- */
- function TypeSet(types) {
- var len = types && types.length, i;
+ var View = __dependency1__["default"];
+ var inherit = __dependency2__.inherit;
+ var positionElementCenteredBelow = __dependency3__.positionElementCenteredBelow;
+ var getEventTargetMatchingTag = __dependency3__.getEventTargetMatchingTag;
- this._autoId = 1; // Auto-increment id counter
- this.idLookup = {}; // Hash cache for finding by id
- this.tagLookup = {}; // Hash cache for finding by tag
+ function Tooltip(options) {
+ var tooltip = this;
+ var rootElement = options.rootElement;
+ var delay = options.delay || 200;
+ var timeout;
+ options.classNames = ['ck-tooltip'];
+ View.call(tooltip, options);
- for (i = 0; i < len; i++) {
- this.addType(types[i]);
- }
+ rootElement.addEventListener('mouseover', function(e) {
+ var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement);
+ if (target && target.isContentEditable) {
+ timeout = setTimeout(function() {
+ tooltip.showLink(target.href, target);
+ }, delay);
+ }
+ });
+
+ rootElement.addEventListener('mouseout', function(e) {
+ clearTimeout(timeout);
+ var toElement = e.toElement || e.relatedTarget;
+ if (toElement && toElement.className !== tooltip.element.className) {
+ tooltip.hide();
+ }
+ });
}
+ inherit(Tooltip, View);
- TypeSet.prototype = {
- /**
- * Adds a type to the set
- */
- addType: function(type) {
- if (type instanceof Type) {
- this[type.name] = type;
- if (type.id === undefined) {
- type.id = this._autoId++;
- }
- this.idLookup[type.id] = type;
- if (type.tag) {
- this.tagLookup[type.tag] = type;
- }
- return type;
- }
- },
+ Tooltip.prototype.showMessage = function(message, element) {
+ var tooltip = this;
+ var tooltipElement = tooltip.element;
+ tooltipElement.innerHTML = message;
+ tooltip.show();
+ positionElementCenteredBelow(tooltipElement, element);
+ };
- /**
- * Returns type info for a given Node
- */
- findByNode: function(node) {
- return this.findByTag(node.tagName);
- },
- /**
- * Returns type info for a given tag
- */
- findByTag: function(tag) {
- return this.tagLookup[tag.toLowerCase()];
- },
- /**
- * Returns type info for a given id
- */
- findById: function(id) {
- return this.idLookup[id];
- }
+ Tooltip.prototype.showLink = function(link, element) {
+ var message = '' + link + '';
+ this.showMessage(message, element);
};
- __exports__["default"] = TypeSet;
+ __exports__["default"] = Tooltip;
});
-define("content-kit-compiler/types/type",
- ["../../content-kit-utils/string-utils","exports"],
- function(__dependency1__, __exports__) {
+define("content-kit-editor/views/view",
+ ["exports"],
+ function(__exports__) {
"use strict";
- var underscore = __dependency1__.underscore;
+ function View(options) {
+ this.tagName = options.tagName || 'div';
+ this.classNames = options.classNames || [];
+ this.element = document.createElement(this.tagName);
+ this.element.className = this.classNames.join(' ');
+ this.container = options.container || document.body;
+ this.isShowing = false;
+ }
- /**
- * @class Type
- * @constructor
- * Contains meta info about a node type (id, name, tag, etc).
- */
- function Type(options) {
- if (options) {
- this.name = underscore(options.name || options.tag).toUpperCase();
- if (options.id !== undefined) {
- this.id = options.id;
+ View.prototype = {
+ show: function() {
+ var view = this;
+ if(!view.isShowing) {
+ view.container.appendChild(view.element);
+ view.isShowing = true;
+ return true;
}
- if (options.tag) {
- this.tag = options.tag.toLowerCase();
- this.selfClosing = /^(br|img|hr|meta|link|embed)$/i.test(this.tag);
+ },
+ hide: function() {
+ var view = this;
+ if(view.isShowing) {
+ view.container.removeChild(view.element);
+ view.isShowing = false;
+ return true;
}
-
- // Register the type as constant
- Type[this.name] = this;
+ },
+ focus: function() {
+ this.element.focus();
+ },
+ addClass: function(className) {
+ this.classNames.push(className);
+ this.element.className = this.classNames.join(' ');
+ },
+ removeClass: function(className) {
+ this.classNames.splice(this.classNames.indexOf(className), 1);
+ this.element.className = this.classNames.join(' ');
}
- }
+ };
- __exports__["default"] = Type;
+ __exports__["default"] = View;
});
define("content-kit-compiler/renderers/embeds/instagram",
["exports"],
diff --git a/src/js/content-kit-editor/commands/text-format.js b/src/js/content-kit-editor/commands/text-format.js
index ce3a877ee..6c40ff05d 100644
--- a/src/js/content-kit-editor/commands/text-format.js
+++ b/src/js/content-kit-editor/commands/text-format.js
@@ -12,6 +12,7 @@ inherit(TextFormatCommand, Command);
TextFormatCommand.prototype = {
exec: function(value) {
document.execCommand(this.action, false, value || null);
+ this.editorContext.syncModelAt(this.editorContext.getCurrentBlockIndex());
},
unexec: function(value) {
document.execCommand(this.removeAction, false, value || null);
diff --git a/src/js/content-kit-editor/editor-factory.js b/src/js/content-kit-editor/editor/editor-factory.js
similarity index 84%
rename from src/js/content-kit-editor/editor-factory.js
rename to src/js/content-kit-editor/editor/editor-factory.js
index f96e20cb2..a2bd1e9fd 100644
--- a/src/js/content-kit-editor/editor-factory.js
+++ b/src/js/content-kit-editor/editor/editor-factory.js
@@ -1,7 +1,7 @@
import Editor from './editor';
-import { TextFormatCommands, EmbedCommands } from './commands/commands';
-import { Tags } from './constants';
-import { merge } from '../content-kit-utils/object-utils';
+import { TextFormatCommands, EmbedCommands } from '../commands/commands';
+import { Tags } from '../constants';
+import { merge } from '../../content-kit-utils/object-utils';
var defaults = {
defaultFormatter: Tags.PARAGRAPH,
diff --git a/src/js/content-kit-editor/editor-html-renderer.js b/src/js/content-kit-editor/editor/editor-html-renderer.js
similarity index 85%
rename from src/js/content-kit-editor/editor-html-renderer.js
rename to src/js/content-kit-editor/editor/editor-html-renderer.js
index 5c7a77496..f6e7064a2 100644
--- a/src/js/content-kit-editor/editor-html-renderer.js
+++ b/src/js/content-kit-editor/editor/editor-html-renderer.js
@@ -1,6 +1,6 @@
-import HTMLRenderer from '../content-kit-compiler/renderers/html-renderer';
-import Type from '../content-kit-compiler/types/type';
-import { inherit } from '../content-kit-utils/object-utils';
+import HTMLRenderer from '../../content-kit-compiler/renderers/html-renderer';
+import Type from '../../content-kit-compiler/types/type';
+import { inherit } from '../../content-kit-utils/object-utils';
function embedRenderer(model) {
var embedAttrs = model.attributes;
diff --git a/src/js/content-kit-editor/editor.js b/src/js/content-kit-editor/editor/editor.js
similarity index 63%
rename from src/js/content-kit-editor/editor.js
rename to src/js/content-kit-editor/editor/editor.js
index b5faf55c3..61d8a261a 100644
--- a/src/js/content-kit-editor/editor.js
+++ b/src/js/content-kit-editor/editor/editor.js
@@ -1,41 +1,23 @@
-import TextFormatToolbar from './views/text-format-toolbar';
-import Tooltip from './views/tooltip';
-import EmbedIntent from './views/embed-intent';
-import UnorderedListCommand from './commands/unordered-list';
-import OrderedListCommand from './commands/ordered-list';
-import TextFormatCommand from './commands/text-format';
-import { Tags, RootTags, Keycodes, RegEx } from './constants';
-import { moveCursorToBeginningOfSelection, getSelectionTagName, getSelectionBlockElement, getSelectionBlockTagName } from './utils/selection-utils';
-import Compiler from '../content-kit-compiler/compiler';
-import TextModel from '../content-kit-compiler/models/text';
-import Type from '../content-kit-compiler/types/type';
-import { toArray } from '../content-kit-utils/array-utils';
-import { merge } from '../content-kit-utils/object-utils';
import EditorHTMLRenderer from './editor-html-renderer';
+import TextFormatToolbar from '../views/text-format-toolbar';
+import Tooltip from '../views/tooltip';
+import EmbedIntent from '../views/embed-intent';
+import UnorderedListCommand from '../commands/unordered-list';
+import OrderedListCommand from '../commands/ordered-list';
+import TextFormatCommand from '../commands/text-format';
+import { Tags, RootTags, Keycodes, RegEx } from '../constants';
+import { moveCursorToBeginningOfSelection, getSelectionTagName, getSelectionBlockElement, getSelectionBlockTagName } from '../utils/selection-utils';
+import { cleanPastedContent } from '../utils/paste-utils';
+import Compiler from '../../content-kit-compiler/compiler';
+import TextModel from '../../content-kit-compiler/models/text';
+import Type from '../../content-kit-compiler/types/type';
+import { toArray } from '../../content-kit-utils/array-utils';
+import { merge } from '../../content-kit-utils/object-utils';
+
var editorClassName = 'ck-editor';
var editorClassNameRegExp = new RegExp(editorClassName);
-function plainTextToBlocks(plainText, blockTag) {
- var blocks = plainText.split(RegEx.NEWLINE),
- len = blocks.length,
- block, openTag, closeTag, content, i;
- if(len < 2) {
- return plainText;
- } else {
- content = '';
- openTag = '<' + blockTag + '>';
- closeTag = '' + blockTag + '>';
- for(i=0; i -1) {
+ var blockElements = toArray(this.element.children);
+ var parsedBlockModel = this.compiler.parser.parseBlock(blockElements[index]);
+ this.model[index] = parsedBlockModel;
+
+ // TODO: event subscription
+ ContentKitDemo.syncCodePane(this);
+ }
};
Editor.prototype.syncVisualAt = function(index) {
- var blockModel = this.model[index];
- var html = this.compiler.render([blockModel]);
- var blockElements = toArray(this.element.children);
- var element = blockElements[index];
- element.innerHTML = html;
+ if (index > -1) {
+ var blockModel = this.model[index];
+ var html = this.compiler.render([blockModel]);
+ var blockElements = toArray(this.element.children);
+ var element = blockElements[index];
+ element.innerHTML = html;
+ }
};
Editor.prototype.getCurrentBlockIndex = function() {
diff --git a/src/js/content-kit-editor/utils/paste-utils.js b/src/js/content-kit-editor/utils/paste-utils.js
new file mode 100644
index 000000000..0e428df29
--- /dev/null
+++ b/src/js/content-kit-editor/utils/paste-utils.js
@@ -0,0 +1,32 @@
+import { RegEx } from '../constants';
+
+function plainTextToBlocks(plainText, tag) {
+ var blocks = plainText.split(RegEx.NEWLINE),
+ len = blocks.length,
+ block, openTag, closeTag, content, i;
+ if(len < 2) {
+ return plainText;
+ } else {
+ content = '';
+ openTag = '<' + tag + '>';
+ closeTag = '' + tag + '>';
+ for(i = 0; i < len; ++i) {
+ block = blocks[i];
+ if(block !== '') {
+ content += openTag + block + closeTag;
+ }
+ }
+ return content;
+ }
+}
+
+function cleanPastedContent(event, defaultBlockTag) {
+ event.preventDefault();
+ var data = event.clipboardData, plainText;
+ if(data && data.getData) {
+ plainText = data.getData('text/plain');
+ return plainTextToBlocks(plainText, defaultBlockTag);
+ }
+}
+
+export { cleanPastedContent };
diff --git a/src/js/content-kit-editor/views/text-format-toolbar.js b/src/js/content-kit-editor/views/text-format-toolbar.js
index a08d205da..4b40dc4ac 100644
--- a/src/js/content-kit-editor/views/text-format-toolbar.js
+++ b/src/js/content-kit-editor/views/text-format-toolbar.js
@@ -3,11 +3,20 @@ import { inherit } from '../../content-kit-utils/object-utils';
import { selectionIsEditable, selectionIsInElement } from '../utils/selection-utils';
import { Keycodes } from '../constants';
+function handleTextSelection(toolbar) {
+ var selection = window.getSelection();
+ if (selection.isCollapsed || !selectionIsEditable(selection) || selection.toString().trim() === '' || !selectionIsInElement(selection, toolbar.rootElement)) {
+ toolbar.hide();
+ } else {
+ toolbar.updateForSelection(selection);
+ }
+}
+
function TextFormatToolbar(options) {
var toolbar = this;
Toolbar.call(this, options);
toolbar.rootElement = options.rootElement;
- toolbar.rootElement.addEventListener('keyup', function() { toolbar.handleTextSelection(); });
+ toolbar.rootElement.addEventListener('keyup', function() { handleTextSelection(toolbar); });
document.addEventListener('keyup', function(e) {
if (e.keyCode === Keycodes.ESC) {
@@ -16,7 +25,7 @@ function TextFormatToolbar(options) {
});
document.addEventListener('mouseup', function() {
- setTimeout(function() { toolbar.handleTextSelection(); });
+ setTimeout(function() { handleTextSelection(toolbar); });
});
window.addEventListener('resize', function() {
@@ -28,14 +37,4 @@ function TextFormatToolbar(options) {
}
inherit(TextFormatToolbar, Toolbar);
-TextFormatToolbar.prototype.handleTextSelection = function() {
- var toolbar = this;
- var selection = window.getSelection();
- if (selection.isCollapsed || !selectionIsEditable(selection) || selection.toString().trim() === '' || !selectionIsInElement(selection, toolbar.rootElement)) {
- toolbar.hide();
- } else {
- toolbar.updateForSelection(selection);
- }
-};
-
export default TextFormatToolbar;
diff --git a/src/js/content-kit-utils/node-utils.js b/src/js/content-kit-utils/node-utils.js
index 5f9f1ac05..9315cda8f 100644
--- a/src/js/content-kit-utils/node-utils.js
+++ b/src/js/content-kit-utils/node-utils.js
@@ -58,7 +58,7 @@ function attributesForNode(node /*,blacklist*/) {
for (i = 0; i < len; i++) {
attr = attrs[i];
name = attr.name;
- if (attr.specified) {
+ if (attr.specified && attr.value) {
//if (blacklist && name in blacklist)) { continue; }
hash = hash || {};
hash[name] = attr.value;
diff --git a/src/js/content-kit.js b/src/js/content-kit.js
index 94a129b52..64f8a6c91 100644
--- a/src/js/content-kit.js
+++ b/src/js/content-kit.js
@@ -7,7 +7,7 @@ import Compiler from './content-kit-compiler/compiler';
import HTMLParser from './content-kit-compiler/parsers/html-parser';
import HTMLRenderer from './content-kit-compiler/renderers/html-renderer';
-import EditorFactory from './content-kit-editor/editor-factory';
+import EditorFactory from './content-kit-editor/editor/editor-factory';
var ContentKit = {};
ContentKit.Type = Type;