diff --git a/demo/demo.css b/demo/demo.css
index 89ec43202..c8ed63494 100644
--- a/demo/demo.css
+++ b/demo/demo.css
@@ -7,9 +7,8 @@
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
color: #333;
- margin: 0 1.45em 3em;
+ margin: 0;
padding: 0;
- overflow-y: scroll;
}
@media only screen and (max-width: 767px) {
body {
@@ -17,9 +16,15 @@ body {
}
}
-.wrapper {
+.editor-pane {
max-width: 50em;
- margin: 6.7em auto 1em;
+ margin: 0 auto;
+ padding: 5.5em 1.45em 3em;
+ width: 100%;
+}
+.code-pane-open .editor-pane {
+ width: 50%;
+ margin: 0;
}
header {
@@ -74,23 +79,30 @@ header {
outline: none;
}
-#code-panes {
- display: none;
-}
-.code-pane {
- position: absolute;
+
+.code-panes {
+ position: fixed;
top: 3.125em;
bottom: 0;
+ right: -50%;
+ width: 0;
+}
+.code-pane-open .code-panes {
right: 0;
width: 50%;
- border-left: 1px dotted #c0c5ce;
+}
+.code-pane {
+ display: none;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
background-color: #2b303b;
overflow: hidden;
}
.code-pane:first-child {
- left: 0;
- right: auto;
- border-left: none;
+ display: block;
}
.code-pane code {
white-space: pre-wrap;
diff --git a/demo/demo.js b/demo/demo.js
index 3190c761e..dadff3ec6 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -3,29 +3,33 @@
'use strict';
exports.ContentKitDemo = {
- toggleCode: function(e, button, editor) {
- var codeUI = document.getElementById('code-panes'),
- editorUI = editor.element;
-
- if(codeUI.style.display === '') {
- var codePaneJSON = document.getElementById('code-json'),
- codePaneHTML = document.getElementById('code-html'),
- json = editor.model,
- html = editor.compiler.render(json);
+ toggleCodePane: function(editor) {
+ if(document.body.className === 'code-pane-open') {
+ this.closeCodePane();
+ } else {
+ this.openCodePane(editor);
+ }
+ },
+
+ openCodePane: function(editor) {
+ this.syncCodePane(editor);
+ window.getSelection().removeAllRanges();
+ document.body.className = 'code-pane-open';
+ },
- codePaneJSON.innerHTML = this.syntaxHighlight(json);
- codePaneHTML.textContent = this.formatXML(html);
+ closeCodePane: function() {
+ window.getSelection().removeAllRanges();
+ document.body.className = '';
+ },
- window.getSelection().removeAllRanges();
+ syncCodePane: function(editor) {
+ var codePaneJSON = document.getElementById('code-json');
+ var codePaneHTML = document.getElementById('code-html');
+ var json = editor.model;
+ var html = editor.compiler.render(json);
- codeUI.style.display = 'block';
- editorUI.style.display = 'none';
- button.textContent = 'Show Editor';
- } else {
- codeUI.style.display = '';
- editorUI.style.display = 'block';
- button.textContent = 'Show Code';
- }
+ codePaneJSON.innerHTML = this.syntaxHighlight(json);
+ codePaneHTML.textContent = this.formatXML(html);
},
formatXML: function(xml) {
diff --git a/demo/index.html b/demo/index.html
index 165258bf6..66e072abc 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -16,11 +16,13 @@
-
+
+
+
A modern, minimalist text editor allowing you to write in a distraction free environment. Simply select any text you would like to format and a toolbar will appear where you can toggle options such as bold and italic , or create a link .
Create headings by pressing "H1" on the toolbar
@@ -53,9 +55,10 @@ Keyboard shortcuts:
Enjoy focusing on your content and not worrying about formatting!
+
-
+
ContentKit JSON
@@ -72,58 +75,10 @@
Keyboard shortcuts:
diff --git a/dist/content-kit-editor.css b/dist/content-kit-editor.css
index cdf0673aa..4a06d080c 100755
--- a/dist/content-kit-editor.css
+++ b/dist/content-kit-editor.css
@@ -285,10 +285,11 @@
.ck-embed figcaption {
position: absolute;
top: 0;
- right: -140px;
- width: 120px;
+ right: -130px;
+ width: 130px;
text-align: left;
margin: 0;
+ padding-left: 2em;
}
}
.ck-video-container {
diff --git a/dist/content-kit-editor.js b/dist/content-kit-editor.js
index e540a3d24..d0a1087f2 100755
--- a/dist/content-kit-editor.js
+++ b/dist/content-kit-editor.js
@@ -3,7 +3,7 @@
* @version 0.1.0
* @author Garth Poitras
(http://garthpoitras.com/)
* @license MIT
- * Last modified: Aug 14, 2014
+ * Last modified: Aug 22, 2014
*/
(function(exports, document) {
@@ -40,15 +40,14 @@ define("content-kit",
__exports__["default"] = ContentKit;
});
define("content-kit-compiler/compiler",
- ["./parsers/html-parser","./renderers/html-renderer","./types/type","./types/default-types","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ ["./parsers/html-parser","./renderers/html-renderer","./types/default-types","../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
"use strict";
var HTMLParser = __dependency1__["default"];
var HTMLRenderer = __dependency2__["default"];
- var Type = __dependency3__["default"];
- var DefaultBlockTypeSet = __dependency4__.DefaultBlockTypeSet;
- var DefaultMarkupTypeSet = __dependency4__.DefaultMarkupTypeSet;
- var mergeWithOptions = __dependency5__.mergeWithOptions;
+ var DefaultBlockTypeSet = __dependency3__.DefaultBlockTypeSet;
+ var DefaultMarkupTypeSet = __dependency3__.DefaultMarkupTypeSet;
+ var mergeWithOptions = __dependency4__.mergeWithOptions;
/**
* @class Compiler
@@ -96,9 +95,7 @@ define("content-kit-compiler/compiler",
* @param {Type} type
*/
Compiler.prototype.registerBlockType = function(type) {
- if (type instanceof Type) {
- return this.blockTypes.addType(type);
- }
+ return this.blockTypes.addType(type);
};
/**
@@ -106,9 +103,7 @@ define("content-kit-compiler/compiler",
* @param {Type} type
*/
Compiler.prototype.registerMarkupType = function(type) {
- if (type instanceof Type) {
- return this.markupTypes.addType(type);
- }
+ return this.markupTypes.addType(type);
};
__exports__["default"] = Compiler;
@@ -214,9 +209,55 @@ define("content-kit-editor/editor-factory",
__exports__["default"] = EditorFactory;
});
+define("content-kit-editor/editor-html-renderer",
+ ["../content-kit-compiler/renderers/html-renderer","../content-kit-compiler/types/type","../content-kit-utils/object-utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var HTMLRenderer = __dependency1__["default"];
+ var Type = __dependency2__["default"];
+ var inherit = __dependency3__.inherit;
+
+ function embedRenderer(model) {
+ var embedAttrs = model.attributes;
+ var isVideo = embedAttrs.embed_type === 'video';
+ return '' +
+ '
' +
+ (isVideo ? '' : '') + this.render(model) + (isVideo ? '
' : '') +
+ '' + embedAttrs.provider_name + ': ' +
+ '' + embedAttrs.title + ' ' +
+ ' ' +
+ ' ' +
+ '
';
+ }
+
+ function imageRenderer(model) {
+ return '' +
+ '' + this.render(model) + ' ' +
+ '
';
+ }
+
+ var typeRenderers = {};
+ typeRenderers[Type.EMBED.id] = embedRenderer;
+ typeRenderers[Type.IMAGE.id] = imageRenderer;
+
+ /**
+ * @class EditorHTMLRenderer
+ * @constructor
+ * Subclass of HTMLRenderer specifically for the Editor
+ * Wraps interactive elements to add functionality
+ */
+ function EditorHTMLRenderer() {
+ HTMLRenderer.call(this, {
+ typeRenderers: typeRenderers
+ });
+ }
+ inherit(EditorHTMLRenderer, HTMLRenderer);
+
+ __exports__["default"] = EditorHTMLRenderer;
+ });
define("content-kit-editor/editor",
- ["./views/text-format-toolbar","./views/tooltip","./views/embed-intent","./commands/unordered-list","./commands/ordered-list","./commands/text-format","./constants","./utils/selection-utils","../content-kit-compiler/compiler","../content-kit-compiler/models/text","../content-kit-compiler/types/type","../content-kit-utils/array-utils","../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
+ ["./views/text-format-toolbar","./views/tooltip","./views/embed-intent","./commands/unordered-list","./commands/ordered-list","./commands/text-format","./constants","./utils/selection-utils","../content-kit-compiler/compiler","../content-kit-compiler/models/text","../content-kit-compiler/types/type","../content-kit-utils/array-utils","../content-kit-utils/object-utils","./editor-html-renderer","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) {
"use strict";
var TextFormatToolbar = __dependency1__["default"];
var Tooltip = __dependency2__["default"];
@@ -237,10 +278,10 @@ define("content-kit-editor/editor",
var Type = __dependency11__["default"];
var toArray = __dependency12__.toArray;
var merge = __dependency13__.merge;
+ var EditorHTMLRenderer = __dependency14__["default"];
var editorClassName = 'ck-editor';
var editorClassNameRegExp = new RegExp(editorClassName);
- var afterRenderHooks = [];
function plainTextToBlocks(plainText, blockTag) {
var blocks = plainText.split(RegEx.NEWLINE),
@@ -330,12 +371,6 @@ define("content-kit-editor/editor",
});
}
- function runAfterRenderHooks(element, blockModel) {
- for (var i = 0, len = afterRenderHooks.length; i < len; i++) {
- afterRenderHooks[i].call(null, element, blockModel);
- }
- }
-
/**
* @class Editor
* An individual Editor
@@ -365,7 +400,10 @@ define("content-kit-editor/editor",
element.setAttribute('contentEditable', true);
editor.element = element;
- var compiler = editor.compiler = options.compiler || new Compiler();
+ var compiler = editor.compiler = options.compiler || new Compiler({
+ includeTypeNames: true, // output type names for easier debugging
+ renderer: new EditorHTMLRenderer()
+ });
editor.syncModel();
bindTypingEvents(editor);
@@ -413,7 +451,6 @@ define("content-kit-editor/editor",
var blockElements = toArray(this.element.children);
var element = blockElements[index];
element.innerHTML = html;
- runAfterRenderHooks(element, blockModel);
};
Editor.prototype.getCurrentBlockIndex = function() {
@@ -441,16 +478,6 @@ define("content-kit-editor/editor",
this.textFormatToolbar.addCommand(command);
};
- Editor.prototype.willRenderType = function(type, renderer) {
- this.compiler.renderer.willRenderType(type, renderer);
- };
-
- Editor.prototype.afterRender = function(callback) {
- if ('function' === typeof callback) {
- afterRenderHooks.push(callback);
- }
- };
-
__exports__["default"] = Editor;
});
define("content-kit-utils/array-utils",
@@ -928,11 +955,10 @@ define("ext/loader",
})();
});
define("content-kit-compiler/models/block",
- ["./model","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
+ ["./model","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
var Model = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
/**
* Ensures block markups at the same index are always in a specific order.
@@ -959,17 +985,15 @@ define("content-kit-compiler/models/block",
this.value = options.value || '';
this.markup = sortBlockMarkups(options.markup || []);
}
- inherit(BlockModel, Model);
__exports__["default"] = BlockModel;
});
define("content-kit-compiler/models/embed",
- ["../../content-kit-utils/object-utils","../models/model","../types/type","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ ["../models/model","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
- var inherit = __dependency1__.inherit;
- var Model = __dependency2__["default"];
- var Type = __dependency3__["default"];
+ var Model = __dependency1__["default"];
+ var Type = __dependency2__["default"];
/**
* @class EmbedModel
@@ -1009,17 +1033,15 @@ define("content-kit-compiler/models/embed",
attributes.html = embedHtml;
}
}
- inherit(Model, EmbedModel);
__exports__["default"] = EmbedModel;
});
define("content-kit-compiler/models/image",
- ["./block","../types/type","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ ["./block","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var BlockModel = __dependency1__["default"];
var Type = __dependency2__["default"];
- var inherit = __dependency3__.inherit;
/**
* @class ImageModel
@@ -1036,16 +1058,14 @@ define("content-kit-compiler/models/image",
}
BlockModel.call(this, options);
}
- inherit(ImageModel, BlockModel);
__exports__["default"] = ImageModel;
});
define("content-kit-compiler/models/markup",
- ["./model","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
+ ["./model","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
var Model = __dependency1__["default"];
- var inherit = __dependency2__.inherit;
/**
* @class MarkupModel
@@ -1058,7 +1078,6 @@ define("content-kit-compiler/models/markup",
this.start = options.start || 0;
this.end = options.end || 0;
}
- inherit(MarkupModel, Model);
__exports__["default"] = MarkupModel;
});
@@ -1088,12 +1107,11 @@ define("content-kit-compiler/models/model",
__exports__["default"] = Model;
});
define("content-kit-compiler/models/text",
- ["./block","../types/type","../../content-kit-utils/object-utils","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ ["./block","../types/type","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var BlockModel = __dependency1__["default"];
var Type = __dependency2__["default"];
- var inherit = __dependency3__.inherit;
/**
* @class TextModel
@@ -1107,7 +1125,6 @@ define("content-kit-compiler/models/text",
options.type_name = Type.TEXT.name;
BlockModel.call(this, options);
}
- inherit(TextModel, BlockModel);
__exports__["default"] = TextModel;
});
@@ -1476,7 +1493,8 @@ define("content-kit-compiler/renderers/html-renderer",
function HTMLRenderer(options) {
var defaults = {
blockTypes : DefaultBlockTypeSet,
- markupTypes : DefaultMarkupTypeSet
+ markupTypes : DefaultMarkupTypeSet,
+ typeRenderers : {}
};
mergeWithOptions(this, defaults, options);
}
@@ -1487,12 +1505,11 @@ define("content-kit-compiler/renderers/html-renderer",
* @param renderer the rendering function that returns a string of html
* Registers custom rendering hooks for a type
*/
- var renderHooks = {};
HTMLRenderer.prototype.willRenderType = function(type, renderer) {
if ('number' !== typeof type) {
type = type.id;
}
- renderHooks[type] = renderer;
+ this.typeRenderers[type] = renderer;
};
/**
@@ -1522,7 +1539,7 @@ define("content-kit-compiler/renderers/html-renderer",
for (i = 0; i < len; i++) {
item = model[i];
renderer = this.rendererFor(item);
- renderHook = renderHooks[item.type];
+ renderHook = this.typeRenderers[item.type];
itemHtml = renderHook ? renderHook.call(renderer, item) : renderer.render(item);
if (itemHtml) { html += itemHtml; }
}
@@ -1570,9 +1587,11 @@ define("content-kit-compiler/types/default-types",
__exports__.DefaultMarkupTypeSet = DefaultMarkupTypeSet;
});
define("content-kit-compiler/types/type-set",
- ["exports"],
- function(__exports__) {
+ ["./type","exports"],
+ function(__dependency1__, __exports__) {
"use strict";
+ var Type = __dependency1__["default"];
+
/**
* @class TypeSet
* @private
@@ -1596,15 +1615,17 @@ define("content-kit-compiler/types/type-set",
* Adds a type to the set
*/
addType: function(type) {
- this[type.name] = type;
- if (type.id === undefined) {
- type.id = this._autoId++;
- }
- this.idLookup[type.id] = type;
- if (type.tag) {
- this.tagLookup[type.tag] = type;
+ if (type instanceof Type) {
+ this[type.name] = type;
+ if (type.id === undefined) {
+ type.id = this._autoId++;
+ }
+ this.idLookup[type.id] = type;
+ if (type.tag) {
+ this.tagLookup[type.tag] = type;
+ }
+ return type;
}
- return type;
},
/**
@@ -1762,6 +1783,17 @@ define("content-kit-editor/commands/embed",
var RegEx = __dependency6__.RegEx;
var OEmbedder = __dependency7__.OEmbedder;
+ function loadTwitterWidgets(element) {
+ if (window.twttr) {
+ window.twttr.widgets.load(element);
+ } else {
+ var script = document.createElement('script');
+ script.async = true;
+ script.src = 'http://platform.twitter.com/widgets.js';
+ document.head.appendChild(script);
+ }
+ }
+
function EmbedCommand(options) {
Command.call(this, {
name: 'embed',
@@ -1779,21 +1811,30 @@ define("content-kit-editor/commands/embed",
EmbedCommand.prototype.exec = function(url) {
var command = this;
var editorContext = command.editorContext;
+ var embedIntent = command.embedIntent;
var index = editorContext.getCurrentBlockIndex();
- command.embedIntent.showLoading();
+ embedIntent.showLoading();
this.embedService.fetch({
url: url,
complete: function(response, error) {
- command.embedIntent.hideLoading();
-
+ embedIntent.hideLoading();
if (error) {
- new Message().show('Embed error');
+ var errorMsg = error;
+ if (error.target && error.target.status === 0) {
+ errorMsg = 'Could not connect to embed service';
+ } else if (typeof error !== 'string') {
+ errorMsg = 'Embed error';
+ }
+ new Message().show(errorMsg);
} else {
var embedModel = new EmbedModel(response);
editorContext.insertBlockAt(embedModel, index);
editorContext.syncVisualAt(index);
+ if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
+ loadTwitterWidgets(editorContext.element);
+ }
}
}
});
@@ -1868,6 +1909,17 @@ define("content-kit-editor/commands/image",
var inherit = __dependency4__.inherit;
var FileUploader = __dependency5__.FileUploader;
+ 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 ImageCommand(options) {
Command.call(this, {
name: 'image',
@@ -1880,19 +1932,12 @@ define("content-kit-editor/commands/image",
ImageCommand.prototype = {
exec: function() {
ImageCommand._super.prototype.exec.call(this);
- var clickEvent = new MouseEvent('click', { bubbles: false });
- if (!this.fileInput) {
- var command = this;
- var fileInput = this.fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.accept = 'image/*';
- fileInput.className = 'ck-file-input';
- fileInput.addEventListener('change', function(e) {
- command.handleFile(e);
- });
+ var fileInput = this.fileInput;
+ if (!fileInput) {
+ fileInput = this.fileInput = createFileInput(this);
document.body.appendChild(fileInput);
}
- this.fileInput.dispatchEvent(clickEvent);
+ fileInput.dispatchEvent(new MouseEvent('click', { bubbles: false }));
},
handleFile: function(e) {
var fileInput = e.target;
@@ -2303,7 +2348,7 @@ define("content-kit-editor/utils/selection-utils",
function selectionIsEditable(selection) {
var el = getSelectionBlockElement(selection);
- return el.contentEditable !== 'false';
+ return el.isContentEditable;
}
/*
@@ -2831,7 +2876,7 @@ define("content-kit-editor/views/tooltip",
rootElement.addEventListener('mouseover', function(e) {
var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement);
- if (target) {
+ if (target && target.isContentEditable) {
timeout = setTimeout(function() {
tooltip.showLink(target.href, target);
}, delay);
@@ -2920,6 +2965,30 @@ define("content-kit-compiler/renderers/embeds/instagram",
__exports__["default"] = InstagramRenderer;
});
+define("content-kit-compiler/renderers/embeds/link",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+
+ function LinkEmbedRenderer() {}
+ LinkEmbedRenderer.prototype.render = function(model) {
+ return ' ';
+ };
+
+ __exports__["default"] = LinkEmbedRenderer;
+ });
+define("content-kit-compiler/renderers/embeds/photo",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+
+ function PhotoEmbedRenderer() {}
+ PhotoEmbedRenderer.prototype.render = function(model) {
+ return ' ';
+ };
+
+ __exports__["default"] = PhotoEmbedRenderer;
+ });
define("content-kit-compiler/renderers/embeds/twitter",
["exports"],
function(__exports__) {
@@ -2940,7 +3009,7 @@ define("content-kit-compiler/renderers/embeds/youtube",
var RegExVideoId = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;
function getVideoIdFromUrl(url) {
- var match = url.match(RegExVideoId);
+ var match = url && url.match(RegExVideoId);
if (match && match[1].length === 11){
return match[1];
}
diff --git a/gulpfile.js b/gulpfile.js
index 5b153b56a..cb9f7f77a 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -107,7 +107,7 @@ gulp.task('clean', function() {
// Watches when js files change and automatically lints/builds
gulp.task('watch-js', function() {
- gulp.watch(jsSrc.concat(jsExtSrc), ['lint', 'build-js']);
+ gulp.watch(jsSrc, ['lint', 'build-js']);
});
// Watches when css files change and automatically builds
diff --git a/src/css/embeds.less b/src/css/embeds.less
index 64270ed71..f76dfd8c0 100644
--- a/src/css/embeds.less
+++ b/src/css/embeds.less
@@ -90,10 +90,11 @@
.ck-embed figcaption {
position: absolute;
top: 0;
- right: -140px;
- width: 120px;
+ right: -130px;
+ width: 130px;
text-align: left;
margin: 0;
+ padding-left: 2em;
}
}
diff --git a/src/js/content-kit-compiler/compiler.js b/src/js/content-kit-compiler/compiler.js
index 348898681..b2d6fc0bc 100644
--- a/src/js/content-kit-compiler/compiler.js
+++ b/src/js/content-kit-compiler/compiler.js
@@ -1,8 +1,7 @@
import HTMLParser from './parsers/html-parser';
import HTMLRenderer from './renderers/html-renderer';
-import Type from './types/type';
import { DefaultBlockTypeSet, DefaultMarkupTypeSet } from './types/default-types';
-import { mergeWithOptions } from '../../content-kit-utils/object-utils';
+import { mergeWithOptions } from '../content-kit-utils/object-utils';
/**
* @class Compiler
@@ -50,9 +49,7 @@ Compiler.prototype.render = function(data) {
* @param {Type} type
*/
Compiler.prototype.registerBlockType = function(type) {
- if (type instanceof Type) {
- return this.blockTypes.addType(type);
- }
+ return this.blockTypes.addType(type);
};
/**
@@ -60,9 +57,7 @@ Compiler.prototype.registerBlockType = function(type) {
* @param {Type} type
*/
Compiler.prototype.registerMarkupType = function(type) {
- if (type instanceof Type) {
- return this.markupTypes.addType(type);
- }
+ return this.markupTypes.addType(type);
};
export default Compiler;
diff --git a/src/js/content-kit-compiler/models/block.js b/src/js/content-kit-compiler/models/block.js
index 07c4e223f..b97e44ab9 100644
--- a/src/js/content-kit-compiler/models/block.js
+++ b/src/js/content-kit-compiler/models/block.js
@@ -1,5 +1,4 @@
import Model from './model';
-import { inherit } from '../../content-kit-utils/object-utils';
/**
* Ensures block markups at the same index are always in a specific order.
@@ -26,6 +25,5 @@ function BlockModel(options) {
this.value = options.value || '';
this.markup = sortBlockMarkups(options.markup || []);
}
-inherit(BlockModel, Model);
export default BlockModel;
diff --git a/src/js/content-kit-compiler/models/embed.js b/src/js/content-kit-compiler/models/embed.js
index da4a5927c..ff5f1288a 100644
--- a/src/js/content-kit-compiler/models/embed.js
+++ b/src/js/content-kit-compiler/models/embed.js
@@ -1,4 +1,3 @@
-import { inherit } from '../../content-kit-utils/object-utils';
import Model from '../models/model';
import Type from '../types/type';
@@ -40,6 +39,5 @@ function EmbedModel(options) {
attributes.html = embedHtml;
}
}
-inherit(Model, EmbedModel);
export default EmbedModel;
diff --git a/src/js/content-kit-compiler/models/image.js b/src/js/content-kit-compiler/models/image.js
index 3e1c7ff34..29553f96a 100644
--- a/src/js/content-kit-compiler/models/image.js
+++ b/src/js/content-kit-compiler/models/image.js
@@ -1,6 +1,5 @@
import BlockModel from './block';
import Type from '../types/type';
-import { inherit } from '../../content-kit-utils/object-utils';
/**
* @class ImageModel
@@ -17,6 +16,5 @@ function ImageModel(options) {
}
BlockModel.call(this, options);
}
-inherit(ImageModel, BlockModel);
export default ImageModel;
diff --git a/src/js/content-kit-compiler/models/markup.js b/src/js/content-kit-compiler/models/markup.js
index da3aa4876..4971b7fbf 100644
--- a/src/js/content-kit-compiler/models/markup.js
+++ b/src/js/content-kit-compiler/models/markup.js
@@ -1,5 +1,4 @@
import Model from './model';
-import { inherit } from '../../content-kit-utils/object-utils';
/**
* @class MarkupModel
@@ -12,6 +11,5 @@ function MarkupModel(options) {
this.start = options.start || 0;
this.end = options.end || 0;
}
-inherit(MarkupModel, Model);
export default MarkupModel;
diff --git a/src/js/content-kit-compiler/models/text.js b/src/js/content-kit-compiler/models/text.js
index 602c8c650..342188419 100644
--- a/src/js/content-kit-compiler/models/text.js
+++ b/src/js/content-kit-compiler/models/text.js
@@ -1,6 +1,5 @@
import BlockModel from './block';
import Type from '../types/type';
-import { inherit } from '../../content-kit-utils/object-utils';
/**
* @class TextModel
@@ -14,6 +13,5 @@ function TextModel(options) {
options.type_name = Type.TEXT.name;
BlockModel.call(this, options);
}
-inherit(TextModel, BlockModel);
export default TextModel;
diff --git a/src/js/content-kit-compiler/renderers/embeds/link.js b/src/js/content-kit-compiler/renderers/embeds/link.js
new file mode 100644
index 000000000..8f4f9baf9
--- /dev/null
+++ b/src/js/content-kit-compiler/renderers/embeds/link.js
@@ -0,0 +1,7 @@
+
+function LinkEmbedRenderer() {}
+LinkEmbedRenderer.prototype.render = function(model) {
+ return ' ';
+};
+
+export default LinkEmbedRenderer;
diff --git a/src/js/content-kit-compiler/renderers/embeds/photo.js b/src/js/content-kit-compiler/renderers/embeds/photo.js
new file mode 100644
index 000000000..c06d86f52
--- /dev/null
+++ b/src/js/content-kit-compiler/renderers/embeds/photo.js
@@ -0,0 +1,7 @@
+
+function PhotoEmbedRenderer() {}
+PhotoEmbedRenderer.prototype.render = function(model) {
+ return ' ';
+};
+
+export default PhotoEmbedRenderer;
diff --git a/src/js/content-kit-compiler/renderers/embeds/youtube.js b/src/js/content-kit-compiler/renderers/embeds/youtube.js
index e88de5a32..3d9b4bf61 100644
--- a/src/js/content-kit-compiler/renderers/embeds/youtube.js
+++ b/src/js/content-kit-compiler/renderers/embeds/youtube.js
@@ -2,7 +2,7 @@
var RegExVideoId = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;
function getVideoIdFromUrl(url) {
- var match = url.match(RegExVideoId);
+ var match = url && url.match(RegExVideoId);
if (match && match[1].length === 11){
return match[1];
}
diff --git a/src/js/content-kit-compiler/renderers/html-renderer.js b/src/js/content-kit-compiler/renderers/html-renderer.js
index 6eb111643..a6991d484 100644
--- a/src/js/content-kit-compiler/renderers/html-renderer.js
+++ b/src/js/content-kit-compiler/renderers/html-renderer.js
@@ -11,7 +11,8 @@ import { mergeWithOptions } from '../../content-kit-utils/object-utils';
function HTMLRenderer(options) {
var defaults = {
blockTypes : DefaultBlockTypeSet,
- markupTypes : DefaultMarkupTypeSet
+ markupTypes : DefaultMarkupTypeSet,
+ typeRenderers : {}
};
mergeWithOptions(this, defaults, options);
}
@@ -22,12 +23,11 @@ function HTMLRenderer(options) {
* @param renderer the rendering function that returns a string of html
* Registers custom rendering hooks for a type
*/
-var renderHooks = {};
HTMLRenderer.prototype.willRenderType = function(type, renderer) {
if ('number' !== typeof type) {
type = type.id;
}
- renderHooks[type] = renderer;
+ this.typeRenderers[type] = renderer;
};
/**
@@ -57,7 +57,7 @@ HTMLRenderer.prototype.render = function(model) {
for (i = 0; i < len; i++) {
item = model[i];
renderer = this.rendererFor(item);
- renderHook = renderHooks[item.type];
+ renderHook = this.typeRenderers[item.type];
itemHtml = renderHook ? renderHook.call(renderer, item) : renderer.render(item);
if (itemHtml) { html += itemHtml; }
}
diff --git a/src/js/content-kit-compiler/types/type-set.js b/src/js/content-kit-compiler/types/type-set.js
index e31b111ad..776dccb16 100644
--- a/src/js/content-kit-compiler/types/type-set.js
+++ b/src/js/content-kit-compiler/types/type-set.js
@@ -1,3 +1,5 @@
+import Type from './type';
+
/**
* @class TypeSet
* @private
@@ -21,15 +23,17 @@ TypeSet.prototype = {
* Adds a type to the set
*/
addType: function(type) {
- this[type.name] = type;
- if (type.id === undefined) {
- type.id = this._autoId++;
- }
- this.idLookup[type.id] = type;
- if (type.tag) {
- this.tagLookup[type.tag] = type;
+ if (type instanceof Type) {
+ this[type.name] = type;
+ if (type.id === undefined) {
+ type.id = this._autoId++;
+ }
+ this.idLookup[type.id] = type;
+ if (type.tag) {
+ this.tagLookup[type.tag] = type;
+ }
+ return type;
}
- return type;
},
/**
diff --git a/src/js/content-kit-editor/commands/embed.js b/src/js/content-kit-editor/commands/embed.js
index 0ef81035d..29578c38a 100644
--- a/src/js/content-kit-editor/commands/embed.js
+++ b/src/js/content-kit-editor/commands/embed.js
@@ -6,6 +6,17 @@ import { inherit } from '../../content-kit-utils/object-utils';
import { RegEx } from '../constants';
import { OEmbedder } from '../../ext/content-kit-services';
+function loadTwitterWidgets(element) {
+ if (window.twttr) {
+ window.twttr.widgets.load(element);
+ } else {
+ var script = document.createElement('script');
+ script.async = true;
+ script.src = 'http://platform.twitter.com/widgets.js';
+ document.head.appendChild(script);
+ }
+}
+
function EmbedCommand(options) {
Command.call(this, {
name: 'embed',
@@ -23,21 +34,30 @@ inherit(EmbedCommand, Command);
EmbedCommand.prototype.exec = function(url) {
var command = this;
var editorContext = command.editorContext;
+ var embedIntent = command.embedIntent;
var index = editorContext.getCurrentBlockIndex();
- command.embedIntent.showLoading();
+ embedIntent.showLoading();
this.embedService.fetch({
url: url,
complete: function(response, error) {
- command.embedIntent.hideLoading();
-
+ embedIntent.hideLoading();
if (error) {
- new Message().show('Embed error');
+ var errorMsg = error;
+ if (error.target && error.target.status === 0) {
+ errorMsg = 'Could not connect to embed service';
+ } else if (typeof error !== 'string') {
+ errorMsg = 'Embed error';
+ }
+ new Message().show(errorMsg);
} else {
var embedModel = new EmbedModel(response);
editorContext.insertBlockAt(embedModel, index);
editorContext.syncVisualAt(index);
+ if (embedModel.attributes.provider_name.toLowerCase() === 'twitter') {
+ loadTwitterWidgets(editorContext.element);
+ }
}
}
});
diff --git a/src/js/content-kit-editor/commands/image.js b/src/js/content-kit-editor/commands/image.js
index c3977e518..2ea815d39 100644
--- a/src/js/content-kit-editor/commands/image.js
+++ b/src/js/content-kit-editor/commands/image.js
@@ -4,6 +4,17 @@ import ImageModel from '../../content-kit-compiler/models/image';
import { inherit } from '../../content-kit-utils/object-utils';
import { FileUploader } from '../../ext/content-kit-services';
+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 ImageCommand(options) {
Command.call(this, {
name: 'image',
@@ -16,19 +27,12 @@ inherit(ImageCommand, Command);
ImageCommand.prototype = {
exec: function() {
ImageCommand._super.prototype.exec.call(this);
- var clickEvent = new MouseEvent('click', { bubbles: false });
- if (!this.fileInput) {
- var command = this;
- var fileInput = this.fileInput = document.createElement('input');
- fileInput.type = 'file';
- fileInput.accept = 'image/*';
- fileInput.className = 'ck-file-input';
- fileInput.addEventListener('change', function(e) {
- command.handleFile(e);
- });
+ var fileInput = this.fileInput;
+ if (!fileInput) {
+ fileInput = this.fileInput = createFileInput(this);
document.body.appendChild(fileInput);
}
- this.fileInput.dispatchEvent(clickEvent);
+ fileInput.dispatchEvent(new MouseEvent('click', { bubbles: false }));
},
handleFile: function(e) {
var fileInput = e.target;
diff --git a/src/js/content-kit-editor/editor-html-renderer.js b/src/js/content-kit-editor/editor-html-renderer.js
new file mode 100644
index 000000000..5c7a77496
--- /dev/null
+++ b/src/js/content-kit-editor/editor-html-renderer.js
@@ -0,0 +1,41 @@
+import HTMLRenderer from '../content-kit-compiler/renderers/html-renderer';
+import Type from '../content-kit-compiler/types/type';
+import { inherit } from '../content-kit-utils/object-utils';
+
+function embedRenderer(model) {
+ var embedAttrs = model.attributes;
+ var isVideo = embedAttrs.embed_type === 'video';
+ return '' +
+ '
' +
+ (isVideo ? '' : '') + this.render(model) + (isVideo ? '
' : '') +
+ '' + embedAttrs.provider_name + ': ' +
+ '' + embedAttrs.title + ' ' +
+ ' ' +
+ ' ' +
+ '
';
+}
+
+function imageRenderer(model) {
+ return '' +
+ '' + this.render(model) + ' ' +
+ '
';
+}
+
+var typeRenderers = {};
+typeRenderers[Type.EMBED.id] = embedRenderer;
+typeRenderers[Type.IMAGE.id] = imageRenderer;
+
+/**
+ * @class EditorHTMLRenderer
+ * @constructor
+ * Subclass of HTMLRenderer specifically for the Editor
+ * Wraps interactive elements to add functionality
+ */
+function EditorHTMLRenderer() {
+ HTMLRenderer.call(this, {
+ typeRenderers: typeRenderers
+ });
+}
+inherit(EditorHTMLRenderer, HTMLRenderer);
+
+export default EditorHTMLRenderer;
diff --git a/src/js/content-kit-editor/editor.js b/src/js/content-kit-editor/editor.js
index 49772a84a..b5faf55c3 100644
--- a/src/js/content-kit-editor/editor.js
+++ b/src/js/content-kit-editor/editor.js
@@ -11,10 +11,10 @@ import TextModel from '../content-kit-compiler/models/text';
import Type from '../content-kit-compiler/types/type';
import { toArray } from '../content-kit-utils/array-utils';
import { merge } from '../content-kit-utils/object-utils';
+import EditorHTMLRenderer from './editor-html-renderer';
var editorClassName = 'ck-editor';
var editorClassNameRegExp = new RegExp(editorClassName);
-var afterRenderHooks = [];
function plainTextToBlocks(plainText, blockTag) {
var blocks = plainText.split(RegEx.NEWLINE),
@@ -104,12 +104,6 @@ function bindPasteEvents(editor) {
});
}
-function runAfterRenderHooks(element, blockModel) {
- for (var i = 0, len = afterRenderHooks.length; i < len; i++) {
- afterRenderHooks[i].call(null, element, blockModel);
- }
-}
-
/**
* @class Editor
* An individual Editor
@@ -139,7 +133,10 @@ function Editor(element, options) {
element.setAttribute('contentEditable', true);
editor.element = element;
- var compiler = editor.compiler = options.compiler || new Compiler();
+ var compiler = editor.compiler = options.compiler || new Compiler({
+ includeTypeNames: true, // output type names for easier debugging
+ renderer: new EditorHTMLRenderer()
+ });
editor.syncModel();
bindTypingEvents(editor);
@@ -187,7 +184,6 @@ Editor.prototype.syncVisualAt = function(index) {
var blockElements = toArray(this.element.children);
var element = blockElements[index];
element.innerHTML = html;
- runAfterRenderHooks(element, blockModel);
};
Editor.prototype.getCurrentBlockIndex = function() {
@@ -215,14 +211,4 @@ Editor.prototype.addTextFormat = function(opts) {
this.textFormatToolbar.addCommand(command);
};
-Editor.prototype.willRenderType = function(type, renderer) {
- this.compiler.renderer.willRenderType(type, renderer);
-};
-
-Editor.prototype.afterRender = function(callback) {
- if ('function' === typeof callback) {
- afterRenderHooks.push(callback);
- }
-};
-
export default Editor;
diff --git a/src/js/content-kit-editor/utils/selection-utils.js b/src/js/content-kit-editor/utils/selection-utils.js
index 066ec4162..7dabd0628 100644
--- a/src/js/content-kit-editor/utils/selection-utils.js
+++ b/src/js/content-kit-editor/utils/selection-utils.js
@@ -62,7 +62,7 @@ function selectionIsInElement(selection, element) {
function selectionIsEditable(selection) {
var el = getSelectionBlockElement(selection);
- return el.contentEditable !== 'false';
+ return el.isContentEditable;
}
/*
diff --git a/src/js/content-kit-editor/views/tooltip.js b/src/js/content-kit-editor/views/tooltip.js
index 392f2fb7e..f56d99a7a 100644
--- a/src/js/content-kit-editor/views/tooltip.js
+++ b/src/js/content-kit-editor/views/tooltip.js
@@ -12,7 +12,7 @@ function Tooltip(options) {
rootElement.addEventListener('mouseover', function(e) {
var target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement);
- if (target) {
+ if (target && target.isContentEditable) {
timeout = setTimeout(function() {
tooltip.showLink(target.href, target);
}, delay);