diff --git a/dist/content-kit-editor.js b/dist/content-kit-editor.js
index c8484ff96..4c74bbb78 100644
--- a/dist/content-kit-editor.js
+++ b/dist/content-kit-editor.js
@@ -1,2549 +1,2508 @@
/**
* @overview content-kit-editor: A modern, minimalist WYSIWYG editor.
- * @version 0.1.2
+ * @version 0.1.3
* @author Garth Poitras (http://garthpoitras.com/)
* @license MIT
- * Last modified: Feb 26, 2015
*/
(function(window, document, undefined) {
- "use strict";
- var node_modules$content$kit$utils$src$string$utils$$RegExpTrim = /^\s+|\s+$/g;
- var node_modules$content$kit$utils$src$string$utils$$RegExpTrimLeft = /^\s+/;
- var node_modules$content$kit$utils$src$string$utils$$RegExpWSChars = /(\r\n|\n|\r|\t)/gm;
- var node_modules$content$kit$utils$src$string$utils$$RegExpMultiWS = /\s+/g;
- var node_modules$content$kit$utils$src$string$utils$$RegExpNonAlphaNum = /[^a-zA-Z\d]/g;
-
- /**
- * String.prototype.trim polyfill
- * Removes whitespace at beginning and end of string
- */
- function node_modules$content$kit$utils$src$string$utils$$trim(string) {
- return string ? (string + '').replace(node_modules$content$kit$utils$src$string$utils$$RegExpTrim, '') : '';
- }
-
- /**
- * String.prototype.trimLeft polyfill
- * Removes whitespace at beginning of string
- */
- function node_modules$content$kit$utils$src$string$utils$$trimLeft(string) {
- return string ? (string + '').replace(node_modules$content$kit$utils$src$string$utils$$RegExpTrimLeft, '') : '';
- }
-
- /**
- * Replaces non-alphanumeric chars with underscores
- */
- function node_modules$content$kit$utils$src$string$utils$$underscore(string) {
- return string ? node_modules$content$kit$utils$src$string$utils$$trim(string + '').replace(node_modules$content$kit$utils$src$string$utils$$RegExpNonAlphaNum, '_') : '';
- }
-
- /**
- * Cleans line breaks, tabs, then multiple occuring whitespaces.
- */
- function node_modules$content$kit$utils$src$string$utils$$sanitizeWhitespace(string) {
- return string ? (string + '').replace(node_modules$content$kit$utils$src$string$utils$$RegExpWSChars, '').replace(node_modules$content$kit$utils$src$string$utils$$RegExpMultiWS, ' ') : '';
- }
-
- /**
- * Injects a string into another string at the index specified
- */
- function node_modules$content$kit$utils$src$string$utils$$injectIntoString(string, injection, index) {
- return string.substr(0, index) + injection + string.substr(index);
- }
-
- /**
- * @class Type
- * @constructor
- * Contains meta info about a node type (id, name, tag, etc).
- */
- function node_modules$content$kit$compiler$src$types$type$$Type(options) {
- if (options) {
- this.name = node_modules$content$kit$utils$src$string$utils$$underscore(options.name || options.tag).toUpperCase();
- this.isTextType = options.isTextType !== undefined ? options.isTextType : true;
-
- 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);
- if (options.mappedTags) {
- this.mappedTags = options.mappedTags;
- }
- }
-
- // Register the type as constant
- node_modules$content$kit$compiler$src$types$type$$Type[this.name] = this;
- }
- }
-
- var node_modules$content$kit$compiler$src$types$type$$default = node_modules$content$kit$compiler$src$types$type$$Type;
- /**
- * @class Model
- * @constructor
- * @private
- */
- function $$model$$Model(options) {
- options = options || {};
- var type_name = options.type_name;
- var attributes = options.attributes;
-
- this.type = options.type || null;
- if (type_name) {
- this.type_name = type_name;
- }
- if (attributes) {
- this.attributes = attributes;
- }
- }
-
- /**
- * @method createWithType
- * @static
- * @param type Type
- * @param options Object
- */
- $$model$$Model.createWithType = function(type, options) {
- options = options || {};
- options.type = type.id;
- options.type_name = type.name;
- return new this(options);
- };
-
- var $$model$$default = $$model$$Model;
- /**
- * Merges defaults/options into an Object
- * Useful for constructors
- */
- function node_modules$content$kit$utils$src$object$utils$$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 node_modules$content$kit$utils$src$object$utils$$merge(original, updates) {
- return node_modules$content$kit$utils$src$object$utils$$mergeWithOptions(original, updates);
- }
-
- /**
- * Prototype inheritance helper
- */
- function node_modules$content$kit$utils$src$object$utils$$inherit(Subclass, Superclass) {
- for (var key in Superclass) {
- if (Superclass.hasOwnProperty(key)) {
- Subclass[key] = Superclass[key];
- }
- }
- Subclass.prototype = new Superclass();
- Subclass.constructor = Subclass;
- Subclass._super = Superclass;
- }
-
- /**
- * 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 node_modules$content$kit$compiler$src$models$block$$sortBlockMarkups(markups) {
- return markups.sort(function(a, b) {
- if (a.start === b.start && a.end === b.end) {
- return b.type - a.type;
- }
- return 0;
- });
- }
-
- /**
- * @class BlockModel
- * @constructor
- * @extends Model
- */
- function node_modules$content$kit$compiler$src$models$block$$BlockModel(options) {
- options = options || {};
- $$model$$default.call(this, options);
- this.value = options.value || '';
- this.markup = node_modules$content$kit$compiler$src$models$block$$sortBlockMarkups(options.markup || []);
- }
-
- node_modules$content$kit$utils$src$object$utils$$inherit(node_modules$content$kit$compiler$src$models$block$$BlockModel, $$model$$default);
-
- var node_modules$content$kit$compiler$src$models$block$$default = node_modules$content$kit$compiler$src$models$block$$BlockModel;
-
- /**
- * @class EmbedModel
- * @constructor
- * @extends Model
- * Massages data from an oEmbed response into an EmbedModel
- */
- function node_modules$content$kit$compiler$src$models$embed$$EmbedModel(options) {
- if (!options) { return null; }
-
- $$model$$default.call(this, {
- type: node_modules$content$kit$compiler$src$types$type$$default.EMBED.id,
- type_name: node_modules$content$kit$compiler$src$types$type$$default.EMBED.name,
- attributes: {}
- });
-
- // Massage the oEmbed data
- 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;
- }
-
- if (embedHtml && (embedType === 'rich' || embedType === 'video')) {
- attributes.html = embedHtml;
- }
- }
-
- var node_modules$content$kit$compiler$src$models$embed$$default = node_modules$content$kit$compiler$src$models$embed$$EmbedModel;
-
- /**
- * @class MarkupModel
- * @constructor
- * @extends Model
- */
- function $$$models$markup$$MarkupModel(options) {
- options = options || {};
- $$model$$default.call(this, options);
- this.start = options.start || 0;
- this.end = options.end || 0;
- }
-
- node_modules$content$kit$utils$src$object$utils$$inherit($$$models$markup$$MarkupModel, $$model$$default);
-
- var $$$models$markup$$default = $$$models$markup$$MarkupModel;
-
- /**
- * @class TypeSet
- * @private
- * @constructor
- * A Set of Types
- */
- function $$type$set$$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]);
- }
- }
-
- $$type$set$$TypeSet.prototype = {
- /**
- * Adds a type to the set
- */
- addType: function(type) {
- if (type instanceof node_modules$content$kit$compiler$src$types$type$$default) {
- 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;
- if (type.mappedTags) {
- for (var i = 0, len = type.mappedTags.length; i < len; i++) {
- this.tagLookup[type.mappedTags[i]] = type;
- }
- }
- }
- return type;
- }
- },
-
- /**
- * Returns type info for a given Node
- */
- findByNode: function(node) {
- if (node) {
- return this.findByTag(node.tagName);
- }
- },
- /**
- * Returns type info for a given tag
- */
- findByTag: function(tag) {
- if (tag) {
- return this.tagLookup[tag.toLowerCase()];
- }
- },
- /**
- * Returns type info for a given id
- */
- findById: function(id) {
- return this.idLookup[id];
- }
- };
-
- var $$type$set$$default = $$type$set$$TypeSet;
-
- /**
- * Default supported block types
- */
- var $$types$default$types$$DefaultBlockTypeSet = new $$type$set$$default([
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'p', name: 'paragraph' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'h2', name: 'heading' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'h3', name: 'subheading' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'img', name: 'image', isTextType: false }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'blockquote', name: 'quote' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'ul', name: 'list' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'ol', name: 'ordered list' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ name: 'embed', isTextType: false })
- ]);
-
- /**
- * Default supported markup types
- */
- var $$types$default$types$$DefaultMarkupTypeSet = new $$type$set$$default([
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'strong', name: 'bold', mappedTags: ['b'] }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'em', name: 'italic', mappedTags: ['i'] }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'u', name: 'underline' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'a', name: 'link' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'br', name: 'break' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'li', name: 'list item' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'sub', name: 'subscript' }),
- new node_modules$content$kit$compiler$src$types$type$$default({ tag: 'sup', name: 'superscript' })
- ]);
-
- /**
- * 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 node_modules$content$kit$utils$src$array$utils$$toArray(obj) {
- var array = [];
- var i = obj && obj.length >>> 0; // cast to Uint32
- while (i--) {
- array[i] = obj[i];
- }
- return array;
- }
-
- /**
- * Computes the sum of values in a (sparse) array
- */
- function node_modules$content$kit$utils$src$array$utils$$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;
- }
-
- /**
- * A document instance separate from the page's document. (if browser supports it)
- * Prevents images, scripts, and styles from executing while parsing nodes.
- */
- var node_modules$content$kit$utils$src$node$utils$$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 node_modules$content$kit$utils$src$node$utils$$createElement(type) {
- return node_modules$content$kit$utils$src$node$utils$$standaloneDocument.createElement(type);
- }
-
- /**
- * A reusable DOM Node for parsing html content.
- */
- var node_modules$content$kit$utils$src$node$utils$$DOMParsingNode = node_modules$content$kit$utils$src$node$utils$$createElement('div');
-
- /**
- * Returns plain-text of a `Node`
- */
- function node_modules$content$kit$utils$src$node$utils$$textOfNode(node) {
- var text = node.textContent || node.innerText;
- return text ? node_modules$content$kit$utils$src$string$utils$$sanitizeWhitespace(text) : '';
- }
-
- /**
- * Replaces a `Node` with its children
- */
- function node_modules$content$kit$utils$src$node$utils$$unwrapNode(node) {
- if (node.hasChildNodes()) {
- var children = node_modules$content$kit$utils$src$array$utils$$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 node_modules$content$kit$utils$src$node$utils$$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 && attr.value) {
- if (blacklist && (name in blacklist)) { continue; }
- hash = hash || {};
- hash[name] = attr.value;
- }
- }
- return hash;
- }
-
- var node_modules$content$kit$compiler$src$parsers$html$parser$$ELEMENT_NODE = window.Node && Node.ELEMENT_NODE || 1;
- var node_modules$content$kit$compiler$src$parsers$html$parser$$TEXT_NODE = window.Node && Node.TEXT_NODE || 3;
- var node_modules$content$kit$compiler$src$parsers$html$parser$$defaultAttributeBlacklist = { 'style' : 1, 'class' : 1 };
-
- /**
- * Returns the last block in the set or creates a default block if none exist yet.
- */
- function node_modules$content$kit$compiler$src$parsers$html$parser$$getLastBlockOrCreate(blocks) {
- var blockCount = blocks.length, block;
- if (blockCount) {
- block = blocks[blockCount - 1];
- } else {
- block = node_modules$content$kit$compiler$src$models$block$$default.createWithType(node_modules$content$kit$compiler$src$types$type$$default.PARAGRAPH);
- blocks.push(block);
- }
- return block;
- }
-
- /**
- * Helper to parse elements at the root that aren't blocks
- */
- function node_modules$content$kit$compiler$src$parsers$html$parser$$handleNonBlockAtRoot(parser, elementNode, blocks) {
- var block = node_modules$content$kit$compiler$src$parsers$html$parser$$getLastBlockOrCreate(blocks);
- var markup = parser.parseMarkupForElement(elementNode, block.value.length);
- if (markup) {
- block.markup = block.markup.concat(markup);
- }
- block.value += node_modules$content$kit$utils$src$node$utils$$textOfNode(elementNode);
- }
-
- /**
- * @class HTMLParser
- * @constructor
- */
- function node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser(options) {
- var defaults = {
- blockTypes : $$types$default$types$$DefaultBlockTypeSet,
- markupTypes : $$types$default$types$$DefaultMarkupTypeSet,
- attributeBlacklist : node_modules$content$kit$compiler$src$parsers$html$parser$$defaultAttributeBlacklist,
- includeTypeNames : false
- };
- node_modules$content$kit$utils$src$object$utils$$mergeWithOptions(this, defaults, options);
- }
-
- /**
- * @method parse
- * @param html String of HTML content
- * @return Array Parsed JSON content array
- */
- node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser.prototype.parse = function(html) {
- node_modules$content$kit$utils$src$node$utils$$DOMParsingNode.innerHTML = node_modules$content$kit$utils$src$string$utils$$sanitizeWhitespace(html);
-
- var nodes = node_modules$content$kit$utils$src$array$utils$$toArray(node_modules$content$kit$utils$src$node$utils$$DOMParsingNode.childNodes);
- var nodeCount = nodes.length;
- var blocks = [];
- var i, node, nodeType, block, text;
-
- for (i = 0; i < nodeCount; i++) {
- node = nodes[i];
- nodeType = node.nodeType;
-
- if (nodeType === node_modules$content$kit$compiler$src$parsers$html$parser$$ELEMENT_NODE) {
- block = this.serializeBlockNode(node);
- if (block) {
- blocks.push(block);
- } else {
- node_modules$content$kit$compiler$src$parsers$html$parser$$handleNonBlockAtRoot(this, node, blocks);
- }
- } else if (nodeType === node_modules$content$kit$compiler$src$parsers$html$parser$$TEXT_NODE) {
- text = node.nodeValue;
- if (node_modules$content$kit$utils$src$string$utils$$trim(text)) {
- block = node_modules$content$kit$compiler$src$parsers$html$parser$$getLastBlockOrCreate(blocks);
- block.value += text;
- }
- }
- }
-
- return blocks;
- };
-
- /**
- * @method parseMarkupForElement
- * @param node element node to parse
- * @return {Array} parsed markups
- */
- node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser.prototype.parseMarkupForElement = function(node, startOffset) {
- var index = 0;
- var markups = [];
- var currentNode, nodeType, markup;
-
- startOffset = startOffset || 0;
- node = node.cloneNode(true);
- markup = this.serializeMarkupNode(node, startOffset);
- if (markup) { markups.push(markup); }
-
- while (node.hasChildNodes()) {
- currentNode = node.firstChild;
- nodeType = currentNode.nodeType;
-
- if (nodeType === node_modules$content$kit$compiler$src$parsers$html$parser$$ELEMENT_NODE) {
- markup = this.serializeMarkupNode(currentNode, startOffset);
- if (markup) { markups.push(markup); }
- node_modules$content$kit$utils$src$node$utils$$unwrapNode(currentNode);
- } else if (nodeType === node_modules$content$kit$compiler$src$parsers$html$parser$$TEXT_NODE) {
- var text = node_modules$content$kit$utils$src$string$utils$$sanitizeWhitespace(currentNode.nodeValue);
- if (index === 0) { text = node_modules$content$kit$utils$src$string$utils$$trimLeft(text); }
- if (text) { startOffset += text.length; }
- }
-
- currentNode.parentNode.removeChild(currentNode);
- index++;
- }
-
- return markups;
- };
-
- /**
- * @method serializeBlockNode
- * @param node element node to parse
- * @return {BlockModel} parsed block model
- * Serializes a single block type node into a model
- */
- node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser.prototype.serializeBlockNode = function(node) {
- var type = this.blockTypes.findByNode(node);
- if (type) {
- return new node_modules$content$kit$compiler$src$models$block$$default({
- type : type.id,
- type_name : this.includeTypeNames && type.name,
- value : node_modules$content$kit$utils$src$string$utils$$trim(node_modules$content$kit$utils$src$node$utils$$textOfNode(node)),
- attributes : node_modules$content$kit$utils$src$node$utils$$attributesForNode(node, this.attributeBlacklist),
- markup : this.parseMarkupForElement(node)
- });
- }
- };
-
- /**
- * @method serializeMarkupNode
- * @param node element node to parse
- * @param startIndex
- * @return {MarkupModel} markup model
- * Serializes markup of a single html element node (no child elements)
- */
- node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser.prototype.serializeMarkupNode = function(node, startIndex) {
- var type = this.markupTypes.findByNode(node);
- var selfClosing, endIndex;
-
- if (type) {
- selfClosing = type.selfClosing;
- if (!selfClosing && !node.hasChildNodes()) { return; } // check for empty nodes
-
- endIndex = startIndex + (selfClosing ? 0 : node_modules$content$kit$utils$src$node$utils$$textOfNode(node).length);
- if (endIndex > startIndex || (selfClosing && endIndex === startIndex)) { // check for empty nodes
- return new $$$models$markup$$default({
- type : type.id,
- type_name : this.includeTypeNames && type.name,
- start : startIndex,
- end : endIndex,
- attributes : node_modules$content$kit$utils$src$node$utils$$attributesForNode(node, this.attributeBlacklist)
- });
- }
- }
- };
-
- var node_modules$content$kit$compiler$src$parsers$html$parser$$default = node_modules$content$kit$compiler$src$parsers$html$parser$$HTMLParser;
-
- /**
- * Builds an opening html tag. i.e. ''
- */
- function $$html$element$renderer$$createOpeningTag(tagName, attributes, selfClosing) {
- var tag = '<' + tagName;
- for (var attr in attributes) {
- if (attributes.hasOwnProperty(attr)) {
- tag += ' ' + attr + '="' + attributes[attr] + '"';
- }
- }
- if (selfClosing) { tag += '/'; }
- tag += '>';
- return tag;
- }
-
- /**
- * Builds a closing html tag. i.e. '
'
- */
- function $$html$element$renderer$$createCloseTag(tagName) {
- return '' + tagName + '>';
- }
-
- /**
- * @class HTMLElementRenderer
- * @constructor
- */
- function $$html$element$renderer$$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.
- */
- $$html$element$renderer$$HTMLElementRenderer.prototype.render = function(model) {
- var html = '';
- var type = this.type;
- var tagName = type.tag;
- var selfClosing = type.selfClosing;
-
- if (tagName) {
- html += $$html$element$renderer$$createOpeningTag(tagName, model.attributes, selfClosing);
- }
- if (!selfClosing) {
- html += this.renderMarkup(model.value, model.markup);
- if (tagName) {
- html += $$html$element$renderer$$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.
- */
- $$html$element$renderer$$HTMLElementRenderer.prototype.renderMarkup = function(text, markups) {
- var parsedTagsIndexes = [];
- var 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 = $$html$element$renderer$$createOpeningTag(tagName, markup.attributes, selfClosing),
- parsedTagLengthAtIndex = parsedTagsIndexes[start] || 0,
- parsedTagLengthBeforeIndex = node_modules$content$kit$utils$src$array$utils$$sumSparseArray(parsedTagsIndexes.slice(0, start + 1));
-
- text = node_modules$content$kit$utils$src$string$utils$$injectIntoString(text, openTag, start + parsedTagLengthBeforeIndex);
- parsedTagsIndexes[start] = parsedTagLengthAtIndex + openTag.length;
-
- if (!selfClosing) {
- var closeTag = $$html$element$renderer$$createCloseTag(tagName);
- parsedTagLengthAtIndex = parsedTagsIndexes[end] || 0;
- parsedTagLengthBeforeIndex = node_modules$content$kit$utils$src$array$utils$$sumSparseArray(parsedTagsIndexes.slice(0, end));
- text = node_modules$content$kit$utils$src$string$utils$$injectIntoString(text, closeTag, end + parsedTagLengthBeforeIndex);
- parsedTagsIndexes[end] = parsedTagLengthAtIndex + closeTag.length;
- }
- }
-
- return text;
- };
-
- var $$html$element$renderer$$default = $$html$element$renderer$$HTMLElementRenderer;
- /**
- * @class HTMLEmbedRenderer
- * @constructor
- */
- function $$html$embed$renderer$$HTMLEmbedRenderer() {}
-
- /**
- * @method render
- * @param model a block model
- * @return String html
- */
- $$html$embed$renderer$$HTMLEmbedRenderer.prototype.render = function(model) {
- var attrs = model.attributes;
- return attrs && attrs.html || '';
- };
-
- var $$html$embed$renderer$$default = $$html$embed$renderer$$HTMLEmbedRenderer;
-
- /**
- * @class HTMLRenderer
- * @constructor
- */
- function node_modules$content$kit$compiler$src$renderers$html$renderer$$HTMLRenderer(options) {
- var defaults = {
- blockTypes : $$types$default$types$$DefaultBlockTypeSet,
- markupTypes : $$types$default$types$$DefaultMarkupTypeSet,
- typeRenderers : {}
- };
- node_modules$content$kit$utils$src$object$utils$$mergeWithOptions(this, defaults, options);
- }
-
- /**
- * @method rendererFor
- * @param block
- * @returns renderer
- * Returns an instance of a renderer for supplied block model
- */
- node_modules$content$kit$compiler$src$renderers$html$renderer$$HTMLRenderer.prototype.rendererFor = function(block) {
- var type = this.blockTypes.findById(block.type);
- if (type === node_modules$content$kit$compiler$src$types$type$$default.EMBED) {
- return new $$html$embed$renderer$$default();
- }
- return new $$html$element$renderer$$default({ type: type, markupTypes: this.markupTypes });
- };
-
- /**
- * @method render
- * @param model
- * @return String html
- */
- node_modules$content$kit$compiler$src$renderers$html$renderer$$HTMLRenderer.prototype.render = function(model) {
- var html = '';
- var len = model && model.length;
- var i, block, renderer, renderHook, blockHtml;
-
- for (i = 0; i < len; i++) {
- block = model[i];
- renderer = this.rendererFor(block);
- renderHook = this.typeRenderers[block.type];
- blockHtml = renderHook ? renderHook.call(renderer, block) : renderer.render(block);
- if (blockHtml) {
- html += blockHtml;
- }
- }
- return html;
- };
-
- var node_modules$content$kit$compiler$src$renderers$html$renderer$$default = node_modules$content$kit$compiler$src$renderers$html$renderer$$HTMLRenderer;
-
- /**
- * @class Compiler
- * @constructor
- * @param options
- */
- function node_modules$content$kit$compiler$src$compiler$$Compiler(options) {
- var parser = new node_modules$content$kit$compiler$src$parsers$html$parser$$default();
- var renderer = new node_modules$content$kit$compiler$src$renderers$html$renderer$$default();
- var defaults = {
- parser : parser,
- renderer : renderer,
- blockTypes : $$types$default$types$$DefaultBlockTypeSet,
- markupTypes : $$types$default$types$$DefaultMarkupTypeSet,
- includeTypeNames : false // Outputs `type_name:'HEADING'` etc. when parsing. Good for debugging.
- };
- node_modules$content$kit$utils$src$object$utils$$mergeWithOptions(this, defaults, options);
-
- // Reference the compiler settings
- this.parser.blockTypes = this.renderer.blockTypes = this.blockTypes;
- this.parser.markupTypes = this.renderer.markupTypes = this.markupTypes;
- this.parser.includeTypeNames = this.includeTypeNames;
- }
-
- /**
- * @method parse
- * @param input
- * @return Array
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.parse = function(input) {
- return this.parser.parse(input);
- };
-
- /**
- * @method render
- * @param model
- * @return String
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.render = function(model) {
- return this.renderer.render(model);
- };
-
- /**
- * @method rerender
- * @param input
- * @return String
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.rerender = function(input) {
- return this.render(this.parse(input));
- };
-
- /**
- * @method reparse
- * @param model
- * @return String
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.reparse = function(model) {
- return this.parse(this.render(model));
- };
-
- /**
- * @method registerBlockType
- * @param {Type} type
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.registerBlockType = function(type) {
- return this.blockTypes.addType(type);
- };
-
- /**
- * @method registerMarkupType
- * @param {Type} type
- */
- node_modules$content$kit$compiler$src$compiler$$Compiler.prototype.registerMarkupType = function(type) {
- return this.markupTypes.addType(type);
- };
-
- var node_modules$content$kit$compiler$src$compiler$$default = node_modules$content$kit$compiler$src$compiler$$Compiler;
- var $$youtube$$RegExVideoId = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;
-
- function $$youtube$$getVideoIdFromUrl(url) {
- var match = url && url.match($$youtube$$RegExVideoId);
- if (match && match[1].length === 11){
- return match[1];
- }
- return null;
- }
-
- function $$youtube$$YouTubeRenderer() {}
- $$youtube$$YouTubeRenderer.prototype.render = function(model) {
- var videoId = $$youtube$$getVideoIdFromUrl(model.attributes.url);
- var embedUrl = 'http://www.youtube.com/embed/' + videoId + '?controls=2&showinfo=0&color=white&theme=light';
- return '';
- };
-
- var $$youtube$$default = $$youtube$$YouTubeRenderer;
- function $$twitter$$TwitterRenderer() {}
- $$twitter$$TwitterRenderer.prototype.render = function(model) {
- return '';
- };
-
- var $$twitter$$default = $$twitter$$TwitterRenderer;
- function $$instagram$$InstagramRenderer() {}
- $$instagram$$InstagramRenderer.prototype.render = function(model) {
- return '';
- };
-
- var $$instagram$$default = $$instagram$$InstagramRenderer;
- function $$link$image$renderer$$LinkImageRenderer() {}
- $$link$image$renderer$$LinkImageRenderer.prototype.render = function(model) {
- return '';
- };
-
- var $$link$image$renderer$$default = $$link$image$renderer$$LinkImageRenderer;
-
- /**
- * A dictionary of supported embeds types that we'll custom render
- * for the editor, instead of the default oembed html.
- */
- var $$$renderers$editor$html$renderer$$embedRenderers = {
- YOUTUBE : new $$youtube$$default(),
- TWITTER : new $$twitter$$default(),
- INSTAGRAM : new $$instagram$$default(),
- LINK_IMAGE : new $$link$image$renderer$$default()
- };
-
- function $$$renderers$editor$html$renderer$$embedRenderer(model) {
- var embedAttrs = model.attributes;
- var embedType = embedAttrs.embed_type;
- var isVideo = embedType === 'video';
- var providerName = embedAttrs.provider_name;
- var customRendererId = providerName && providerName.toUpperCase();
- var customRenderer = $$$renderers$editor$html$renderer$$embedRenderers[customRendererId];
- if (!customRenderer && embedType === 'link' && embedAttrs.thumbnail) {
- customRenderer = $$$renderers$editor$html$renderer$$embedRenderers.LINK_IMAGE;
- }
- var renderer = customRenderer ? customRenderer : this;
-
- return '' +
- '
' +
- '
';
- }
-
- function $$$renderers$editor$html$renderer$$imageRenderer(model) {
- return '' +
- '' +
- '
';
- }
-
- var $$$renderers$editor$html$renderer$$typeRenderers = {};
- $$$renderers$editor$html$renderer$$typeRenderers[node_modules$content$kit$compiler$src$types$type$$default.EMBED.id] = $$$renderers$editor$html$renderer$$embedRenderer;
- $$$renderers$editor$html$renderer$$typeRenderers[node_modules$content$kit$compiler$src$types$type$$default.IMAGE.id] = $$$renderers$editor$html$renderer$$imageRenderer;
-
- /**
- * @class EditorHTMLRenderer
- * @constructor
- * Subclass of HTMLRenderer specifically for the Editor
- * Wraps interactive elements to add functionality
- */
- function $$$renderers$editor$html$renderer$$EditorHTMLRenderer() {
- node_modules$content$kit$compiler$src$renderers$html$renderer$$default.call(this, {
- typeRenderers: $$$renderers$editor$html$renderer$$typeRenderers
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$renderers$editor$html$renderer$$EditorHTMLRenderer, node_modules$content$kit$compiler$src$renderers$html$renderer$$default);
-
- var $$$renderers$editor$html$renderer$$default = $$$renderers$editor$html$renderer$$EditorHTMLRenderer;
- function $$view$$renderClasses(view) {
- var classNames = view.classNames;
- if (classNames && classNames.length) {
- view.element.className = classNames.join(' ');
- } else if(view.element.className) {
- view.element.removeAttribute('className');
- }
- }
-
- function $$view$$View(options) {
- options = options || {};
- this.tagName = options.tagName || 'div';
- this.classNames = options.classNames || [];
- this.element = document.createElement(this.tagName);
- this.container = options.container || document.body;
- this.isShowing = false;
- $$view$$renderClasses(this);
- }
-
- $$view$$View.prototype = {
- show: function() {
- var view = this;
- if(!view.isShowing) {
- view.container.appendChild(view.element);
- view.isShowing = true;
- return true;
- }
- },
- hide: function() {
- var view = this;
- if(view.isShowing) {
- view.container.removeChild(view.element);
- view.isShowing = false;
- return true;
- }
- },
- addClass: function(className) {
- var index = this.classNames && this.classNames.indexOf(className);
- if (index === -1) {
- this.classNames.push(className);
- $$view$$renderClasses(this);
- }
- },
- removeClass: function(className) {
- var index = this.classNames && this.classNames.indexOf(className);
- if (index > -1) {
- this.classNames.splice(index, 1);
- $$view$$renderClasses(this);
- }
- },
- setClasses: function(classNameArr) {
- this.classNames = classNameArr;
- $$view$$renderClasses(this);
- }
- };
-
- var $$view$$default = $$view$$View;
- var $$toolbar$button$$buttonClassName = 'ck-toolbar-btn';
-
- function $$toolbar$button$$ToolbarButton(options) {
- var button = this;
- var toolbar = options.toolbar;
- var command = options.command;
- var prompt = command.prompt;
- var element = document.createElement('button');
-
- button.element = element;
- button.command = command;
- button.isActive = false;
-
- element.title = command.name;
- element.className = $$toolbar$button$$buttonClassName;
- element.innerHTML = command.button;
- element.addEventListener('mouseup', function(e) {
- if (!button.isActive && prompt) {
- toolbar.displayPrompt(prompt);
- } else {
- command.exec();
- toolbar.updateForSelection();
- }
- e.stopPropagation();
- });
- }
-
- $$toolbar$button$$ToolbarButton.prototype = {
- setActive: function() {
- var button = this;
- if (!button.isActive) {
- button.element.className = $$toolbar$button$$buttonClassName + ' active';
- button.isActive = true;
- }
- },
- setInactive: function() {
- var button = this;
- if (button.isActive) {
- button.element.className = $$toolbar$button$$buttonClassName;
- button.isActive = false;
- }
- }
- };
-
- var $$toolbar$button$$default = $$toolbar$button$$ToolbarButton;
- function $$$utils$element$utils$$createDiv(className) {
- var div = document.createElement('div');
- if (className) {
- div.className = className;
- }
- return div;
- }
-
- function $$$utils$element$utils$$hideElement(element) {
- element.style.display = 'none';
- }
-
- function $$$utils$element$utils$$showElement(element) {
- element.style.display = 'block';
- }
-
- function $$$utils$element$utils$$swapElements(elementToShow, elementToHide) {
- $$$utils$element$utils$$hideElement(elementToHide);
- $$$utils$element$utils$$showElement(elementToShow);
- }
-
- function $$$utils$element$utils$$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.toLowerCase() === tag) {
- return target;
- }
- target = target.parentNode;
- }
- }
-
- function $$$utils$element$utils$$nodeIsDescendantOfElement(node, element) {
- var parentNode = node.parentNode;
- while(parentNode) {
- if (parentNode === element) {
- return true;
- }
- parentNode = parentNode.parentNode;
- }
- return false;
- }
-
- function $$$utils$element$utils$$elementContentIsEmpty(element) {
- var content = element && element.innerHTML;
- if (content) {
- return content === '' || content === '
';
- }
- return false;
- }
-
- function $$$utils$element$utils$$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 $$$utils$element$utils$$getElementComputedStyleNumericProp(element, prop) {
- return parseFloat(window.getComputedStyle(element)[prop]);
- }
-
- function $$$utils$element$utils$$positionElementToRect(element, rect, topOffset, leftOffset) {
- var relativeOffset = $$$utils$element$utils$$getElementRelativeOffset(element);
- var style = element.style;
- var round = Math.round;
- var left, top;
-
- topOffset = topOffset || 0;
- leftOffset = leftOffset || 0;
- left = round(rect.left - relativeOffset.left - leftOffset);
- top = round(rect.top - relativeOffset.top - topOffset);
- style.left = left + 'px';
- style.top = top + 'px';
- return { left: left, top: top };
- }
-
- function $$$utils$element$utils$$positionElementHorizontallyCenteredToRect(element, rect, topOffset) {
- var horizontalCenter = (element.offsetWidth / 2) - (rect.width / 2);
- return $$$utils$element$utils$$positionElementToRect(element, rect, topOffset, horizontalCenter);
- }
-
- function $$$utils$element$utils$$positionElementCenteredAbove(element, aboveElement) {
- var elementMargin = $$$utils$element$utils$$getElementComputedStyleNumericProp(element, 'marginBottom');
- return $$$utils$element$utils$$positionElementHorizontallyCenteredToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin);
- }
-
- function $$$utils$element$utils$$positionElementCenteredBelow(element, belowElement) {
- var elementMargin = $$$utils$element$utils$$getElementComputedStyleNumericProp(element, 'marginTop');
- return $$$utils$element$utils$$positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin);
- }
-
- function $$$utils$element$utils$$positionElementCenteredIn(element, inElement) {
- var verticalCenter = (inElement.offsetHeight / 2) - (element.offsetHeight / 2);
- return $$$utils$element$utils$$positionElementHorizontallyCenteredToRect(element, inElement.getBoundingClientRect(), -verticalCenter);
- }
-
- function $$$utils$element$utils$$positionElementToLeftOf(element, leftOfElement) {
- var verticalCenter = (leftOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
- var elementMargin = $$$utils$element$utils$$getElementComputedStyleNumericProp(element, 'marginRight');
- return $$$utils$element$utils$$positionElementToRect(element, leftOfElement.getBoundingClientRect(), -verticalCenter, element.offsetWidth + elementMargin);
- }
-
- function $$$utils$element$utils$$positionElementToRightOf(element, rightOfElement) {
- var verticalCenter = (rightOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
- var elementMargin = $$$utils$element$utils$$getElementComputedStyleNumericProp(element, 'marginLeft');
- var rightOfElementRect = rightOfElement.getBoundingClientRect();
- return $$$utils$element$utils$$positionElementToRect(element, rightOfElementRect, -verticalCenter, -rightOfElement.offsetWidth - elementMargin);
- }
-
- // TODO: remove, pass in Editor's current block set
- var $$$utils$selection$utils$$RootTags = [
- node_modules$content$kit$compiler$src$types$type$$default.PARAGRAPH.tag,
- node_modules$content$kit$compiler$src$types$type$$default.HEADING.tag,
- node_modules$content$kit$compiler$src$types$type$$default.SUBHEADING.tag,
- node_modules$content$kit$compiler$src$types$type$$default.QUOTE.tag,
- node_modules$content$kit$compiler$src$types$type$$default.LIST.tag,
- node_modules$content$kit$compiler$src$types$type$$default.ORDERED_LIST.tag
- ];
-
- var $$$utils$selection$utils$$SelectionDirection = {
- LEFT_TO_RIGHT : 1,
- RIGHT_TO_LEFT : 2,
- SAME_NODE : 3
- };
-
- function $$$utils$selection$utils$$getDirectionOfSelection(selection) {
- var node = selection.anchorNode;
- var position = node && node.compareDocumentPosition(selection.focusNode);
- if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
- return $$$utils$selection$utils$$SelectionDirection.LEFT_TO_RIGHT;
- } else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
- return $$$utils$selection$utils$$SelectionDirection.RIGHT_TO_LEFT;
- }
- return $$$utils$selection$utils$$SelectionDirection.SAME_NODE;
- }
-
- function $$$utils$selection$utils$$getSelectionElement(selection) {
- selection = selection || window.getSelection();
- var node = $$$utils$selection$utils$$getDirectionOfSelection(selection) === $$$utils$selection$utils$$SelectionDirection.LEFT_TO_RIGHT ? selection.anchorNode : selection.focusNode;
- return node && (node.nodeType === 3 ? node.parentNode : node);
- }
-
- function $$$utils$selection$utils$$getSelectionBlockElement(selection) {
- selection = selection || window.getSelection();
- var element = $$$utils$selection$utils$$getSelectionElement();
- var tag = element && element.tagName.toLowerCase();
- while (tag && $$$utils$selection$utils$$RootTags.indexOf(tag) === -1) {
- if (element.contentEditable === 'true') {
- return null; // Stop traversing up dom when hitting an editor element
- }
- element = element.parentNode;
- tag = element.tagName && element.tagName.toLowerCase();
- }
- return element;
- }
-
- function $$$utils$selection$utils$$getSelectionTagName() {
- var element = $$$utils$selection$utils$$getSelectionElement();
- return element ? element.tagName.toLowerCase() : null;
- }
-
- function $$$utils$selection$utils$$getSelectionBlockTagName() {
- var element = $$$utils$selection$utils$$getSelectionBlockElement();
- return element ? element.tagName && element.tagName.toLowerCase() : null;
- }
-
- function $$$utils$selection$utils$$tagsInSelection(selection) {
- var element = $$$utils$selection$utils$$getSelectionElement(selection);
- var tags = [];
- while(element) {
- if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
- if (element.tagName) {
- tags.push(element.tagName.toLowerCase());
- }
- element = element.parentNode;
- }
- return tags;
- }
-
- function $$$utils$selection$utils$$selectionIsInElement(selection, element) {
- var node = selection.anchorNode;
- return node && $$$utils$element$utils$$nodeIsDescendantOfElement(node, element);
- }
-
- function $$$utils$selection$utils$$selectionIsEditable(selection) {
- var el = $$$utils$selection$utils$$getSelectionBlockElement(selection);
- return el && el.isContentEditable;
- }
-
- function $$$utils$selection$utils$$restoreRange(range) {
- var selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- }
-
- function $$$utils$selection$utils$$selectNode(node) {
- var range = document.createRange();
- var selection = window.getSelection();
- range.setStart(node, 0);
- range.setEnd(node, node.length);
- selection.removeAllRanges();
- selection.addRange(range);
- }
-
- function $$$utils$selection$utils$$setCursorIndexInElement(element, index) {
- var range = document.createRange();
- var selection = window.getSelection();
- range.setStart(element, index);
- range.collapse(true);
- selection.removeAllRanges();
- selection.addRange(range);
- }
-
- function $$$utils$selection$utils$$setCursorToStartOfElement(element) {
- $$$utils$selection$utils$$setCursorIndexInElement(element, 0);
- }
-
- function $$$utils$selection$utils$$getCursorOffsetInElement(element) {
- // http://stackoverflow.com/questions/4811822/get-a-ranges-start-and-end-offsets-relative-to-its-parent-container/4812022#4812022
- var caretOffset = 0;
- var selection = window.getSelection();
- if (selection.rangeCount > 0) {
- var range = selection.getRangeAt(0);
- var preCaretRange = range.cloneRange();
- preCaretRange.selectNodeContents(element);
- preCaretRange.setEnd(range.endContainer, range.endOffset);
- caretOffset = preCaretRange.toString().length;
- }
- return caretOffset;
- }
-
- var $$toolbar$$ToolbarDirection = {
- TOP : 1,
- RIGHT : 2
- };
-
- function $$toolbar$$selectionContainsButtonsTag(selectedTags, buttonsTags) {
- return selectedTags.filter(function(tag) {
- return buttonsTags.indexOf(tag) > -1;
- }).length;
- }
-
- function $$toolbar$$updateButtonsForSelection(buttons, selection) {
- var selectedTags = $$$utils$selection$utils$$tagsInSelection(selection);
- var len = buttons.length;
- var i, button;
-
- for (i = 0; i < len; i++) {
- button = buttons[i];
- if ($$toolbar$$selectionContainsButtonsTag(selectedTags, button.command.mappedTags)) {
- button.setActive();
- } else {
- button.setInactive();
- }
- }
- }
-
- function $$toolbar$$Toolbar(options) {
- options = options || {};
- var toolbar = this;
- var commands = options.commands;
- var commandCount = commands && commands.length, i;
- options.classNames = ['ck-toolbar'];
- $$view$$default.call(toolbar, options);
-
- toolbar.setSticky(options.sticky || false);
- toolbar.setDirection(options.direction || $$toolbar$$ToolbarDirection.TOP);
- toolbar.editor = options.editor || null;
- toolbar.embedIntent = options.embedIntent || null;
- toolbar.activePrompt = null;
- toolbar.buttons = [];
-
- toolbar.contentElement = $$$utils$element$utils$$createDiv('ck-toolbar-content');
- toolbar.promptContainerElement = $$$utils$element$utils$$createDiv('ck-toolbar-prompt');
- toolbar.buttonContainerElement = $$$utils$element$utils$$createDiv('ck-toolbar-buttons');
- toolbar.contentElement.appendChild(toolbar.promptContainerElement);
- toolbar.contentElement.appendChild(toolbar.buttonContainerElement);
- toolbar.element.appendChild(toolbar.contentElement);
-
- for(i = 0; i < commandCount; i++) {
- this.addCommand(commands[i]);
- }
-
- // Closes prompt if displayed when changing selection
- document.addEventListener('mouseup', function() {
- toolbar.dismissPrompt();
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$toolbar$$Toolbar, $$view$$default);
-
- $$toolbar$$Toolbar.prototype.hide = function() {
- if ($$toolbar$$Toolbar._super.prototype.hide.call(this)) {
- var style = this.element.style;
- style.left = '';
- style.top = '';
- this.dismissPrompt();
- }
- };
-
- $$toolbar$$Toolbar.prototype.addCommand = function(command) {
- command.editorContext = this.editor;
- command.embedIntent = this.embedIntent;
- var button = new $$toolbar$button$$default({ command: command, toolbar: this });
- this.buttons.push(button);
- this.buttonContainerElement.appendChild(button.element);
- };
-
- $$toolbar$$Toolbar.prototype.displayPrompt = function(prompt) {
- var toolbar = this;
- $$$utils$element$utils$$swapElements(toolbar.promptContainerElement, toolbar.buttonContainerElement);
- toolbar.promptContainerElement.appendChild(prompt.element);
- prompt.show(function() {
- toolbar.dismissPrompt();
- toolbar.updateForSelection();
- });
- toolbar.activePrompt = prompt;
- };
-
- $$toolbar$$Toolbar.prototype.dismissPrompt = function() {
- var toolbar = this;
- var activePrompt = toolbar.activePrompt;
- if (activePrompt) {
- activePrompt.hide();
- $$$utils$element$utils$$swapElements(toolbar.buttonContainerElement, toolbar.promptContainerElement);
- toolbar.activePrompt = null;
- }
- };
-
- $$toolbar$$Toolbar.prototype.updateForSelection = function(selection) {
- var toolbar = this;
- selection = selection || window.getSelection();
- if (toolbar.sticky) {
- $$toolbar$$updateButtonsForSelection(toolbar.buttons, selection);
- } else if (!selection.isCollapsed) {
- toolbar.positionToContent(selection.getRangeAt(0));
- $$toolbar$$updateButtonsForSelection(toolbar.buttons, selection);
- }
- };
-
- $$toolbar$$Toolbar.prototype.positionToContent = function(content) {
- var directions = $$toolbar$$ToolbarDirection;
- var positioningMethod, position, sideEdgeOffset;
- switch(this.direction) {
- case directions.RIGHT:
- positioningMethod = $$$utils$element$utils$$positionElementToRightOf;
- break;
- default:
- positioningMethod = $$$utils$element$utils$$positionElementCenteredAbove;
- }
- position = positioningMethod(this.element, content);
- sideEdgeOffset = Math.min(Math.max(10, position.left), document.body.clientWidth - this.element.offsetWidth - 10);
- this.contentElement.style.transform = 'translateX(' + (sideEdgeOffset - position.left) + 'px)';
- };
-
- $$toolbar$$Toolbar.prototype.setDirection = function(direction) {
- this.direction = direction;
- if (direction === $$toolbar$$ToolbarDirection.RIGHT) {
- this.addClass('right');
- } else {
- this.removeClass('right');
- }
- };
-
- $$toolbar$$Toolbar.prototype.setSticky = function(sticky) {
- this.sticky = sticky;
- if (sticky) {
- this.addClass('sticky');
- this.element.removeAttribute('style'); // clears any prior positioning
- this.show();
- } else {
- this.removeClass('sticky');
- this.hide();
- }
- };
-
- $$toolbar$$Toolbar.prototype.toggleSticky = function() {
- this.setSticky(!this.sticky);
- };
-
- $$toolbar$$Toolbar.Direction = $$toolbar$$ToolbarDirection;
-
- var $$toolbar$$default = $$toolbar$$Toolbar;
-
- var $$$utils$keycodes$$default = {
- BKSP : 8,
- ENTER : 13,
- ESC : 27,
- DEL : 46,
- M : 77
- };
-
- function $$$views$text$format$toolbar$$selectionIsEditableByToolbar(selection, toolbar) {
- return $$$utils$selection$utils$$selectionIsEditable(selection) && $$$utils$selection$utils$$selectionIsInElement(selection, toolbar.rootElement);
- }
-
- function $$$views$text$format$toolbar$$handleTextSelection(toolbar) {
- var selection = window.getSelection();
- if (toolbar.sticky) {
- toolbar.updateForSelection($$$views$text$format$toolbar$$selectionIsEditableByToolbar(selection, toolbar) ? selection : null);
- } else {
- if (selection.isCollapsed || selection.toString().trim() === '' || !$$$views$text$format$toolbar$$selectionIsEditableByToolbar(selection, toolbar)) {
- toolbar.hide();
- } else {
- toolbar.show();
- toolbar.updateForSelection(selection);
- }
- }
- }
-
- function $$$views$text$format$toolbar$$TextFormatToolbar(options) {
- var toolbar = this;
- $$toolbar$$default.call(this, options);
- toolbar.rootElement = options.rootElement;
- toolbar.rootElement.addEventListener('keyup', function() { $$$views$text$format$toolbar$$handleTextSelection(toolbar); });
-
- document.addEventListener('mouseup', function() {
- setTimeout(function() {
- $$$views$text$format$toolbar$$handleTextSelection(toolbar);
- });
- });
-
- document.addEventListener('keyup', function(e) {
- var key = e.keyCode;
- if (key === 116) { //F5
- toolbar.toggleSticky();
- $$$views$text$format$toolbar$$handleTextSelection(toolbar);
- } else if (!toolbar.sticky && key === $$$utils$keycodes$$default.ESC) {
- toolbar.hide();
- }
- });
-
- window.addEventListener('resize', function() {
- if(!toolbar.sticky && toolbar.isShowing) {
- var activePromptRange = toolbar.activePrompt && toolbar.activePrompt.range;
- toolbar.positionToContent(activePromptRange ? activePromptRange : window.getSelection().getRangeAt(0));
- }
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$views$text$format$toolbar$$TextFormatToolbar, $$toolbar$$default);
-
- var $$$views$text$format$toolbar$$default = $$$views$text$format$toolbar$$TextFormatToolbar;
-
- function $$$views$tooltip$$Tooltip(options) {
- var tooltip = this;
- var rootElement = options.rootElement;
- var delay = options.delay || 200;
- var timeout;
- options.classNames = ['ck-tooltip'];
- $$view$$default.call(tooltip, options);
-
- rootElement.addEventListener('mouseover', function(e) {
- var target = $$$utils$element$utils$$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();
- }
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$views$tooltip$$Tooltip, $$view$$default);
-
- $$$views$tooltip$$Tooltip.prototype.showMessage = function(message, element) {
- var tooltip = this;
- var tooltipElement = tooltip.element;
- tooltipElement.innerHTML = message;
- tooltip.show();
- $$$utils$element$utils$$positionElementCenteredBelow(tooltipElement, element);
- };
-
- $$$views$tooltip$$Tooltip.prototype.showLink = function(link, element) {
- var message = '' + link + '';
- this.showMessage(message, element);
- };
-
- var $$$views$tooltip$$default = $$$views$tooltip$$Tooltip;
-
- var $$$views$embed$intent$$LayoutStyle = {
- GUTTER : 1,
- CENTERED : 2
- };
-
- function $$$views$embed$intent$$computeLayoutStyle(rootElement) {
- if (rootElement.getBoundingClientRect().left > 100) {
- return $$$views$embed$intent$$LayoutStyle.GUTTER;
- }
- return $$$views$embed$intent$$LayoutStyle.CENTERED;
- }
-
- function $$$views$embed$intent$$EmbedIntent(options) {
- var embedIntent = this;
- var rootElement = embedIntent.rootElement = options.rootElement;
- options.classNames = ['ck-embed-intent'];
- $$view$$default.call(embedIntent, options);
-
- embedIntent.isActive = false;
- embedIntent.editorContext = options.editorContext;
- embedIntent.loadingIndicator = $$$utils$element$utils$$createDiv('ck-embed-loading');
- embedIntent.button = document.createElement('button');
- embedIntent.button.className = 'ck-embed-intent-btn';
- embedIntent.button.title = 'Insert image or embed...';
- embedIntent.element.appendChild(embedIntent.button);
- embedIntent.button.addEventListener('mouseup', function(e) {
- if (embedIntent.isActive) {
- embedIntent.deactivate();
- } else {
- embedIntent.activate();
- }
- e.stopPropagation();
- });
-
- embedIntent.toolbar = new $$toolbar$$default({
- container: embedIntent.element,
- embedIntent: embedIntent,
- editor: embedIntent.editorContext,
- commands: options.commands,
- direction: $$toolbar$$default.Direction.RIGHT
- });
-
- function embedIntentHandler() {
- var blockElement = $$$utils$selection$utils$$getSelectionBlockElement();
- if (blockElement && $$$utils$element$utils$$elementContentIsEmpty(blockElement)) {
- embedIntent.showAt(blockElement);
- } else {
- embedIntent.hide();
- }
- }
-
- rootElement.addEventListener('keyup', embedIntentHandler);
- document.addEventListener('mouseup', function() {
- setTimeout(function() { embedIntentHandler(); });
- });
-
- document.addEventListener('keyup', function(e) {
- if (e.keyCode === $$$utils$keycodes$$default.ESC) {
- embedIntent.hide();
- }
- });
-
- window.addEventListener('resize', function() {
- if(embedIntent.isShowing) {
- embedIntent.reposition();
- }
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$views$embed$intent$$EmbedIntent, $$view$$default);
-
- $$$views$embed$intent$$EmbedIntent.prototype.hide = function() {
- if ($$$views$embed$intent$$EmbedIntent._super.prototype.hide.call(this)) {
- this.deactivate();
- }
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.showAt = function(node) {
- this.atNode = node;
- this.show();
- this.deactivate();
- this.reposition();
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.reposition = function() {
- if ($$$views$embed$intent$$computeLayoutStyle(this.rootElement) === $$$views$embed$intent$$LayoutStyle.GUTTER) {
- $$$utils$element$utils$$positionElementToLeftOf(this.element, this.atNode);
- } else {
- $$$utils$element$utils$$positionElementCenteredIn(this.element, this.atNode);
- }
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.activate = function() {
- if (!this.isActive) {
- this.addClass('activated');
- this.toolbar.show();
- this.isActive = true;
- }
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.deactivate = function() {
- if (this.isActive) {
- this.removeClass('activated');
- this.toolbar.hide();
- this.isActive = false;
- }
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.showLoading = function() {
- var embedIntent = this;
- var loadingIndicator = embedIntent.loadingIndicator;
- embedIntent.hide();
- embedIntent.atNode.appendChild(loadingIndicator);
- };
-
- $$$views$embed$intent$$EmbedIntent.prototype.hideLoading = function() {
- this.atNode.removeChild(this.loadingIndicator);
- };
-
- var $$$views$embed$intent$$default = $$$views$embed$intent$$EmbedIntent;
- function $$base$$Command(options) {
- options = options || {};
- var command = this;
- var name = options.name;
- var prompt = options.prompt;
- command.name = name;
- command.button = options.button || name;
- if (prompt) { command.prompt = prompt; }
- }
-
- $$base$$Command.prototype.exec = function() {};
-
- var $$base$$default = $$base$$Command;
-
- function $$text$format$$TextFormatCommand(options) {
- options = options || {};
- $$base$$default.call(this, options);
- this.tag = options.tag;
- this.mappedTags = options.mappedTags || [];
- this.mappedTags.push(this.tag);
- this.action = options.action || this.name;
- this.removeAction = options.removeAction || this.action;
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$text$format$$TextFormatCommand, $$base$$default);
-
- $$text$format$$TextFormatCommand.prototype = {
- exec: function(value) {
- document.execCommand(this.action, false, value || null);
- },
- unexec: function(value) {
- document.execCommand(this.removeAction, false, value || null);
+ var RegExpTrim = /^\s+|\s+$/g;
+ var RegExpTrimLeft = /^\s+/;
+ var RegExpWSChars = /(\r\n|\n|\r|\t)/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, 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);
+ }
+
+ function Type(options) {
+ if (options) {
+ this.name = underscore(options.name || options.tag).toUpperCase();
+ this.isTextType = options.isTextType !== undefined ? options.isTextType : true;
+
+ 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);
+ if (options.mappedTags) {
+ this.mappedTags = options.mappedTags;
+ }
}
- };
-
- var $$text$format$$default = $$text$format$$TextFormatCommand;
-
- var $$$commands$bold$$RegExpHeadingTag = /^(h1|h2|h3|h4|h5|h6)$/i;
-
- function $$$commands$bold$$BoldCommand() {
- $$text$format$$default.call(this, {
- name: 'bold',
- tag: node_modules$content$kit$compiler$src$types$type$$default.BOLD.tag,
- mappedTags: node_modules$content$kit$compiler$src$types$type$$default.BOLD.mappedTags,
- button: ''
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$bold$$BoldCommand, $$text$format$$default);
- $$$commands$bold$$BoldCommand.prototype.exec = function() {
- // Don't allow executing bold command on heading tags
- if (!$$$commands$bold$$RegExpHeadingTag.test($$$utils$selection$utils$$getSelectionBlockTagName())) {
- $$$commands$bold$$BoldCommand._super.prototype.exec.call(this);
- }
- };
+ // Register the type as constant
+ Type[this.name] = this;
+ }
+ }
+
+ var types_type = Type;
+
+ /**
+ * @class Model
+ * @constructor
+ * @private
+ */
+ function Model(options) {
+ options = options || {};
+ var type_name = options.type_name;
+ var attributes = options.attributes;
+
+ this.type = options.type || null;
+ if (type_name) {
+ this.type_name = type_name;
+ }
+ if (attributes) {
+ this.attributes = attributes;
+ }
+ }
+
+ /**
+ * @method createWithType
+ * @static
+ * @param type Type
+ * @param options Object
+ */
+ Model.createWithType = function(type, options) {
+ options = options || {};
+ options.type = type.id;
+ options.type_name = type.name;
+ return new this(options);
+ };
+
+ var models_model = Model;
+
+ /**
+ * 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) {
+ for (var key in Superclass) {
+ if (Superclass.hasOwnProperty(key)) {
+ Subclass[key] = Superclass[key];
+ }
+ }
+ Subclass.prototype = new Superclass();
+ Subclass.constructor = Subclass;
+ Subclass._super = Superclass;
+ }
+
+ 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;
+ });
+ }
+
+ /**
+ * @class BlockModel
+ * @constructor
+ * @extends Model
+ */
+ function BlockModel(options) {
+ options = options || {};
+ models_model.call(this, options);
+ this.value = options.value || '';
+ this.markup = sortBlockMarkups(options.markup || []);
+ }
+
+ inherit(BlockModel, models_model);
+
+ var models_block = BlockModel;
+
+ function EmbedModel(options) {
+ if (!options) { return null; }
+
+ models_model.call(this, {
+ type: types_type.EMBED.id,
+ type_name: types_type.EMBED.name,
+ attributes: {}
+ });
+
+ // Massage the oEmbed data
+ 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;
+ }
+
+ if (embedHtml && (embedType === 'rich' || embedType === 'video')) {
+ attributes.html = embedHtml;
+ }
+ }
+
+ var embed = EmbedModel;
+
+ /**
+ * Abstracted `document` between node.js and browser
+ */
+
+ var doc;
+
+ if (typeof exports === 'object') {
+ var jsdom = require('jsdom').jsdom;
+ doc = jsdom();
+ } else {
+ // A document instance separate from the html page document. (if browser supports it)
+ // Prevents images, scripts, and styles from executing while parsing
+ var implementation = document.implementation;
+ var createHTMLDocument = implementation.createHTMLDocument;
+ if (createHTMLDocument) {
+ doc = createHTMLDocument.call(implementation, '');
+ } else {
+ doc = document;
+ }
+ }
+
+ var parserDocument = doc;
+
+ function MarkupModel(options) {
+ options = options || {};
+ models_model.call(this, options);
+ this.start = options.start || 0;
+ this.end = options.end || 0;
+ }
- var $$$commands$bold$$default = $$$commands$bold$$BoldCommand;
+ inherit(MarkupModel, models_model);
- function $$$commands$italic$$ItalicCommand() {
- $$text$format$$default.call(this, {
- name: 'italic',
- tag: node_modules$content$kit$compiler$src$types$type$$default.ITALIC.tag,
- mappedTags: node_modules$content$kit$compiler$src$types$type$$default.ITALIC.mappedTags,
- button: ''
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$italic$$ItalicCommand, $$text$format$$default);
+ var models_markup = MarkupModel;
- var $$$commands$italic$$default = $$$commands$italic$$ItalicCommand;
+ function TypeSet(types) {
+ var len = types && types.length, i;
- var $$$views$prompt$$container = document.body;
- var $$$views$prompt$$hiliter = $$$utils$element$utils$$createDiv('ck-editor-hilite');
+ this._autoId = 1; // Auto-increment id counter
+ this.idLookup = {}; // Hash cache for finding by id
+ this.tagLookup = {}; // Hash cache for finding by tag
- function $$$views$prompt$$positionHiliteRange(range) {
- var rect = range.getBoundingClientRect();
- var style = $$$views$prompt$$hiliter.style;
- style.width = rect.width + 'px';
- style.height = rect.height + 'px';
- $$$utils$element$utils$$positionElementToRect($$$views$prompt$$hiliter, rect);
+ for (i = 0; i < len; i++) {
+ this.addType(types[i]);
}
+ }
- function $$$views$prompt$$Prompt(options) {
- var prompt = this;
- options.tagName = 'input';
- $$view$$default.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 === $$$utils$keycodes$$default.ENTER) {
- $$$utils$selection$utils$$restoreRange(prompt.range);
- prompt.command.exec(entry);
- if (prompt.onComplete) { prompt.onComplete(); }
+ TypeSet.prototype = {
+ /**
+ * Adds a type to the set
+ */
+ addType: function(type) {
+ if (type instanceof types_type) {
+ this[type.name] = type;
+ if (type.id === undefined) {
+ type.id = this._autoId++;
}
- });
-
- window.addEventListener('resize', function() {
- var activeHilite = $$$views$prompt$$hiliter.parentNode;
- var range = prompt.range;
- if(activeHilite && range) {
- $$$views$prompt$$positionHiliteRange(range);
+ this.idLookup[type.id] = type;
+ if (type.tag) {
+ this.tagLookup[type.tag] = type;
+ if (type.mappedTags) {
+ for (var i = 0, len = type.mappedTags.length; i < len; i++) {
+ this.tagLookup[type.mappedTags[i]] = type;
+ }
+ }
}
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$views$prompt$$Prompt, $$view$$default);
-
- $$$views$prompt$$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) {
- $$$views$prompt$$container.appendChild($$$views$prompt$$hiliter);
- $$$views$prompt$$positionHiliteRange(prompt.range);
- setTimeout(function(){ element.focus(); }); // defer focus (disrupts mouseup events)
- if (callback) { prompt.onComplete = callback; }
+ return type;
}
- };
+ },
- $$$views$prompt$$Prompt.prototype.hide = function() {
- if ($$$views$prompt$$hiliter.parentNode) {
- $$$views$prompt$$container.removeChild($$$views$prompt$$hiliter);
+ /**
+ * Returns type info for a given Node
+ */
+ findByNode: function(node) {
+ if (node) {
+ return this.findByTag(node.tagName);
}
- };
-
- var $$$views$prompt$$default = $$$views$prompt$$Prompt;
-
- var $$$commands$link$$RegExpHttp = /^https?:\/\//i;
-
- function $$$commands$link$$LinkCommand() {
- $$text$format$$default.call(this, {
- name: 'link',
- tag: node_modules$content$kit$compiler$src$types$type$$default.LINK.tag,
- action: 'createLink',
- removeAction: 'unlink',
- button: '',
- prompt: new $$$views$prompt$$default({
- command: this,
- placeholder: 'Enter a url, press return...'
- })
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$link$$LinkCommand, $$text$format$$default);
-
- $$$commands$link$$LinkCommand.prototype.exec = function(url) {
- if (!url) {
- return $$$commands$link$$LinkCommand._super.prototype.unexec.call(this);
+ },
+ /**
+ * Returns type info for a given tag
+ */
+ findByTag: function(tag) {
+ if (tag) {
+ return this.tagLookup[tag.toLowerCase()];
}
-
- if(this.tag === $$$utils$selection$utils$$getSelectionTagName()) {
- this.unexec();
- } else {
- if (!$$$commands$link$$RegExpHttp.test(url)) {
- url = 'http://' + url;
+ },
+ /**
+ * Returns type info for a given id
+ */
+ findById: function(id) {
+ return this.idLookup[id];
+ }
+ };
+
+ var type_set = TypeSet;
+
+ var DefaultBlockTypeSet = new type_set([
+ new types_type({ tag: 'p', name: 'paragraph' }),
+ new types_type({ tag: 'h2', name: 'heading' }),
+ new types_type({ tag: 'h3', name: 'subheading' }),
+ new types_type({ tag: 'img', name: 'image', isTextType: false }),
+ new types_type({ tag: 'blockquote', name: 'quote' }),
+ new types_type({ tag: 'ul', name: 'list' }),
+ new types_type({ tag: 'ol', name: 'ordered list' }),
+ new types_type({ name: 'embed', isTextType: false })
+ ]);
+
+ /**
+ * Default supported markup types
+ */
+ var DefaultMarkupTypeSet = new type_set([
+ new types_type({ tag: 'strong', name: 'bold', mappedTags: ['b'] }),
+ new types_type({ tag: 'em', name: 'italic', mappedTags: ['i'] }),
+ new types_type({ tag: 'u', name: 'underline' }),
+ new types_type({ tag: 'a', name: 'link' }),
+ new types_type({ tag: 'br', name: 'break' }),
+ new types_type({ tag: 'li', name: 'list item' }),
+ new types_type({ tag: 'sub', name: 'subscript' }),
+ new types_type({ tag: 'sup', name: 'superscript' })
+ ]);
+
+ /**
+ * 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 toArray(obj) {
+ var array = [];
+ var i = obj && obj.length >>> 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;
+ }
+
+ function textOfNode(node) {
+ var text = node.textContent || node.innerText;
+ return text ? sanitizeWhitespace(text) : '';
+ }
+
+ /**
+ * Replaces a `Node` with its children
+ */
+ function unwrapNode(node) {
+ if (node.hasChildNodes()) {
+ 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 && attr.value) {
+ if (blacklist && (name in blacklist)) { continue; }
+ hash = hash || {};
+ hash[name] = attr.value;
+ }
+ }
+ return hash;
+ }
+
+ var ELEMENT_NODE = 1;
+ var TEXT_NODE = 3;
+ var defaultAttributeBlacklist = { 'style' : 1, 'class' : 1 };
+
+ /**
+ * Returns the last block in the set or creates a default block if none exist yet.
+ */
+ function getLastBlockOrCreate(blocks) {
+ var blockCount = blocks.length, block;
+ if (blockCount) {
+ block = blocks[blockCount - 1];
+ } else {
+ block = models_block.createWithType(types_type.PARAGRAPH);
+ blocks.push(block);
+ }
+ return block;
+ }
+
+ /**
+ * Helper to parse elements at the root that aren't blocks
+ */
+ function handleNonBlockAtRoot(parser, elementNode, blocks) {
+ var block = getLastBlockOrCreate(blocks);
+ var markup = parser.parseMarkupForElement(elementNode, block.value.length);
+ if (markup) {
+ block.markup = block.markup.concat(markup);
+ }
+ block.value += textOfNode(elementNode);
+ }
+
+ /**
+ * @class HTMLParser
+ * @constructor
+ */
+ function HTMLParser(options) {
+ var defaults = {
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ attributeBlacklist : defaultAttributeBlacklist,
+ 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) {
+ var parsingNode = parserDocument.createElement('div');
+ parsingNode.innerHTML = sanitizeWhitespace(html);
+
+ var nodes = toArray(parsingNode.childNodes);
+ var nodeCount = nodes.length;
+ var blocks = [];
+ var i, node, nodeType, block, text;
+
+ for (i = 0; i < nodeCount; i++) {
+ node = nodes[i];
+ nodeType = node.nodeType;
+
+ if (nodeType === ELEMENT_NODE) {
+ block = this.serializeBlockNode(node);
+ if (block) {
+ blocks.push(block);
+ } else {
+ handleNonBlockAtRoot(this, node, blocks);
}
- $$$commands$link$$LinkCommand._super.prototype.exec.call(this, url);
- }
- };
-
- var $$$commands$link$$default = $$$commands$link$$LinkCommand;
-
- function $$format$block$$FormatBlockCommand(options) {
- options = options || {};
- options.action = 'formatBlock';
- $$text$format$$default.call(this, options);
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$format$block$$FormatBlockCommand, $$text$format$$default);
-
- $$format$block$$FormatBlockCommand.prototype.exec = function() {
- var tag = this.tag;
- // Brackets neccessary for certain browsers
- var value = '<' + tag + '>';
- var blockElement = $$$utils$selection$utils$$getSelectionBlockElement();
- // Allow block commands to be toggled back to a text block
- if(tag === blockElement.tagName.toLowerCase()) {
- value = node_modules$content$kit$compiler$src$types$type$$default.PARAGRAPH.tag;
- } else {
- // Flattens the selection before applying the block format.
- // Otherwise, undesirable nested blocks can occur.
- // TODO: would love to be able to remove this
- var flatNode = document.createTextNode(blockElement.textContent);
- blockElement.parentNode.insertBefore(flatNode, blockElement);
- blockElement.parentNode.removeChild(blockElement);
- $$$utils$selection$utils$$selectNode(flatNode);
- }
-
- $$format$block$$FormatBlockCommand._super.prototype.exec.call(this, value);
- };
-
- var $$format$block$$default = $$format$block$$FormatBlockCommand;
-
- function $$$commands$quote$$QuoteCommand() {
- $$format$block$$default.call(this, {
- name: 'quote',
- tag: node_modules$content$kit$compiler$src$types$type$$default.QUOTE.tag,
- button: ''
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$quote$$QuoteCommand, $$format$block$$default);
-
- var $$$commands$quote$$default = $$$commands$quote$$QuoteCommand;
-
- function $$$commands$heading$$HeadingCommand() {
- $$format$block$$default.call(this, {
- name: 'heading',
- tag: node_modules$content$kit$compiler$src$types$type$$default.HEADING.tag,
- button: '1'
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$heading$$HeadingCommand, $$format$block$$default);
-
- var $$$commands$heading$$default = $$$commands$heading$$HeadingCommand;
-
- function $$$commands$subheading$$SubheadingCommand() {
- $$format$block$$default.call(this, {
- name: 'subheading',
- tag: node_modules$content$kit$compiler$src$types$type$$default.SUBHEADING.tag,
- button: '2'
- });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$subheading$$SubheadingCommand, $$format$block$$default);
-
- var $$$commands$subheading$$default = $$$commands$subheading$$SubheadingCommand;
-
- function $$list$$ListCommand(options) {
- $$text$format$$default.call(this, options);
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$list$$ListCommand, $$text$format$$default);
-
- $$list$$ListCommand.prototype.exec = function() {
- $$list$$ListCommand._super.prototype.exec.call(this);
-
- // After creation, lists need to be unwrapped
- // TODO: eventually can remove this when direct model manipulation is ready
- var listElement = $$$utils$selection$utils$$getSelectionBlockElement();
- var wrapperNode = listElement.parentNode;
- if (wrapperNode.firstChild === listElement) {
- var editorNode = wrapperNode.parentNode;
- editorNode.insertBefore(listElement, wrapperNode);
- editorNode.removeChild(wrapperNode);
- $$$utils$selection$utils$$selectNode(listElement);
- }
- };
-
- $$list$$ListCommand.prototype.checkAutoFormat = function(node) {
- // Creates unordered lists when node starts with '- '
- // or ordered list if node starts with '1. '
- var regex = this.autoFormatRegex, text;
- if (node && regex) {
- text = node.textContent;
- if (node_modules$content$kit$compiler$src$types$type$$default.LIST_ITEM.tag !== $$$utils$selection$utils$$getSelectionTagName() && regex.test(text)) {
- this.exec();
- window.getSelection().anchorNode.textContent = text.replace(regex, '');
- return true;
+ } else if (nodeType === TEXT_NODE) {
+ text = node.nodeValue;
+ if (trim(text)) {
+ block = getLastBlockOrCreate(blocks);
+ block.value += text;
}
}
- return false;
- };
-
- var $$list$$default = $$list$$ListCommand;
-
- function $$$commands$unordered$list$$UnorderedListCommand() {
- $$list$$default.call(this, {
- name: 'list',
- tag: node_modules$content$kit$compiler$src$types$type$$default.LIST.tag,
- action: 'insertUnorderedList'
- });
}
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$unordered$list$$UnorderedListCommand, $$list$$default);
- $$$commands$unordered$list$$UnorderedListCommand.prototype.autoFormatRegex = /^[-*]\s/;
-
- var $$$commands$unordered$list$$default = $$$commands$unordered$list$$UnorderedListCommand;
-
- function $$$commands$ordered$list$$OrderedListCommand() {
- $$list$$default.call(this, {
- name: 'ordered list',
- tag: node_modules$content$kit$compiler$src$types$type$$default.ORDERED_LIST.tag,
- action: 'insertOrderedList'
+ return blocks;
+ };
+
+ /**
+ * @method parseMarkupForElement
+ * @param node element node to parse
+ * @return {Array} parsed markups
+ */
+ HTMLParser.prototype.parseMarkupForElement = function(node, startOffset) {
+ var index = 0;
+ var markups = [];
+ var currentNode, nodeType, markup;
+
+ startOffset = startOffset || 0;
+ node = node.cloneNode(true);
+ markup = this.serializeMarkupNode(node, startOffset);
+ if (markup) { markups.push(markup); }
+
+ while (node.hasChildNodes()) {
+ currentNode = node.firstChild;
+ nodeType = currentNode.nodeType;
+
+ if (nodeType === ELEMENT_NODE) {
+ markup = this.serializeMarkupNode(currentNode, startOffset);
+ if (markup) { markups.push(markup); }
+ unwrapNode(currentNode);
+ } else if (nodeType === TEXT_NODE) {
+ var text = sanitizeWhitespace(currentNode.nodeValue);
+ if (index === 0) { text = trimLeft(text); }
+ if (text) { startOffset += text.length; }
+ }
+
+ currentNode.parentNode.removeChild(currentNode);
+ index++;
+ }
+
+ return markups;
+ };
+
+ /**
+ * @method serializeBlockNode
+ * @param node element node to parse
+ * @return {BlockModel} parsed block model
+ * Serializes a single block type node into a model
+ */
+ HTMLParser.prototype.serializeBlockNode = function(node) {
+ var type = this.blockTypes.findByNode(node);
+ if (type) {
+ return new models_block({
+ type : type.id,
+ type_name : this.includeTypeNames && type.name,
+ value : trim(textOfNode(node)),
+ attributes : attributesForNode(node, this.attributeBlacklist),
+ markup : this.parseMarkupForElement(node)
});
}
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$ordered$list$$OrderedListCommand, $$list$$default);
-
- $$$commands$ordered$list$$OrderedListCommand.prototype.autoFormatRegex = /^1\.\s/;
-
- var $$$commands$ordered$list$$default = $$$commands$ordered$list$$OrderedListCommand;
-
- var $$$views$message$$defaultClassNames = ['ck-message'];
-
- function $$$views$message$$Message(options) {
- options = options || {};
- options.classNames = $$$views$message$$defaultClassNames;
- $$view$$default.call(this, options);
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$views$message$$Message, $$view$$default);
-
- function $$$views$message$$show(view, message) {
- view.element.innerHTML = message;
- $$$views$message$$Message._super.prototype.show.call(view);
- setTimeout(function() {
- view.hide();
- }, 3200);
- }
-
- $$$views$message$$Message.prototype.showInfo = function(message) {
- this.setClasses($$$views$message$$defaultClassNames);
- $$$views$message$$show(this, message);
- };
-
- $$$views$message$$Message.prototype.showError = function(message) {
- this.addClass('ck-message-error');
- $$$views$message$$show(this, message);
- };
-
- var $$$views$message$$default = $$$views$message$$Message;
- function $$$utils$http$utils$$createXHR(options) {
- var xhr = new XMLHttpRequest();
- xhr.open(options.method, options.url);
- xhr.onload = function () {
- var response = xhr.responseText;
- if (xhr.status === 200) {
- return options.success.call(this, response);
- }
- options.error.call(this, response);
- };
- xhr.onerror = function (error) {
- options.error.call(this, error);
- };
- return xhr;
- }
-
- function $$$utils$http$utils$$xhrGet(options) {
- options.method = 'GET';
- var xhr = $$$utils$http$utils$$createXHR(options);
- try {
- xhr.send();
- } catch(error) {}
- }
-
- function $$$utils$http$utils$$xhrPost(options) {
- options.method = 'POST';
- var xhr = $$$utils$http$utils$$createXHR(options);
- var formData = new FormData();
- formData.append('file', options.data);
- try {
- xhr.send(formData);
- } catch(error) {}
- }
-
- function $$$utils$http$utils$$responseJSON(jsonString) {
- if (!jsonString) { return null; }
- try {
- return JSON.parse(jsonString);
- } catch(e) {
- return jsonString;
+ };
+
+ /**
+ * @method serializeMarkupNode
+ * @param node element node to parse
+ * @param startIndex
+ * @return {MarkupModel} markup model
+ * Serializes markup of a single html element node (no child elements)
+ */
+ HTMLParser.prototype.serializeMarkupNode = function(node, startIndex) {
+ var type = this.markupTypes.findByNode(node);
+ var 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 models_markup({
+ type : type.id,
+ type_name : this.includeTypeNames && type.name,
+ start : startIndex,
+ end : endIndex,
+ attributes : attributesForNode(node, this.attributeBlacklist)
+ });
}
}
+ };
+
+ var html_parser = HTMLParser;
+
+ function createOpeningTag(tagName, attributes, selfClosing) {
+ var tag = '<' + tagName;
+ for (var attr in attributes) {
+ if (attributes.hasOwnProperty(attr)) {
+ 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 = [];
+ var 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;
- // --------------------------------------------
-
- function $$$utils$http$utils$$FileUploader(options) {
- options = options || {};
- var url = options.url;
- var maxFileSize = options.maxFileSize;
- if (url) {
- this.url = url;
+ 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;
+ };
+
+ var html_element_renderer = HTMLElementRenderer;
+
+ /**
+ * @class HTMLEmbedRenderer
+ * @constructor
+ */
+ function HTMLEmbedRenderer() {}
+
+ /**
+ * @method render
+ * @param model a block model
+ * @return String html
+ */
+ HTMLEmbedRenderer.prototype.render = function(model) {
+ var attrs = model.attributes;
+ return attrs && attrs.html || '';
+ };
+
+ var html_embed_renderer = HTMLEmbedRenderer;
+
+ function HTMLRenderer(options) {
+ var defaults = {
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ typeRenderers : {}
+ };
+ mergeWithOptions(this, defaults, options);
+ }
+
+ /**
+ * @method rendererFor
+ * @param block
+ * @returns renderer
+ * Returns an instance of a renderer for supplied block model
+ */
+ HTMLRenderer.prototype.rendererFor = function(block) {
+ var type = this.blockTypes.findById(block.type);
+ if (type === types_type.EMBED) {
+ return new html_embed_renderer();
+ }
+ return new html_element_renderer({ 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, blockHtml;
+
+ for (i = 0; i < len; i++) {
+ blockHtml = this.renderBlock(model[i]);
+ if (blockHtml) {
+ html += blockHtml;
+ }
+ }
+
+ return html;
+ };
+
+ /**
+ * @method renderBlock
+ * @param block
+ * @return String html
+ */
+ HTMLRenderer.prototype.renderBlock = function(block) {
+ var renderer = this.rendererFor(block);
+ var renderHook = this.typeRenderers[block.type];
+ return renderHook ? renderHook.call(renderer, block) : renderer.render(block);
+ };
+
+ var html_renderer = HTMLRenderer;
+
+ function Compiler(options) {
+ var parser = new html_parser();
+ var renderer = new html_renderer();
+ var defaults = {
+ parser : parser,
+ renderer : renderer,
+ blockTypes : DefaultBlockTypeSet,
+ markupTypes : DefaultMarkupTypeSet,
+ includeTypeNames : false // Outputs `type_name:'HEADING'` etc. when parsing. Good for debugging.
+ };
+ mergeWithOptions(this, defaults, options);
+
+ // Reference the compiler settings
+ this.parser.blockTypes = this.renderer.blockTypes = this.blockTypes;
+ this.parser.markupTypes = this.renderer.markupTypes = this.markupTypes;
+ this.parser.includeTypeNames = this.includeTypeNames;
+ }
+
+ /**
+ * @method parse
+ * @param input
+ * @return Array
+ */
+ Compiler.prototype.parse = function(input) {
+ return this.parser.parse(input);
+ };
+
+ /**
+ * @method render
+ * @param model
+ * @return String
+ */
+ Compiler.prototype.render = function(model) {
+ return this.renderer.render(model);
+ };
+
+ /**
+ * @method rerender
+ * @param input
+ * @return String
+ */
+ Compiler.prototype.rerender = function(input) {
+ return this.render(this.parse(input));
+ };
+
+ /**
+ * @method reparse
+ * @param model
+ * @return String
+ */
+ Compiler.prototype.reparse = function(model) {
+ return this.parse(this.render(model));
+ };
+
+ /**
+ * @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);
+ };
+
+ var compiler = Compiler;
+
+ var RegExVideoId = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;
+
+ function getVideoIdFromUrl(url) {
+ var match = url && url.match(RegExVideoId);
+ if (match && match[1].length === 11){
+ return match[1];
+ }
+ return null;
+ }
+
+ function YouTubeRenderer() {}
+ YouTubeRenderer.prototype.render = function(model) {
+ var videoId = getVideoIdFromUrl(model.attributes.url);
+ var embedUrl = 'http://www.youtube.com/embed/' + videoId + '?controls=2&showinfo=0&color=white&theme=light';
+ return '';
+ };
+
+ var youtube = YouTubeRenderer;
+
+ function TwitterRenderer() {}
+ TwitterRenderer.prototype.render = function(model) {
+ return '';
+ };
+
+ var twitter = TwitterRenderer;
+
+ function InstagramRenderer() {}
+ InstagramRenderer.prototype.render = function(model) {
+ return '';
+ };
+
+ var instagram = InstagramRenderer;
+
+ function LinkImageRenderer() {}
+ LinkImageRenderer.prototype.render = function(model) {
+ return '';
+ };
+
+ var link_image_renderer = LinkImageRenderer;
+
+ var embedRenderers = {
+ YOUTUBE : new youtube(),
+ TWITTER : new twitter(),
+ INSTAGRAM : new instagram(),
+ LINK_IMAGE : new link_image_renderer()
+ };
+
+ function embedRenderer(model) {
+ var embedAttrs = model.attributes;
+ var embedType = embedAttrs.embed_type;
+ var isVideo = embedType === 'video';
+ var providerName = embedAttrs.provider_name;
+ var customRendererId = providerName && providerName.toUpperCase();
+ var customRenderer = embedRenderers[customRendererId];
+ if (!customRenderer && embedType === 'link' && embedAttrs.thumbnail) {
+ customRenderer = embedRenderers.LINK_IMAGE;
+ }
+ var renderer = customRenderer ? customRenderer : this;
+
+ return '' +
+ '
' +
+ '
';
+ }
+
+ function imageRenderer(model) {
+ return '' +
+ '' +
+ '
';
+ }
+
+ var typeRenderers = {};
+ typeRenderers[types_type.EMBED.id] = embedRenderer;
+ typeRenderers[types_type.IMAGE.id] = imageRenderer;
+
+ /**
+ * @class EditorHTMLRenderer
+ * @constructor
+ * Subclass of HTMLRenderer specifically for the Editor
+ * Wraps interactive elements to add functionality
+ */
+ function EditorHTMLRenderer() {
+ html_renderer.call(this, {
+ typeRenderers: typeRenderers
+ });
+ }
+ inherit(EditorHTMLRenderer, html_renderer);
+
+ var editor_html_renderer = EditorHTMLRenderer;
+
+ function renderClasses(view) {
+ var classNames = view.classNames;
+ if (classNames && classNames.length) {
+ view.element.className = classNames.join(' ');
+ } else if(view.element.className) {
+ view.element.removeAttribute('className');
+ }
+ }
+
+ function View(options) {
+ options = options || {};
+ this.tagName = options.tagName || 'div';
+ this.classNames = options.classNames || [];
+ this.element = document.createElement(this.tagName);
+ this.container = options.container || document.body;
+ this.isShowing = false;
+ renderClasses(this);
+ }
+
+ View.prototype = {
+ show: function() {
+ var view = this;
+ if(!view.isShowing) {
+ view.container.appendChild(view.element);
+ view.isShowing = true;
+ return true;
+ }
+ },
+ hide: function() {
+ var view = this;
+ if(view.isShowing) {
+ view.container.removeChild(view.element);
+ view.isShowing = false;
+ return true;
+ }
+ },
+ addClass: function(className) {
+ var index = this.classNames && this.classNames.indexOf(className);
+ if (index === -1) {
+ this.classNames.push(className);
+ renderClasses(this);
+ }
+ },
+ removeClass: function(className) {
+ var index = this.classNames && this.classNames.indexOf(className);
+ if (index > -1) {
+ this.classNames.splice(index, 1);
+ renderClasses(this);
+ }
+ },
+ setClasses: function(classNameArr) {
+ this.classNames = classNameArr;
+ renderClasses(this);
+ }
+ };
+
+ var views_view = View;
+
+ 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');
+
+ button.element = element;
+ button.command = command;
+ button.isActive = false;
+
+ element.title = command.name;
+ element.className = buttonClassName;
+ element.innerHTML = command.button;
+ element.addEventListener('mouseup', function(e) {
+ if (!button.isActive && prompt) {
+ toolbar.displayPrompt(prompt);
} else {
- throw new Error('FileUploader: setting the `url` to an upload service is required');
- }
- if (maxFileSize) {
- this.maxFileSize = maxFileSize;
+ command.exec();
+ toolbar.updateForSelection();
}
- }
-
- $$$utils$http$utils$$FileUploader.prototype.upload = function(options) {
- if (!options) { return; }
+ e.stopPropagation();
+ });
+ }
- var fileInput = options.fileInput;
- var file = options.file || (fileInput && fileInput.files && fileInput.files[0]);
- var callback = options.complete;
- var maxFileSize = this.maxFileSize;
- if (!file || !(file instanceof window.File)) { return; }
-
- if (maxFileSize && file.size > maxFileSize) {
- if (callback) { callback.call(this, null, { message: 'max file size is ' + maxFileSize + ' bytes' }); }
- return;
+ 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;
+ }
+ }
+ };
- $$$utils$http$utils$$xhrPost({
- url: this.url,
- data: file,
- success: function(response) {
- if (callback) { callback.call(this, $$$utils$http$utils$$responseJSON(response)); }
- },
- error: function(error) {
- if (callback) { callback.call(this, null, $$$utils$http$utils$$responseJSON(error)); }
- }
- });
- };
-
- function $$$utils$http$utils$$OEmbedder(options) {
- options = options || {};
- var url = options.url;
- if (url) {
- this.url = url;
+ var toolbar_button = ToolbarButton;
+
+ 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.toLowerCase() === 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 elementContentIsEmpty(element) {
+ var content = element && element.innerHTML;
+ if (content) {
+ return content === '' || content === '
';
+ }
+ 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;
+ var left, top;
+
+ topOffset = topOffset || 0;
+ leftOffset = leftOffset || 0;
+ left = round(rect.left - relativeOffset.left - leftOffset);
+ top = round(rect.top - relativeOffset.top - topOffset);
+ style.left = left + 'px';
+ style.top = top + 'px';
+ return { left: left, top: top };
+ }
+
+ function positionElementHorizontallyCenteredToRect(element, rect, topOffset) {
+ var horizontalCenter = (element.offsetWidth / 2) - (rect.width / 2);
+ return positionElementToRect(element, rect, topOffset, horizontalCenter);
+ }
+
+ function positionElementCenteredAbove(element, aboveElement) {
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginBottom');
+ return positionElementHorizontallyCenteredToRect(element, aboveElement.getBoundingClientRect(), element.offsetHeight + elementMargin);
+ }
+
+ function positionElementCenteredBelow(element, belowElement) {
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop');
+ return positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin);
+ }
+
+ function positionElementCenteredIn(element, inElement) {
+ var verticalCenter = (inElement.offsetHeight / 2) - (element.offsetHeight / 2);
+ return positionElementHorizontallyCenteredToRect(element, inElement.getBoundingClientRect(), -verticalCenter);
+ }
+
+ function positionElementToLeftOf(element, leftOfElement) {
+ var verticalCenter = (leftOfElement.offsetHeight / 2) - (element.offsetHeight / 2);
+ var elementMargin = getElementComputedStyleNumericProp(element, 'marginRight');
+ return 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();
+ return positionElementToRect(element, rightOfElementRect, -verticalCenter, -rightOfElement.offsetWidth - elementMargin);
+ }
+
+ var RootTags = [
+ types_type.PARAGRAPH.tag,
+ types_type.HEADING.tag,
+ types_type.SUBHEADING.tag,
+ types_type.QUOTE.tag,
+ types_type.LIST.tag,
+ types_type.ORDERED_LIST.tag
+ ];
+
+ var SelectionDirection = {
+ LEFT_TO_RIGHT : 1,
+ RIGHT_TO_LEFT : 2,
+ SAME_NODE : 3
+ };
+
+ 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);
+ }
+
+ function getSelectionBlockElement(selection) {
+ selection = selection || window.getSelection();
+ var element = getSelectionElement();
+ var tag = element && element.tagName.toLowerCase();
+ while (tag && RootTags.indexOf(tag) === -1) {
+ if (element.contentEditable === 'true') {
+ return null; // Stop traversing up dom when hitting an editor element
+ }
+ element = element.parentNode;
+ tag = element.tagName && element.tagName.toLowerCase();
+ }
+ return element;
+ }
+
+ function getSelectionTagName() {
+ var element = getSelectionElement();
+ return element ? element.tagName.toLowerCase() : null;
+ }
+
+ function getSelectionBlockTagName() {
+ var element = getSelectionBlockElement();
+ return element ? element.tagName && element.tagName.toLowerCase() : null;
+ }
+
+ function tagsInSelection(selection) {
+ var element = getSelectionElement(selection);
+ var tags = [];
+ while(element) {
+ if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
+ if (element.tagName) {
+ tags.push(element.tagName.toLowerCase());
+ }
+ element = element.parentNode;
+ }
+ return tags;
+ }
+
+ function selectionIsInElement(selection, element) {
+ var node = selection.anchorNode;
+ return node && nodeIsDescendantOfElement(node, element);
+ }
+
+ function selectionIsEditable(selection) {
+ var el = getSelectionBlockElement(selection);
+ return el && el.isContentEditable;
+ }
+
+ 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);
+ }
+
+ function setCursorIndexInElement(element, index) {
+ var range = document.createRange();
+ var selection = window.getSelection();
+ range.setStart(element, index);
+ range.collapse(true);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+
+ function setCursorToStartOfElement(element) {
+ setCursorIndexInElement(element, 0);
+ }
+
+ function getCursorOffsetInElement(element) {
+ // http://stackoverflow.com/questions/4811822/get-a-ranges-start-and-end-offsets-relative-to-its-parent-container/4812022#4812022
+ var caretOffset = 0;
+ var selection = window.getSelection();
+ if (selection.rangeCount > 0) {
+ var range = selection.getRangeAt(0);
+ var preCaretRange = range.cloneRange();
+ preCaretRange.selectNodeContents(element);
+ preCaretRange.setEnd(range.endContainer, range.endOffset);
+ caretOffset = preCaretRange.toString().length;
+ }
+ return caretOffset;
+ }
+
+ var ToolbarDirection = {
+ TOP : 1,
+ RIGHT : 2
+ };
+
+ function selectionContainsButtonsTag(selectedTags, buttonsTags) {
+ return selectedTags.filter(function(tag) {
+ return buttonsTags.indexOf(tag) > -1;
+ }).length;
+ }
+
+ function updateButtonsForSelection(buttons, selection) {
+ var selectedTags = tagsInSelection(selection);
+ var len = buttons.length;
+ var i, button;
+
+ for (i = 0; i < len; i++) {
+ button = buttons[i];
+ if (selectionContainsButtonsTag(selectedTags, button.command.mappedTags)) {
+ button.setActive();
+ } else {
+ button.setInactive();
+ }
+ }
+ }
+
+ function Toolbar(options) {
+ options = options || {};
+ var toolbar = this;
+ var commands = options.commands;
+ var commandCount = commands && commands.length, i;
+ options.classNames = ['ck-toolbar'];
+ views_view.call(toolbar, options);
+
+ toolbar.setSticky(options.sticky || false);
+ toolbar.setDirection(options.direction || ToolbarDirection.TOP);
+ toolbar.editor = options.editor || null;
+ toolbar.embedIntent = options.embedIntent || null;
+ toolbar.activePrompt = null;
+ toolbar.buttons = [];
+
+ toolbar.contentElement = createDiv('ck-toolbar-content');
+ toolbar.promptContainerElement = createDiv('ck-toolbar-prompt');
+ toolbar.buttonContainerElement = createDiv('ck-toolbar-buttons');
+ toolbar.contentElement.appendChild(toolbar.promptContainerElement);
+ toolbar.contentElement.appendChild(toolbar.buttonContainerElement);
+ toolbar.element.appendChild(toolbar.contentElement);
+
+ for(i = 0; i < commandCount; i++) {
+ this.addCommand(commands[i]);
+ }
+
+ // Closes prompt if displayed when changing selection
+ document.addEventListener('mouseup', function() {
+ toolbar.dismissPrompt();
+ });
+ }
+ inherit(Toolbar, views_view);
+
+ Toolbar.prototype.hide = function() {
+ if (Toolbar._super.prototype.hide.call(this)) {
+ var style = this.element.style;
+ style.left = '';
+ style.top = '';
+ this.dismissPrompt();
+ }
+ };
+
+ Toolbar.prototype.addCommand = function(command) {
+ command.editorContext = this.editor;
+ command.embedIntent = this.embedIntent;
+ var button = new toolbar_button({ command: command, toolbar: this });
+ this.buttons.push(button);
+ this.buttonContainerElement.appendChild(button.element);
+ };
+
+ 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();
+ });
+ toolbar.activePrompt = prompt;
+ };
+
+ Toolbar.prototype.dismissPrompt = function() {
+ var toolbar = this;
+ var activePrompt = toolbar.activePrompt;
+ if (activePrompt) {
+ activePrompt.hide();
+ swapElements(toolbar.buttonContainerElement, toolbar.promptContainerElement);
+ toolbar.activePrompt = null;
+ }
+ };
+
+ Toolbar.prototype.updateForSelection = function(selection) {
+ var toolbar = this;
+ selection = selection || window.getSelection();
+ if (toolbar.sticky) {
+ updateButtonsForSelection(toolbar.buttons, selection);
+ } else if (!selection.isCollapsed) {
+ toolbar.positionToContent(selection.getRangeAt(0));
+ updateButtonsForSelection(toolbar.buttons, selection);
+ }
+ };
+
+ Toolbar.prototype.positionToContent = function(content) {
+ var directions = ToolbarDirection;
+ var positioningMethod, position, sideEdgeOffset;
+ switch(this.direction) {
+ case directions.RIGHT:
+ positioningMethod = positionElementToRightOf;
+ break;
+ default:
+ positioningMethod = positionElementCenteredAbove;
+ }
+ position = positioningMethod(this.element, content);
+ sideEdgeOffset = Math.min(Math.max(10, position.left), document.body.clientWidth - this.element.offsetWidth - 10);
+ this.contentElement.style.transform = 'translateX(' + (sideEdgeOffset - position.left) + 'px)';
+ };
+
+ Toolbar.prototype.setDirection = function(direction) {
+ this.direction = direction;
+ if (direction === ToolbarDirection.RIGHT) {
+ this.addClass('right');
+ } else {
+ this.removeClass('right');
+ }
+ };
+
+ Toolbar.prototype.setSticky = function(sticky) {
+ this.sticky = sticky;
+ if (sticky) {
+ this.addClass('sticky');
+ this.element.removeAttribute('style'); // clears any prior positioning
+ this.show();
+ } else {
+ this.removeClass('sticky');
+ this.hide();
+ }
+ };
+
+ Toolbar.prototype.toggleSticky = function() {
+ this.setSticky(!this.sticky);
+ };
+
+ Toolbar.Direction = ToolbarDirection;
+
+ var views_toolbar = Toolbar;
+
+ var Keycodes = {
+ BKSP : 8,
+ ENTER : 13,
+ ESC : 27,
+ DEL : 46,
+ M : 77
+ };
+
+ function selectionIsEditableByToolbar(selection, toolbar) {
+ return selectionIsEditable(selection) && selectionIsInElement(selection, toolbar.rootElement);
+ }
+
+ function handleTextSelection(toolbar) {
+ var selection = window.getSelection();
+ if (toolbar.sticky) {
+ toolbar.updateForSelection(selectionIsEditableByToolbar(selection, toolbar) ? selection : null);
+ } else {
+ if (selection.isCollapsed || selection.toString().trim() === '' || !selectionIsEditableByToolbar(selection, toolbar)) {
+ toolbar.hide();
} else {
- throw new Error('OEmbedder: setting the `url` to an embed service is required');
+ toolbar.show();
+ toolbar.updateForSelection(selection);
}
}
+ }
- $$$utils$http$utils$$OEmbedder.prototype.fetch = function(options) {
- var callback = options.complete;
- $$$utils$http$utils$$xhrGet({
- url: this.url + "?url=" + encodeURI(options.url),
- success: function(response) {
- if (callback) { callback.call(this, $$$utils$http$utils$$responseJSON(response)); }
- },
- error: function(error) {
- if (callback) { callback.call(this, null, $$$utils$http$utils$$responseJSON(error)); }
- }
- });
- };
+ function TextFormatToolbar(options) {
+ var toolbar = this;
+ views_toolbar.call(this, options);
+ toolbar.rootElement = options.rootElement;
+ toolbar.rootElement.addEventListener('keyup', function() { handleTextSelection(toolbar); });
- function $$$commands$image$$createFileInput(command) {
- var fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.accept = 'image/*';
- fileInput.className = 'ck-file-input';
- fileInput.addEventListener('change', function(e) {
- command.handleFile(e);
+ document.addEventListener('mouseup', function() {
+ setTimeout(function() {
+ handleTextSelection(toolbar);
});
- return fileInput;
- }
-
- function $$$commands$image$$injectImageBlock(src, editor, index) {
- var imageModel = node_modules$content$kit$compiler$src$models$block$$default.createWithType(node_modules$content$kit$compiler$src$types$type$$default.IMAGE, { attributes: { src: src } });
- editor.replaceBlock(imageModel, index);
- }
-
- function $$$commands$image$$renderFromFile(file, editor, index) {
- if (file && window.FileReader) {
- var reader = new FileReader();
- reader.onload = function(e) {
- var base64Src = e.target.result;
- $$$commands$image$$injectImageBlock(base64Src, editor, index);
- editor.renderBlockAt(index, true);
- };
- reader.readAsDataURL(file);
+ });
+
+ document.addEventListener('keyup', function(e) {
+ var key = e.keyCode;
+ if (key === 116) { //F5
+ toolbar.toggleSticky();
+ handleTextSelection(toolbar);
+ } else if (!toolbar.sticky && key === Keycodes.ESC) {
+ toolbar.hide();
+ }
+ });
+
+ window.addEventListener('resize', function() {
+ if(!toolbar.sticky && toolbar.isShowing) {
+ var activePromptRange = toolbar.activePrompt && toolbar.activePrompt.range;
+ toolbar.positionToContent(activePromptRange ? activePromptRange : window.getSelection().getRangeAt(0));
+ }
+ });
+ }
+ inherit(TextFormatToolbar, views_toolbar);
+
+ var text_format_toolbar = TextFormatToolbar;
+
+ function Tooltip(options) {
+ var tooltip = this;
+ var rootElement = options.rootElement;
+ var delay = options.delay || 200;
+ var timeout;
+ options.classNames = ['ck-tooltip'];
+ views_view.call(tooltip, options);
+
+ 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, views_view);
+
+ Tooltip.prototype.showMessage = function(message, element) {
+ var tooltip = this;
+ var tooltipElement = tooltip.element;
+ tooltipElement.innerHTML = message;
+ tooltip.show();
+ positionElementCenteredBelow(tooltipElement, element);
+ };
+
+ Tooltip.prototype.showLink = function(link, element) {
+ var message = '' + link + '';
+ this.showMessage(message, element);
+ };
+
+ var views_tooltip = Tooltip;
+
+ var LayoutStyle = {
+ GUTTER : 1,
+ CENTERED : 2
+ };
+
+ function computeLayoutStyle(rootElement) {
+ if (rootElement.getBoundingClientRect().left > 100) {
+ return LayoutStyle.GUTTER;
+ }
+ return LayoutStyle.CENTERED;
+ }
+
+ function EmbedIntent(options) {
+ var embedIntent = this;
+ var rootElement = embedIntent.rootElement = options.rootElement;
+ options.classNames = ['ck-embed-intent'];
+ views_view.call(embedIntent, options);
+
+ embedIntent.isActive = false;
+ embedIntent.editorContext = options.editorContext;
+ embedIntent.loadingIndicator = createDiv('ck-embed-loading');
+ embedIntent.button = document.createElement('button');
+ embedIntent.button.className = 'ck-embed-intent-btn';
+ embedIntent.button.title = 'Insert image or embed...';
+ embedIntent.element.appendChild(embedIntent.button);
+ embedIntent.button.addEventListener('mouseup', function(e) {
+ if (embedIntent.isActive) {
+ embedIntent.deactivate();
+ } else {
+ embedIntent.activate();
+ }
+ e.stopPropagation();
+ });
+
+ embedIntent.toolbar = new views_toolbar({
+ container: embedIntent.element,
+ embedIntent: embedIntent,
+ editor: embedIntent.editorContext,
+ commands: options.commands,
+ direction: views_toolbar.Direction.RIGHT
+ });
+
+ function embedIntentHandler() {
+ var blockElement = getSelectionBlockElement();
+ if (blockElement && elementContentIsEmpty(blockElement)) {
+ embedIntent.showAt(blockElement);
+ } else {
+ embedIntent.hide();
}
}
- function $$$commands$image$$ImageCommand(options) {
- $$base$$default.call(this, {
- name: 'image',
- button: ''
- });
- this.uploader = new $$$utils$http$utils$$FileUploader({ url: options.serviceUrl, maxFileSize: 5000000 });
- }
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$image$$ImageCommand, $$base$$default);
+ rootElement.addEventListener('keyup', embedIntentHandler);
+ document.addEventListener('mouseup', function() {
+ setTimeout(function() { embedIntentHandler(); });
+ });
- $$$commands$image$$ImageCommand.prototype = {
- exec: function() {
- $$$commands$image$$ImageCommand._super.prototype.exec.call(this);
- var fileInput = this.fileInput;
- if (!fileInput) {
- fileInput = this.fileInput = $$$commands$image$$createFileInput(this);
- document.body.appendChild(fileInput);
- }
- fileInput.dispatchEvent(new MouseEvent('click', { bubbles: false }));
- },
- handleFile: function(e) {
- var fileInput = e.target;
- var file = fileInput.files && fileInput.files[0];
- var editor = this.editorContext;
- var embedIntent = this.embedIntent;
- var currentEditingIndex = editor.getCurrentBlockIndex();
-
- embedIntent.showLoading();
- $$$commands$image$$renderFromFile(file, editor, currentEditingIndex); // render image immediately client-side
- this.uploader.upload({
- fileInput: fileInput,
- complete: function(response, error) {
- embedIntent.hideLoading();
- if (error || !response || !response.url) {
- setTimeout(function() {
- editor.removeBlockAt(currentEditingIndex);
- editor.syncVisual();
- }, 1000);
- return new $$$views$message$$default().showError(error.message || 'Error uploading image');
- }
- $$$commands$image$$injectImageBlock(response.url, editor, currentEditingIndex);
- }
- });
- fileInput.value = null; // reset file input
+ document.addEventListener('keyup', function(e) {
+ if (e.keyCode === Keycodes.ESC) {
+ embedIntent.hide();
}
- };
-
- var $$$commands$image$$default = $$$commands$image$$ImageCommand;
+ });
- function $$$commands$oembed$$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);
+ window.addEventListener('resize', function() {
+ if(embedIntent.isShowing) {
+ embedIntent.reposition();
}
- }
-
- function $$$commands$oembed$$OEmbedCommand(options) {
- $$base$$default.call(this, {
- name: 'embed',
- button: '',
- prompt: new $$$views$prompt$$default({
- command: this,
- placeholder: 'Paste a YouTube or Twitter url...'
- })
- });
+ });
+ }
+ inherit(EmbedIntent, views_view);
- this.embedService = new $$$utils$http$utils$$OEmbedder({ url: options.serviceUrl });
+ EmbedIntent.prototype.hide = function() {
+ if (EmbedIntent._super.prototype.hide.call(this)) {
+ this.deactivate();
}
- node_modules$content$kit$utils$src$object$utils$$inherit($$$commands$oembed$$OEmbedCommand, $$base$$default);
+ };
+
+ EmbedIntent.prototype.showAt = function(node) {
+ this.atNode = node;
+ this.show();
+ this.deactivate();
+ this.reposition();
+ };
+
+ EmbedIntent.prototype.reposition = function() {
+ if (computeLayoutStyle(this.rootElement) === LayoutStyle.GUTTER) {
+ positionElementToLeftOf(this.element, this.atNode);
+ } else {
+ positionElementCenteredIn(this.element, this.atNode);
+ }
+ };
+
+ EmbedIntent.prototype.activate = function() {
+ if (!this.isActive) {
+ this.addClass('activated');
+ this.toolbar.show();
+ this.isActive = true;
+ }
+ };
+
+ EmbedIntent.prototype.deactivate = function() {
+ if (this.isActive) {
+ this.removeClass('activated');
+ this.toolbar.hide();
+ this.isActive = false;
+ }
+ };
+
+ EmbedIntent.prototype.showLoading = function() {
+ var embedIntent = this;
+ var loadingIndicator = embedIntent.loadingIndicator;
+ embedIntent.hide();
+ embedIntent.atNode.appendChild(loadingIndicator);
+ };
+
+ EmbedIntent.prototype.hideLoading = function() {
+ this.atNode.removeChild(this.loadingIndicator);
+ };
+
+ var embed_intent = EmbedIntent;
+
+ function Command(options) {
+ options = options || {};
+ var command = this;
+ var name = options.name;
+ var prompt = options.prompt;
+ command.name = name;
+ command.button = options.button || name;
+ if (prompt) { command.prompt = prompt; }
+ }
+
+ Command.prototype.exec = function() {};
+
+ var base = Command;
+
+ function TextFormatCommand(options) {
+ options = options || {};
+ base.call(this, options);
+ this.tag = options.tag;
+ this.mappedTags = options.mappedTags || [];
+ this.mappedTags.push(this.tag);
+ this.action = options.action || this.name;
+ this.removeAction = options.removeAction || this.action;
+ }
+ inherit(TextFormatCommand, base);
+
+ TextFormatCommand.prototype = {
+ exec: function(value) {
+ document.execCommand(this.action, false, value || null);
+ },
+ unexec: function(value) {
+ document.execCommand(this.removeAction, false, value || null);
+ }
+ };
+
+ var text_format = TextFormatCommand;
+
+ var RegExpHeadingTag = /^(h1|h2|h3|h4|h5|h6)$/i;
+
+ function BoldCommand() {
+ text_format.call(this, {
+ name: 'bold',
+ tag: types_type.BOLD.tag,
+ mappedTags: types_type.BOLD.mappedTags,
+ button: ''
+ });
+ }
+ inherit(BoldCommand, text_format);
+
+ BoldCommand.prototype.exec = function() {
+ // Don't allow executing bold command on heading tags
+ if (!RegExpHeadingTag.test(getSelectionBlockTagName())) {
+ BoldCommand._super.prototype.exec.call(this);
+ }
+ };
+
+ var bold = BoldCommand;
+
+ function ItalicCommand() {
+ text_format.call(this, {
+ name: 'italic',
+ tag: types_type.ITALIC.tag,
+ mappedTags: types_type.ITALIC.mappedTags,
+ button: ''
+ });
+ }
+ inherit(ItalicCommand, text_format);
+
+ var italic = ItalicCommand;
+
+ 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);
+ }
+
+ function Prompt(options) {
+ var prompt = this;
+ options.tagName = 'input';
+ views_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(); }
+ }
+ });
+
+ window.addEventListener('resize', function() {
+ var activeHilite = hiliter.parentNode;
+ var range = prompt.range;
+ if(activeHilite && range) {
+ positionHiliteRange(range);
+ }
+ });
+ }
+ inherit(Prompt, views_view);
+
+ 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; }
+ }
+ };
+
+ Prompt.prototype.hide = function() {
+ if (hiliter.parentNode) {
+ container.removeChild(hiliter);
+ }
+ };
+
+ var views_prompt = Prompt;
+
+ var RegExpHttp = /^https?:\/\//i;
+
+ function LinkCommand() {
+ text_format.call(this, {
+ name: 'link',
+ tag: types_type.LINK.tag,
+ action: 'createLink',
+ removeAction: 'unlink',
+ button: '',
+ prompt: new views_prompt({
+ command: this,
+ placeholder: 'Enter a url, press return...'
+ })
+ });
+ }
+ inherit(LinkCommand, text_format);
+
+ LinkCommand.prototype.exec = function(url) {
+ if (!url) {
+ return LinkCommand._super.prototype.unexec.call(this);
+ }
+
+ if(this.tag === getSelectionTagName()) {
+ this.unexec();
+ } else {
+ if (!RegExpHttp.test(url)) {
+ url = 'http://' + url;
+ }
+ LinkCommand._super.prototype.exec.call(this, url);
+ }
+ };
+
+ var commands_link = LinkCommand;
+
+ function FormatBlockCommand(options) {
+ options = options || {};
+ options.action = 'formatBlock';
+ text_format.call(this, options);
+ }
+ inherit(FormatBlockCommand, text_format);
+
+ 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 text block
+ if(tag === blockElement.tagName.toLowerCase()) {
+ value = types_type.PARAGRAPH.tag;
+ } else {
+ // Flattens the selection before applying the block format.
+ // Otherwise, undesirable nested blocks can occur.
+ // TODO: would love to be able to remove this
+ var flatNode = document.createTextNode(blockElement.textContent);
+ blockElement.parentNode.insertBefore(flatNode, blockElement);
+ blockElement.parentNode.removeChild(blockElement);
+ selectNode(flatNode);
+ }
+
+ FormatBlockCommand._super.prototype.exec.call(this, value);
+ };
+
+ var format_block = FormatBlockCommand;
+
+ function QuoteCommand() {
+ format_block.call(this, {
+ name: 'quote',
+ tag: types_type.QUOTE.tag,
+ button: ''
+ });
+ }
+ inherit(QuoteCommand, format_block);
+
+ var quote = QuoteCommand;
+
+ function HeadingCommand() {
+ format_block.call(this, {
+ name: 'heading',
+ tag: types_type.HEADING.tag,
+ button: '1'
+ });
+ }
+ inherit(HeadingCommand, format_block);
+
+ var heading = HeadingCommand;
+
+ function SubheadingCommand() {
+ format_block.call(this, {
+ name: 'subheading',
+ tag: types_type.SUBHEADING.tag,
+ button: '2'
+ });
+ }
+ inherit(SubheadingCommand, format_block);
+
+ var subheading = SubheadingCommand;
+
+ function ListCommand(options) {
+ text_format.call(this, options);
+ }
+ inherit(ListCommand, text_format);
+
+ ListCommand.prototype.exec = function() {
+ ListCommand._super.prototype.exec.call(this);
+
+ // After creation, lists need to be unwrapped
+ // TODO: eventually can remove this when direct model manipulation is ready
+ var listElement = getSelectionBlockElement();
+ var wrapperNode = listElement.parentNode;
+ if (wrapperNode.firstChild === listElement) {
+ var editorNode = wrapperNode.parentNode;
+ editorNode.insertBefore(listElement, wrapperNode);
+ editorNode.removeChild(wrapperNode);
+ selectNode(listElement);
+ }
+ };
+
+ ListCommand.prototype.checkAutoFormat = function(node) {
+ // Creates unordered lists when node starts with '- '
+ // or ordered list if node starts with '1. '
+ var regex = this.autoFormatRegex, text;
+ if (node && regex) {
+ text = node.textContent;
+ if (types_type.LIST_ITEM.tag !== getSelectionTagName() && regex.test(text)) {
+ this.exec();
+ window.getSelection().anchorNode.textContent = text.replace(regex, '');
+ return true;
+ }
+ }
+ return false;
+ };
+
+ var list = ListCommand;
+
+ function UnorderedListCommand() {
+ list.call(this, {
+ name: 'list',
+ tag: types_type.LIST.tag,
+ action: 'insertUnorderedList'
+ });
+ }
+ inherit(UnorderedListCommand, list);
+
+ UnorderedListCommand.prototype.autoFormatRegex = /^[-*]\s/;
+
+ var unordered_list = UnorderedListCommand;
+
+ function OrderedListCommand() {
+ list.call(this, {
+ name: 'ordered list',
+ tag: types_type.ORDERED_LIST.tag,
+ action: 'insertOrderedList'
+ });
+ }
+ inherit(OrderedListCommand, list);
+
+ OrderedListCommand.prototype.autoFormatRegex = /^1\.\s/;
+
+ var ordered_list = OrderedListCommand;
+
+ var defaultClassNames = ['ck-message'];
+
+ function Message(options) {
+ options = options || {};
+ options.classNames = defaultClassNames;
+ views_view.call(this, options);
+ }
+ inherit(Message, views_view);
+
+ function show(view, message) {
+ view.element.innerHTML = message;
+ Message._super.prototype.show.call(view);
+ setTimeout(function() {
+ view.hide();
+ }, 3200);
+ }
+
+ Message.prototype.showInfo = function(message) {
+ this.setClasses(defaultClassNames);
+ show(this, message);
+ };
+
+ Message.prototype.showError = function(message) {
+ this.addClass('ck-message-error');
+ show(this, message);
+ };
+
+ var views_message = Message;
+
+ function createXHR(options) {
+ var xhr = new XMLHttpRequest();
+ xhr.open(options.method, options.url);
+ xhr.onload = function () {
+ var response = xhr.responseText;
+ if (xhr.status === 200) {
+ return options.success.call(this, response);
+ }
+ options.error.call(this, response);
+ };
+ xhr.onerror = function (error) {
+ options.error.call(this, error);
+ };
+ return xhr;
+ }
+
+ function xhrGet(options) {
+ options.method = 'GET';
+ var xhr = createXHR(options);
+ try {
+ xhr.send();
+ } catch(error) {}
+ }
+
+ function xhrPost(options) {
+ options.method = 'POST';
+ var xhr = createXHR(options);
+ var formData = new FormData();
+ formData.append('file', options.data);
+ try {
+ xhr.send(formData);
+ } catch(error) {}
+ }
+
+ function responseJSON(jsonString) {
+ if (!jsonString) { return null; }
+ try {
+ return JSON.parse(jsonString);
+ } catch(e) {
+ return jsonString;
+ }
+ }
+
+ // --------------------------------------------
+
+ function FileUploader(options) {
+ options = options || {};
+ var url = options.url;
+ var maxFileSize = options.maxFileSize;
+ if (url) {
+ this.url = url;
+ } else {
+ throw new Error('FileUploader: setting the `url` to an upload service is required');
+ }
+ if (maxFileSize) {
+ this.maxFileSize = maxFileSize;
+ }
+ }
+
+ FileUploader.prototype.upload = function(options) {
+ if (!options) { return; }
+
+ var fileInput = options.fileInput;
+ var file = options.file || (fileInput && fileInput.files && fileInput.files[0]);
+ var callback = options.complete;
+ var maxFileSize = this.maxFileSize;
+ if (!file || !(file instanceof window.File)) { return; }
+
+ if (maxFileSize && file.size > maxFileSize) {
+ if (callback) { callback.call(this, null, { message: 'max file size is ' + maxFileSize + ' bytes' }); }
+ return;
+ }
+
+ xhrPost({
+ url: this.url,
+ data: file,
+ success: function(response) {
+ if (callback) { callback.call(this, responseJSON(response)); }
+ },
+ error: function(error) {
+ if (callback) { callback.call(this, null, responseJSON(error)); }
+ }
+ });
+ };
+
+ function OEmbedder(options) {
+ options = options || {};
+ var url = options.url;
+ if (url) {
+ this.url = url;
+ } else {
+ throw new Error('OEmbedder: setting the `url` to an embed service is required');
+ }
+ }
+
+ OEmbedder.prototype.fetch = function(options) {
+ var callback = options.complete;
+ xhrGet({
+ url: this.url + "?url=" + encodeURI(options.url),
+ success: function(response) {
+ if (callback) { callback.call(this, responseJSON(response)); }
+ },
+ error: function(error) {
+ if (callback) { callback.call(this, null, responseJSON(error)); }
+ }
+ });
+ };
+
+ function createFileInput(command) {
+ var fileInput = document.createElement('input');
+ fileInput.type = 'file';
+ fileInput.accept = 'image/*';
+ fileInput.className = 'ck-file-input';
+ fileInput.addEventListener('change', function(e) {
+ command.handleFile(e);
+ });
+ return fileInput;
+ }
+
+ function injectImageBlock(src, editor, index) {
+ var imageModel = models_block.createWithType(types_type.IMAGE, { attributes: { src: src } });
+ editor.replaceBlock(imageModel, index);
+ }
+
+ function renderFromFile(file, editor, index) {
+ if (file && window.FileReader) {
+ var reader = new FileReader();
+ reader.onload = function(e) {
+ var base64Src = e.target.result;
+ injectImageBlock(base64Src, editor, index);
+ editor.renderBlockAt(index, true);
+ };
+ reader.readAsDataURL(file);
+ }
+ }
+
+ function ImageCommand(options) {
+ base.call(this, {
+ name: 'image',
+ button: ''
+ });
+ this.uploader = new FileUploader({ url: options.serviceUrl, maxFileSize: 5000000 });
+ }
+ inherit(ImageCommand, base);
+
+ ImageCommand.prototype = {
+ exec: function() {
+ ImageCommand._super.prototype.exec.call(this);
+ var fileInput = this.fileInput;
+ if (!fileInput) {
+ fileInput = this.fileInput = createFileInput(this);
+ document.body.appendChild(fileInput);
+ }
+ fileInput.dispatchEvent(new MouseEvent('click', { bubbles: false }));
+ },
+ handleFile: function(e) {
+ var fileInput = e.target;
+ var file = fileInput.files && fileInput.files[0];
+ var editor = this.editorContext;
+ var embedIntent = this.embedIntent;
+ var currentEditingIndex = editor.getCurrentBlockIndex();
- $$$commands$oembed$$OEmbedCommand.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,
+ renderFromFile(file, editor, currentEditingIndex); // render image immediately client-side
+ this.uploader.upload({
+ fileInput: fileInput,
complete: function(response, error) {
embedIntent.hideLoading();
- if (error) {
- var errorMsg = error;
- if (error.target && error.target.status === 0) {
- errorMsg = 'Error: could not connect to embed service.';
- } else if (typeof error !== 'string') {
- errorMsg = 'Error: unexpected embed error.';
- }
- new $$$views$message$$default().showError(errorMsg);
- embedIntent.show();
- } else if (response.error_message) {
- new $$$views$message$$default().showError(response.error_message);
- embedIntent.show();
- } else {
- var embedModel = new node_modules$content$kit$compiler$src$models$embed$$default(response);
- editorContext.insertBlock(embedModel, index);
- editorContext.renderBlockAt(index);
- if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
- $$$commands$oembed$$loadTwitterWidgets(editorContext.element);
- }
+ if (error || !response || !response.url) {
+ setTimeout(function() {
+ editor.removeBlockAt(currentEditingIndex);
+ editor.syncVisual();
+ }, 1000);
+ return new views_message().showError(error.message || 'Error uploading image');
}
+ injectImageBlock(response.url, editor, currentEditingIndex);
}
});
- };
-
- var $$$commands$oembed$$default = $$$commands$oembed$$OEmbedCommand;
- // Based on https://github.com/jeromeetienne/microevent.js/blob/master/microevent.js
- // See also: https://github.com/allouis/minivents/blob/master/minivents.js
-
- var $$$utils$event$emitter$$EventEmitter = {
- on : function(type, handler){
- var events = this.__events = this.__events || {};
- events[type] = events[type] || [];
- events[type].push(handler);
- },
- off : function(type, handler){
- var events = this.__events = this.__events || {};
- if (type in events) {
- events[type].splice(events[type].indexOf(handler), 1);
- }
- },
- trigger : function(type) {
- var events = this.__events = this.__events || {};
- var eventForTypeCount, i;
- if (type in events) {
- eventForTypeCount = events[type].length;
- for(i = 0; i < eventForTypeCount; i++) {
- events[type][i].apply(this, Array.prototype.slice.call(arguments, 1));
+ fileInput.value = null; // reset file input
+ }
+ };
+
+ var image = ImageCommand;
+
+ 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 OEmbedCommand(options) {
+ base.call(this, {
+ name: 'embed',
+ button: '',
+ prompt: new views_prompt({
+ command: this,
+ placeholder: 'Paste a YouTube or Twitter url...'
+ })
+ });
+
+ this.embedService = new OEmbedder({ url: options.serviceUrl });
+ }
+ inherit(OEmbedCommand, base);
+
+ OEmbedCommand.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 = 'Error: could not connect to embed service.';
+ } else if (typeof error !== 'string') {
+ errorMsg = 'Error: unexpected embed error.';
+ }
+ new views_message().showError(errorMsg);
+ embedIntent.show();
+ } else if (response.error_message) {
+ new views_message().showError(response.error_message);
+ embedIntent.show();
+ } else {
+ var embedModel = new embed(response);
+ editorContext.insertBlock(embedModel, index);
+ editorContext.renderBlockAt(index);
+ if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
+ loadTwitterWidgets(editorContext.element);
}
}
}
- };
-
- var $$$utils$event$emitter$$default = $$$utils$event$emitter$$EventEmitter;
-
- var $$editor$$defaults = {
- placeholder: 'Write here...',
- spellcheck: true,
- autofocus: true,
- model: null,
- serverHost: '',
- stickyToolbar: !!('ontouchstart' in window),
- textFormatCommands: [
- new $$$commands$bold$$default(),
- new $$$commands$italic$$default(),
- new $$$commands$link$$default(),
- new $$$commands$quote$$default(),
- new $$$commands$heading$$default(),
- new $$$commands$subheading$$default()
- ],
- embedCommands: [
- new $$$commands$image$$default({ serviceUrl: '/upload' }),
- new $$$commands$oembed$$default({ serviceUrl: '/embed' })
- ],
- autoTypingCommands: [
- new $$$commands$unordered$list$$default(),
- new $$$commands$ordered$list$$default()
- ],
- compiler: new node_modules$content$kit$compiler$src$compiler$$default({
- includeTypeNames: true, // outputs models with type names, i.e. 'BOLD', for easier debugging
- renderer: new $$$renderers$editor$html$renderer$$default() // subclassed HTML renderer that adds dom structure for additional editor interactivity
- })
- };
-
- function $$editor$$bindContentEditableTypingListeners(editor) {
-
-
- editor.element.addEventListener('keyup', function(e) {
- // Assure there is always a supported block tag, and not empty text nodes or divs.
- // On a carrage return, make sure to always generate a 'p' tag
- if (!$$$utils$selection$utils$$getSelectionBlockElement() ||
- !editor.element.textContent ||
- (!e.shiftKey && e.which === $$$utils$keycodes$$default.ENTER) || (e.ctrlKey && e.which === $$$utils$keycodes$$default.M)) {
- document.execCommand('formatBlock', false, node_modules$content$kit$compiler$src$types$type$$default.PARAGRAPH.tag);
- } //else if (e.which === Keycodes.BKSP) {
- // TODO: Need to rerender when backspacing 2 blocks together
- //var cursorIndex = editor.getCursorIndexInCurrentBlock();
- //var currentBlockElement = getSelectionBlockElement();
- //editor.renderBlockAt(editor.getCurrentBlockIndex(), true);
- //setCursorIndexInElement(currentBlockElement, cursorIndex);
- //}
- });
-
- // On 'PASTE' sanitize and insert
- editor.element.addEventListener('paste', function(e) {
- var data = e.clipboardData;
- var pastedHTML = data && data.getData && data.getData('text/html');
- var sanitizedHTML = pastedHTML && editor.compiler.rerender(pastedHTML);
- if (sanitizedHTML) {
- document.execCommand('insertHTML', false, sanitizedHTML);
- editor.syncVisual();
+ });
+ };
+
+ var oembed = OEmbedCommand;
+
+ // Based on https://github.com/jeromeetienne/microevent.js/blob/master/microevent.js
+ // See also: https://github.com/allouis/minivents/blob/master/minivents.js
+
+ var EventEmitter = {
+ on : function(type, handler){
+ var events = this.__events = this.__events || {};
+ events[type] = events[type] || [];
+ events[type].push(handler);
+ },
+ off : function(type, handler){
+ var events = this.__events = this.__events || {};
+ if (type in events) {
+ events[type].splice(events[type].indexOf(handler), 1);
+ }
+ },
+ trigger : function(type) {
+ var events = this.__events = this.__events || {};
+ var eventForTypeCount, i;
+ if (type in events) {
+ eventForTypeCount = events[type].length;
+ for(i = 0; i < eventForTypeCount; i++) {
+ events[type][i].apply(this, Array.prototype.slice.call(arguments, 1));
}
- e.preventDefault();
- return false;
- });
- }
-
- function $$editor$$bindLiveUpdate(editor) {
- editor.element.addEventListener('input', function() {
- editor.syncContentEditableBlocks();
- });
+ }
}
-
- function $$editor$$bindAutoTypingListeners(editor) {
- // Watch typing patterns for auto format commands (e.g. lists '- ', '1. ')
- editor.element.addEventListener('keyup', function(e) {
- var commands = editor.autoTypingCommands;
- var count = commands && commands.length;
- var selection, i;
-
- if (count) {
- selection = window.getSelection();
- for (i = 0; i < count; i++) {
- if (commands[i].checkAutoFormat(selection.anchorNode)) {
- e.stopPropagation();
- return;
- }
+ };
+
+ var event_emitter = EventEmitter;
+
+ var defaults = {
+ placeholder: 'Write here...',
+ spellcheck: true,
+ autofocus: true,
+ model: null,
+ serverHost: '',
+ stickyToolbar: !!('ontouchstart' in window),
+ textFormatCommands: [
+ new bold(),
+ new italic(),
+ new commands_link(),
+ new quote(),
+ new heading(),
+ new subheading()
+ ],
+ embedCommands: [
+ new image({ serviceUrl: '/upload' }),
+ new oembed({ serviceUrl: '/embed' })
+ ],
+ autoTypingCommands: [
+ new unordered_list(),
+ new ordered_list()
+ ],
+ compiler: new compiler({
+ includeTypeNames: true, // outputs models with type names, i.e. 'BOLD', for easier debugging
+ renderer: new editor_html_renderer() // subclassed HTML renderer that adds dom structure for additional editor interactivity
+ })
+ };
+
+ function bindContentEditableTypingListeners(editor) {
+
+
+ editor.element.addEventListener('keyup', function(e) {
+ // Assure there is always a supported block tag, and not empty text nodes or divs.
+ // On a carrage return, make sure to always generate a 'p' tag
+ if (!getSelectionBlockElement() ||
+ !editor.element.textContent ||
+ (!e.shiftKey && e.which === Keycodes.ENTER) || (e.ctrlKey && e.which === Keycodes.M)) {
+ document.execCommand('formatBlock', false, types_type.PARAGRAPH.tag);
+ } //else if (e.which === Keycodes.BKSP) {
+ // TODO: Need to rerender when backspacing 2 blocks together
+ //var cursorIndex = editor.getCursorIndexInCurrentBlock();
+ //var currentBlockElement = getSelectionBlockElement();
+ //editor.renderBlockAt(editor.getCurrentBlockIndex(), true);
+ //setCursorIndexInElement(currentBlockElement, cursorIndex);
+ //}
+ });
+
+ // On 'PASTE' sanitize and insert
+ editor.element.addEventListener('paste', function(e) {
+ var data = e.clipboardData;
+ var pastedHTML = data && data.getData && data.getData('text/html');
+ var sanitizedHTML = pastedHTML && editor.compiler.rerender(pastedHTML);
+ if (sanitizedHTML) {
+ document.execCommand('insertHTML', false, sanitizedHTML);
+ editor.syncVisual();
+ }
+ e.preventDefault();
+ return false;
+ });
+ }
+
+ function bindLiveUpdate(editor) {
+ editor.element.addEventListener('input', function() {
+ editor.syncContentEditableBlocks();
+ });
+ }
+
+ function bindAutoTypingListeners(editor) {
+ // Watch typing patterns for auto format commands (e.g. lists '- ', '1. ')
+ editor.element.addEventListener('keyup', function(e) {
+ var commands = editor.autoTypingCommands;
+ var count = commands && commands.length;
+ var selection, i;
+
+ if (count) {
+ selection = window.getSelection();
+ for (i = 0; i < count; i++) {
+ if (commands[i].checkAutoFormat(selection.anchorNode)) {
+ e.stopPropagation();
+ return;
}
}
- });
- }
-
- function $$editor$$bindDragAndDrop() {
- // TODO. For now, just prevent redirect when dropping something on the page
- window.addEventListener('dragover', function(e) {
- e.preventDefault(); // prevents showing cursor where to drop
- });
- window.addEventListener('drop', function(e) {
- e.preventDefault(); // prevent page from redirecting
- });
- }
-
- function $$editor$$initEmbedCommands(editor) {
- var commands = editor.embedCommands;
- if(commands) {
- return new $$$views$embed$intent$$default({
- editorContext: editor,
- commands: commands,
- rootElement: editor.element
- });
}
+ });
+ }
+
+ function bindDragAndDrop() {
+ // TODO. For now, just prevent redirect when dropping something on the page
+ window.addEventListener('dragover', function(e) {
+ e.preventDefault(); // prevents showing cursor where to drop
+ });
+ window.addEventListener('drop', function(e) {
+ e.preventDefault(); // prevent page from redirecting
+ });
+ }
+
+ function initEmbedCommands(editor) {
+ var commands = editor.embedCommands;
+ if(commands) {
+ return new embed_intent({
+ editorContext: editor,
+ commands: commands,
+ rootElement: editor.element
+ });
}
+ }
- function $$editor$$applyClassName(editorElement) {
- var editorClassName = 'ck-editor';
- var editorClassNameRegExp = new RegExp(editorClassName);
- var existingClassName = editorElement.className;
-
- if (!editorClassNameRegExp.test(existingClassName)) {
- existingClassName += (existingClassName ? ' ' : '') + editorClassName;
- }
- editorElement.className = existingClassName;
- }
+ function applyClassName(editorElement) {
+ var editorClassName = 'ck-editor';
+ var editorClassNameRegExp = new RegExp(editorClassName);
+ var existingClassName = editorElement.className;
- function $$editor$$applyPlaceholder(editorElement, placeholder) {
- var dataset = editorElement.dataset;
- if (placeholder && !dataset.placeholder) {
- dataset.placeholder = placeholder;
- }
+ if (!editorClassNameRegExp.test(existingClassName)) {
+ existingClassName += (existingClassName ? ' ' : '') + editorClassName;
}
+ editorElement.className = existingClassName;
+ }
- function $$editor$$getNonTextBlocks(blockTypeSet, model) {
- var blocks = [];
- var len = model.length;
- var i, block, type;
- for (i = 0; i < len; i++) {
- block = model[i];
- type = blockTypeSet.findById(block && block.type);
- if (type && !type.isTextType) {
- blocks.push(block);
- }
- }
- return blocks;
+ function applyPlaceholder(editorElement, placeholder) {
+ var dataset = editorElement.dataset;
+ if (placeholder && !dataset.placeholder) {
+ dataset.placeholder = placeholder;
}
+ }
- /**
- * @class Editor
- * An individual Editor
- * @param element `Element` node
- * @param options hash of options
- */
- function $$editor$$Editor(element, options) {
- var editor = this;
- node_modules$content$kit$utils$src$object$utils$$mergeWithOptions(editor, $$editor$$defaults, options);
-
- // Update embed commands by prepending the serverHost
- editor.embedCommands = [
- new $$$commands$image$$default({ serviceUrl: editor.serverHost + '/upload' }),
- new $$$commands$oembed$$default({ serviceUrl: editor.serverHost + '/embed' })
- ];
-
- if (element) {
- $$editor$$applyClassName(element);
- $$editor$$applyPlaceholder(element, editor.placeholder);
- element.spellcheck = editor.spellcheck;
- element.setAttribute('contentEditable', true);
- editor.element = element;
-
- if (editor.model) {
- editor.loadModel(editor.model);
- } else {
- editor.sync();
- }
-
- $$editor$$bindContentEditableTypingListeners(editor);
- $$editor$$bindAutoTypingListeners(editor);
- $$editor$$bindDragAndDrop(editor);
- $$editor$$bindLiveUpdate(editor);
- $$editor$$initEmbedCommands(editor);
-
- editor.textFormatToolbar = new $$$views$text$format$toolbar$$default({ rootElement: element, commands: editor.textFormatCommands, sticky: editor.stickyToolbar });
- editor.linkTooltips = new $$$views$tooltip$$default({ rootElement: element, showForTag: node_modules$content$kit$compiler$src$types$type$$default.LINK.tag });
-
- if(editor.autofocus) { element.focus(); }
+ function getNonTextBlocks(blockTypeSet, model) {
+ var blocks = [];
+ var len = model.length;
+ var i, block, type;
+ for (i = 0; i < len; i++) {
+ block = model[i];
+ type = blockTypeSet.findById(block && block.type);
+ if (type && !type.isTextType) {
+ blocks.push(block);
}
}
+ return blocks;
+ }
- // Add event emitter pub/sub functionality
- node_modules$content$kit$utils$src$object$utils$$merge($$editor$$Editor.prototype, $$$utils$event$emitter$$default);
-
- $$editor$$Editor.prototype.loadModel = function(model) {
- this.model = model;
- this.syncVisual();
- this.trigger('update');
- };
-
- $$editor$$Editor.prototype.syncModel = function() {
- this.model = this.compiler.parse(this.element.innerHTML);
- this.trigger('update');
- };
-
- $$editor$$Editor.prototype.syncVisual = function() {
- this.element.innerHTML = this.compiler.render(this.model);
- };
-
- $$editor$$Editor.prototype.sync = function() {
- this.syncModel();
- this.syncVisual();
- };
-
- $$editor$$Editor.prototype.getCurrentBlockIndex = function(element) {
- var selectionEl = element || $$$utils$selection$utils$$getSelectionBlockElement();
- var blockElements = node_modules$content$kit$utils$src$array$utils$$toArray(this.element.children);
- return blockElements.indexOf(selectionEl);
- };
-
- $$editor$$Editor.prototype.getCursorIndexInCurrentBlock = function() {
- var currentBlock = $$$utils$selection$utils$$getSelectionBlockElement();
- if (currentBlock) {
- return $$$utils$selection$utils$$getCursorOffsetInElement(currentBlock);
- }
- return -1;
- };
-
- $$editor$$Editor.prototype.insertBlock = function(block, index) {
- this.model.splice(index, 0, block);
- this.trigger('update');
- };
+ /**
+ * @class Editor
+ * An individual Editor
+ * @param element `Element` node
+ * @param options hash of options
+ */
+ function Editor(element, options) {
+ var editor = this;
+ mergeWithOptions(editor, defaults, options);
- $$editor$$Editor.prototype.removeBlockAt = function(index) {
- this.model.splice(index, 1);
- this.trigger('update');
- };
+ // Update embed commands by prepending the serverHost
+ editor.embedCommands = [
+ new image({ serviceUrl: editor.serverHost + '/upload' }),
+ new oembed({ serviceUrl: editor.serverHost + '/embed' })
+ ];
- $$editor$$Editor.prototype.replaceBlock = function(block, index) {
- this.model[index] = block;
- this.trigger('update');
- };
+ if (element) {
+ applyClassName(element);
+ applyPlaceholder(element, editor.placeholder);
+ element.spellcheck = editor.spellcheck;
+ element.setAttribute('contentEditable', true);
+ editor.element = element;
- $$editor$$Editor.prototype.renderBlockAt = function(index, replace) {
- var modelAtIndex = this.model[index];
- var html = this.compiler.render([modelAtIndex]);
- var dom = document.createElement('div');
- dom.innerHTML = html;
- var newEl = dom.firstChild;
- var sibling = this.element.children[index];
- if (replace) {
- this.element.replaceChild(newEl, sibling);
+ if (editor.model) {
+ editor.loadModel(editor.model);
} else {
- this.element.insertBefore(newEl, sibling);
- }
- };
-
- $$editor$$Editor.prototype.syncContentEditableBlocks = function() {
- var nonTextBlocks = $$editor$$getNonTextBlocks(this.compiler.blockTypes, this.model);
- var blockElements = node_modules$content$kit$utils$src$array$utils$$toArray(this.element.children);
- var len = blockElements.length;
- var updatedModel = [];
- var i, blockEl;
- for (i = 0; i < len; i++) {
- blockEl = blockElements[i];
- if(blockEl.isContentEditable) {
- updatedModel.push(this.compiler.parser.serializeBlockNode(blockEl));
- } else {
- updatedModel.push(nonTextBlocks.shift());
- }
+ editor.sync();
+ }
+
+ bindContentEditableTypingListeners(editor);
+ bindAutoTypingListeners(editor);
+ bindDragAndDrop(editor);
+ bindLiveUpdate(editor);
+ initEmbedCommands(editor);
+
+ editor.textFormatToolbar = new text_format_toolbar({ rootElement: element, commands: editor.textFormatCommands, sticky: editor.stickyToolbar });
+ editor.linkTooltips = new views_tooltip({ rootElement: element, showForTag: types_type.LINK.tag });
+
+ if(editor.autofocus) { element.focus(); }
+ }
+ }
+
+ // Add event emitter pub/sub functionality
+ merge(Editor.prototype, event_emitter);
+
+ Editor.prototype.loadModel = function(model) {
+ this.model = model;
+ this.syncVisual();
+ this.trigger('update');
+ };
+
+ Editor.prototype.syncModel = function() {
+ this.model = this.compiler.parse(this.element.innerHTML);
+ this.trigger('update');
+ };
+
+ Editor.prototype.syncVisual = function() {
+ this.element.innerHTML = this.compiler.render(this.model);
+ };
+
+ Editor.prototype.sync = function() {
+ this.syncModel();
+ this.syncVisual();
+ };
+
+ Editor.prototype.getCurrentBlockIndex = function(element) {
+ var selectionEl = element || getSelectionBlockElement();
+ var blockElements = toArray(this.element.children);
+ return blockElements.indexOf(selectionEl);
+ };
+
+ Editor.prototype.getCursorIndexInCurrentBlock = function() {
+ var currentBlock = getSelectionBlockElement();
+ if (currentBlock) {
+ return getCursorOffsetInElement(currentBlock);
+ }
+ return -1;
+ };
+
+ Editor.prototype.insertBlock = function(block, index) {
+ this.model.splice(index, 0, block);
+ this.trigger('update');
+ };
+
+ Editor.prototype.removeBlockAt = function(index) {
+ this.model.splice(index, 1);
+ this.trigger('update');
+ };
+
+ Editor.prototype.replaceBlock = function(block, index) {
+ this.model[index] = block;
+ this.trigger('update');
+ };
+
+ Editor.prototype.renderBlockAt = function(index, replace) {
+ var modelAtIndex = this.model[index];
+ var html = this.compiler.render([modelAtIndex]);
+ var dom = document.createElement('div');
+ dom.innerHTML = html;
+ var newEl = dom.firstChild;
+ var sibling = this.element.children[index];
+ if (replace) {
+ this.element.replaceChild(newEl, sibling);
+ } else {
+ this.element.insertBefore(newEl, sibling);
+ }
+ };
+
+ Editor.prototype.syncContentEditableBlocks = function() {
+ var nonTextBlocks = getNonTextBlocks(this.compiler.blockTypes, this.model);
+ var blockElements = toArray(this.element.children);
+ var len = blockElements.length;
+ var updatedModel = [];
+ var i, blockEl;
+ for (i = 0; i < len; i++) {
+ blockEl = blockElements[i];
+ if(blockEl.isContentEditable) {
+ updatedModel.push(this.compiler.parser.serializeBlockNode(blockEl));
+ } else {
+ updatedModel.push(nonTextBlocks.shift());
}
- this.model = updatedModel;
- this.trigger('update');
- };
+ }
+ this.model = updatedModel;
+ this.trigger('update');
+ };
- var $$editor$$default = $$editor$$Editor;
+ var editor_editor = Editor;
- /**
- * @class EditorFactory
- * @private
- * `EditorFactory` is publically exposed as `Editor`
- * It takes an `element` param which can be a css selector, Node, or NodeList
- * and sets up indiviual `Editor` instances
- */
- function $$editor$editor$factory$$EditorFactory(element, options) {
- var editors = [];
- var elements, elementsLen, i;
+ function EditorFactory(element, options) {
+ var editors = [];
+ var elements, elementsLen, i;
- if (!element) {
- return new $$editor$$default(element, options);
- }
+ if (!element) {
+ return new editor_editor(element, options);
+ }
- if (typeof element === 'string') {
- elements = document.querySelectorAll(element);
- } else if (element && element.length) {
- elements = element;
- } else if (element) {
- elements = [element];
- }
+ if (typeof element === 'string') {
+ elements = document.querySelectorAll(element);
+ } else if (element && element.length) {
+ elements = element;
+ } else if (element) {
+ elements = [element];
+ }
- if (elements) {
- elementsLen = elements.length;
- for (i = 0; i < elementsLen; i++) {
- editors.push(new $$editor$$default(elements[i], options));
- }
+ if (elements) {
+ elementsLen = elements.length;
+ for (i = 0; i < elementsLen; i++) {
+ editors.push(new editor_editor(elements[i], options));
}
-
- return editors.length > 1 ? editors : editors[0];
}
- $$editor$editor$factory$$EditorFactory.prototype = $$editor$$default.prototype;
+ return editors.length > 1 ? editors : editors[0];
+ }
- var $$editor$editor$factory$$default = $$editor$editor$factory$$EditorFactory;
+ EditorFactory.prototype = editor_editor.prototype;
- // Create a namespace and selectivly expose public modules
- var src$js$index$$ContentKit = {};
- src$js$index$$ContentKit.Type = node_modules$content$kit$compiler$src$types$type$$default;
- src$js$index$$ContentKit.BlockModel = node_modules$content$kit$compiler$src$models$block$$default;
- src$js$index$$ContentKit.EmbedModel = node_modules$content$kit$compiler$src$models$embed$$default;
- src$js$index$$ContentKit.Compiler = node_modules$content$kit$compiler$src$compiler$$default;
- src$js$index$$ContentKit.HTMLParser = node_modules$content$kit$compiler$src$parsers$html$parser$$default;
- src$js$index$$ContentKit.HTMLRenderer = node_modules$content$kit$compiler$src$renderers$html$renderer$$default;
- src$js$index$$ContentKit.Editor = $$editor$editor$factory$$default;
+ var editor_factory = EditorFactory;
- window.ContentKit = src$js$index$$ContentKit;
+ var ContentKit = {};
+ ContentKit.Type = types_type;
+ ContentKit.BlockModel = models_block;
+ ContentKit.EmbedModel = embed;
+ ContentKit.Compiler = compiler;
+ ContentKit.HTMLParser = html_parser;
+ ContentKit.HTMLRenderer = html_renderer;
+ ContentKit.Editor = editor_factory;
+ window.ContentKit = ContentKit;
}(this, document));
diff --git a/gulpfile.js b/gulpfile.js
index 8903581e5..6911ece67 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -5,13 +5,11 @@ var qunit = require('gulp-qunit');
var less = require('gulp-less');
var concat = require('gulp-concat');
var header = require('gulp-header');
-var footer = require('gulp-footer');
-var util = require('gulp-util');
var open = require('gulp-open');
-var replace = require('gulp-replace');
var uglify = require('gulp-uglify');
var cssmin = require('gulp-cssmin');
-var transpile = require('gulp-es6-module-transpiler');
+var file = require('gulp-file');
+var transpile = require('esperanto');
// -------------------------------------------
@@ -37,7 +35,6 @@ var cssSrc = [
var distDest = './dist/';
var jsDistName = 'content-kit-editor.js';
var cssDistName = 'content-kit-editor.css';
-var jsDistPath = distDest + jsDistName;
var cssDistPath = distDest + cssDistName;
var testRunner = './tests/index.html';
@@ -49,7 +46,6 @@ var banner = ['/**',
' * @version <%= pkg.version %>',
' * @author <%= pkg.author %>',
' * @license <%= pkg.license %>',
- ' * Last modified: ' + util.date('mmm d, yyyy'),
' */',
''].join('\n');
@@ -64,18 +60,20 @@ gulp.task('lint', function() {
});
gulp.task('build-js', function() {
- return gulp.src(jsEntry)
- .pipe(transpile({ formatter: 'bundle' }))
- .pipe(concat(jsDistName))
- // Remove gulp-es6-module-transpiler's IIFE so we can add our own
- .pipe(replace(/^\(function\(\) {\n/g, ''))
- .pipe(replace(/\}\)\.call\(this\);/g, ''))
- .pipe(replace(/\n\/\/# sourceMappingURL\=bundle\.map/g, ''))
- // end IFFE removal
- .pipe(header(iifeHeader))
- .pipe(footer(iifeFooter))
- .pipe(header(banner, { pkg : pkg } ))
- .pipe(gulp.dest(distDest));
+ return transpile.bundle({
+ entry: jsEntry,
+ resolvePath: function (importee, importer) {
+ return 'node_modules/' + importee + '.js';
+ }
+ }).then(function(bundle) {
+ var transpiled = bundle.concat({
+ intro: iifeHeader,
+ outro: iifeFooter
+ });
+ return file(jsDistName, transpiled.code, { src: true })
+ .pipe(header(banner, { pkg : pkg } ))
+ .pipe(gulp.dest(distDest));
+ });
});
// Compiles LESS and concatenates css
diff --git a/package.json b/package.json
index 95e892548..876066e13 100644
--- a/package.json
+++ b/package.json
@@ -27,22 +27,20 @@
"express": "^4.11.1"
},
"devDependencies": {
- "content-kit-compiler": "0.1.2",
- "content-kit-utils": "0.1.1",
+ "content-kit-compiler": "0.2.3",
+ "content-kit-utils": "0.1.2",
"del": "^1.1.1",
+ "esperanto": "^0.6.32",
"gulp": "^3.8.11",
"gulp-concat": "~2.1.7",
"gulp-cssmin": "^0.1.6",
- "gulp-es6-module-transpiler": "^0.2.1",
- "gulp-footer": "^1.0.5",
+ "gulp-file": "^0.2.0",
"gulp-header": "^1.2.2",
"gulp-jshint": "~1.3.4",
"gulp-less": "^1.3.1",
"gulp-open": "^0.2.8",
"gulp-qunit": "^1.2.1",
- "gulp-replace": "^0.5.2",
"gulp-uglify": "^1.1.0",
- "gulp-util": "~2.2.12",
"qunitjs": "^1.17.1"
}
}