││
+││ You will never guess what happens ││
+││ when these cats take a bath ││
+││
││
+│└────────────────────────────────────────────────────────────────────────────┘│
+│┌────────────────────────────────────────────────────────────────────────────┐│
+││ ┌──────────────┐ ││
+││ │ EmbedModel │ ││
+││ └──────────────┘ ││
+││ ││
+││ ││
+│└────────────────────────────────────────────────────────────────────────────┘│
+│┌────────────────────────────────────────────────────────────────────────────┐│
+││ ┌────────────────────────┐ ││
+││ │ BlockModel (type CARD) │ ││
+││ └────────────────────────┘ ││
+││ node.innerHTML = 'Cards can change content at render time. For example, ││
+││ use an Ember component'; ││
+│└────────────────────────────────────────────────────────────────────────────┘│
+└──────────────────────────────────────────────────────────────────────────────┘
+```
+
+### Usage
+
+ContentKit is attached to a DOM node when the `Editor` factory is instantiated.
+
+```js
+var editor = new ContentKit.Editor(this.element, options);
+```
+
+`options` is an object which may include the following properties:
+
+* `stickyToolbar` - a boolean option enabling a persistent header with
+ formatting tools. Default is true for touch devices.
+* `placeholder` - default text to show before a user starts typing.
+* `spellcheck` - a boolean option enabling spellcheck. Default is true.
+* `autofocus` - a boolean option for grabbing input focus when an editor is
+ rendered.
+* `serverHost` - a URL prefix for embed and image API request. **[FIXME Remove]**
+* `cards` - an object describing available cards.
+
+### Public Editor API
+
+* `editor.loadPost(post)` - render the editor with a post. **[FIXME Implement]**
+* `editor.serializePost()` - serialize the current post for persistence. **[FIXME Implement]**
+
+### Contributing
+
+Running ContentKit tests and demo server locally requires the following dependencies:
-## Prerequisites
* [node.js](http://nodejs.org/) ([install package](http://nodejs.org/download/)) or `brew install node`
+* `gulp`, via `npm install -g gulp`
+
+To run tests:
+
+```
+gulp test
+```
+
+To run the demo server:
+
+```
+npm start && open http://localhost:5000
+```
+
+To ensure ContentKit rebuilds while you work with it:
+
+```
+gulp watch
+```
+
+For uploads and embeds to work, you will have to configure AWS and
+Embedly keys as environment variables:
-## Server Configuration (optional)
-For uploads and embeds to work, you will have to configure AWS and Embedly keys as environment variables:
```bash
export AWS_ACCESS_KEY_ID=XXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXX
export EMBEDLY_KEY=XXXXXX
```
-Also, set the `bucketName` in `server/config.json` with the name of your AWS S3 bucket for uploading files.
-
-## Playing
-1. Install dependencies: `npm install`
-2. Build: `gulp build`
-3. Start server: `npm start` then visit [http://localhost:5000](http://localhost:5000) || To play without the server, simply open [demo/index.html](demo/index.html)
-
-## Testing
-`gulp test`
-## Dev tips
-`gulp watch` to auto build/test as you save files
+Also, set the `bucketName` in `server/config.json` with the name of your AWS
+S3 bucket for uploading files.
From 4d6e05f1778c9b780c57ba7338ddaad92d17b2e6 Mon Sep 17 00:00:00 2001
From: Matthew Beale
Date: Thu, 25 Jun 2015 15:34:48 -0400
Subject: [PATCH 05/77] Build dist
---
dist/content-kit-editor.js | 111 ++++++++++++++++++++++++++++++-------
1 file changed, 90 insertions(+), 21 deletions(-)
diff --git a/dist/content-kit-editor.js b/dist/content-kit-editor.js
index 4c74bbb78..451dadb96 100644
--- a/dist/content-kit-editor.js
+++ b/dist/content-kit-editor.js
@@ -308,7 +308,8 @@
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 })
+ new types_type({ name: 'embed', isTextType: false }),
+ new types_type({ name: 'card' })
]);
/**
@@ -666,11 +667,32 @@
var html_embed_renderer = HTMLEmbedRenderer;
+ /**
+ * @class CardRenderer
+ * @constructor
+ */
+ function CardRenderer(cards) {
+ this.cards = cards;
+ }
+
+ /**
+ * @method render
+ * @param model a card model
+ * @return String html
+ */
+ CardRenderer.prototype.render = function(model) {
+ var render = this.cards[model.attributes.name];
+ 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(options) {
- var rendererOptions = {
- typeRenderers: typeRenderers
- };
- merge(rendererOptions, options);
- html_renderer.call(this, rendererOptions);
- }
- 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 {
- command.exec();
- toolbar.updateForSelection();
- }
- e.stopPropagation();
- });
- }
-
- 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;
- }
- }
- };
-
- 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 {
- toolbar.show();
- toolbar.updateForSelection(selection);
- }
- }
- }
-
- function TextFormatToolbar(options) {
- var toolbar = this;
- views_toolbar.call(this, options);
- toolbar.rootElement = options.rootElement;
- toolbar.rootElement.addEventListener('keyup', function() { handleTextSelection(toolbar); });
-
- document.addEventListener('mouseup', function() {
- setTimeout(function() {
- handleTextSelection(toolbar);
- });
- });
-
- 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();
- }
- }
-
- rootElement.addEventListener('keyup', embedIntentHandler);
- document.addEventListener('mouseup', function() {
- setTimeout(function() { embedIntentHandler(); });
- });
-
- document.addEventListener('keyup', function(e) {
- if (e.keyCode === Keycodes.ESC) {
- embedIntent.hide();
- }
- });
-
- window.addEventListener('resize', function() {
- if(embedIntent.isShowing) {
- embedIntent.reposition();
- }
- });
- }
- inherit(EmbedIntent, views_view);
-
- EmbedIntent.prototype.hide = function() {
- if (EmbedIntent._super.prototype.hide.call(this)) {
- this.deactivate();
- }
- };
-
- 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();
-
- embedIntent.showLoading();
- 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().showError(error.message || 'Error uploading image');
- }
- injectImageBlock(response.url, editor, currentEditingIndex);
- }
- });
- 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 oembed = OEmbedCommand;
-
- function injectCardBlock(cardName, cardPayload, editor, index) {
- // FIXME: Do we change the block model internal representation here?
- var cardBlock = models_block.createWithType(types_type.CARD, {
- attributes: {
- name: cardName,
- payload: cardPayload
- }
- });
- editor.replaceBlock(cardBlock, index);
- }
-
- function CardCommand() {
- base.call(this, {
- name: 'card',
- button: 'CA'
- });
- }
- inherit(CardCommand, base);
-
- CardCommand.prototype = {
- exec: function() {
- CardCommand._super.prototype.exec.call(this);
- var editor = this.editorContext;
- var currentEditingIndex = editor.getCurrentBlockIndex();
-
- var cardName = 'pick-color';
- var cardPayload = { options: ['red', 'blue'] };
- injectCardBlock(cardName, cardPayload, editor, currentEditingIndex);
- editor.renderBlockAt(currentEditingIndex, true);
- }
- };
-
- var card = CardCommand;
-
- // 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));
- }
- }
- }
- };
-
- 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' }),
- new card()
- ],
- autoTypingCommands: [
- new unordered_list(),
- new ordered_list()
- ],
- compiler: null,
- cards: {}
- };
-
- 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 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 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 applyPlaceholder(editorElement, placeholder) {
- var dataset = editorElement.dataset;
- if (placeholder && !dataset.placeholder) {
- dataset.placeholder = placeholder;
- }
- }
-
- 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;
- }
-
- /**
- * @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);
- if (!editor.compiler) {
- editor.compiler = new compiler({
- includeTypeNames: true, // outputs models with type names, i.e. 'BOLD', for easier debugging
- renderer: new editor_html_renderer({
- cards: editor.cards
- }) // subclassed HTML renderer that adds dom structure for additional editor interactivity
- });
- }
-
- if (element) {
- applyClassName(element);
- 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();
- }
-
- 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;
- newEl.dataset.modelIndex = index;
- 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, block, blockEl;
- for (i = 0; i < len; i++) {
- blockEl = blockElements[i];
- if(blockEl.isContentEditable) {
- updatedModel.push(this.compiler.parser.serializeBlockNode(blockEl));
- } else {
- if (blockEl.dataset.modelIndex) {
- block = this.model[blockEl.dataset.modelIndex];
- updatedModel.push(block);
- } else {
- updatedModel.push(nonTextBlocks.shift());
- }
- }
- }
- this.model = updatedModel;
- this.trigger('update');
- };
-
-
- var editor_editor = Editor;
-
- function EditorFactory(element, options) {
- var editors = [];
- var elements, elementsLen, i;
-
- 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 (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];
- }
-
- EditorFactory.prototype = editor_editor.prototype;
-
- var editor_factory = EditorFactory;
-
- 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/package.json b/package.json
index 876066e13..608fef4b3 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,9 @@
},
"scripts": {
"start": "node server/index.js",
- "test": "gulp test"
+ "test": "gulp test",
+ "build": "rm -rf dist && broccoli build dist",
+ "prepublish": "npm run build"
},
"keywords": [
"html",
@@ -27,6 +29,9 @@
"express": "^4.11.1"
},
"devDependencies": {
+ "broccoli": "^0.16.3",
+ "broccoli-merge-trees": "^0.2.1",
+ "broccoli-multi-builder": "^0.1.0",
"content-kit-compiler": "0.2.3",
"content-kit-utils": "0.1.2",
"del": "^1.1.1",
diff --git a/src/js/commands/bold.js b/src/js/commands/bold.js
index 6f74bc0f9..50e25939a 100644
--- a/src/js/commands/bold.js
+++ b/src/js/commands/bold.js
@@ -1,7 +1,7 @@
import TextFormatCommand from './text-format';
import { getSelectionBlockTagName } from '../utils/selection-utils';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
var RegExpHeadingTag = /^(h1|h2|h3|h4|h5|h6)$/i;
diff --git a/src/js/commands/card.js b/src/js/commands/card.js
index 1a94b9f9c..f5f0455cf 100644
--- a/src/js/commands/card.js
+++ b/src/js/commands/card.js
@@ -1,7 +1,9 @@
import Command from './base';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import BlockModel from 'node_modules/content-kit-compiler/src/models/block';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import {
+ BlockModel,
+ Type
+} from 'content-kit-compiler';
function injectCardBlock(cardName, cardPayload, editor, index) {
// FIXME: Do we change the block model internal representation here?
diff --git a/src/js/commands/format-block.js b/src/js/commands/format-block.js
index 81e2a059f..60a329ab2 100644
--- a/src/js/commands/format-block.js
+++ b/src/js/commands/format-block.js
@@ -1,7 +1,8 @@
import TextFormatCommand from './text-format';
import { getSelectionBlockElement, selectNode } from '../utils/selection-utils';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
+import { doc } from 'content-kit-compiler';
function FormatBlockCommand(options) {
options = options || {};
@@ -22,7 +23,7 @@ FormatBlockCommand.prototype.exec = function() {
// 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);
+ var flatNode = doc.createTextNode(blockElement.textContent);
blockElement.parentNode.insertBefore(flatNode, blockElement);
blockElement.parentNode.removeChild(blockElement);
selectNode(flatNode);
diff --git a/src/js/commands/heading.js b/src/js/commands/heading.js
index fa1cbf936..c28816adb 100644
--- a/src/js/commands/heading.js
+++ b/src/js/commands/heading.js
@@ -1,6 +1,6 @@
import FormatBlockCommand from './format-block';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
function HeadingCommand() {
FormatBlockCommand.call(this, {
diff --git a/src/js/commands/image.js b/src/js/commands/image.js
index 9db3235e2..fb07e1490 100644
--- a/src/js/commands/image.js
+++ b/src/js/commands/image.js
@@ -1,12 +1,16 @@
import Command from './base';
import Message from '../views/message';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
-import BlockModel from 'node_modules/content-kit-compiler/src/models/block';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import {
+ Type,
+ BlockModel,
+ doc,
+} from 'content-kit-compiler';
+import win from '../utils/win';
+import { inherit } from 'content-kit-utils';
import { FileUploader } from '../utils/http-utils';
function createFileInput(command) {
- var fileInput = document.createElement('input');
+ var fileInput = doc.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
fileInput.className = 'ck-file-input';
@@ -22,7 +26,7 @@ function injectImageBlock(src, editor, index) {
}
function renderFromFile(file, editor, index) {
- if (file && window.FileReader) {
+ if (file && win.FileReader) {
var reader = new FileReader();
reader.onload = function(e) {
var base64Src = e.target.result;
@@ -48,7 +52,7 @@ ImageCommand.prototype = {
var fileInput = this.fileInput;
if (!fileInput) {
fileInput = this.fileInput = createFileInput(this);
- document.body.appendChild(fileInput);
+ doc.body.appendChild(fileInput);
}
fileInput.dispatchEvent(new MouseEvent('click', { bubbles: false }));
},
diff --git a/src/js/commands/italic.js b/src/js/commands/italic.js
index a14eb0731..5380d09d0 100644
--- a/src/js/commands/italic.js
+++ b/src/js/commands/italic.js
@@ -1,6 +1,6 @@
import TextFormatCommand from './text-format';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type }from 'content-kit-compiler';
function ItalicCommand() {
TextFormatCommand.call(this, {
diff --git a/src/js/commands/link.js b/src/js/commands/link.js
index de36b68de..9b79d5bc9 100644
--- a/src/js/commands/link.js
+++ b/src/js/commands/link.js
@@ -1,8 +1,8 @@
import TextFormatCommand from './text-format';
import Prompt from '../views/prompt';
import { getSelectionTagName } from '../utils/selection-utils';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
var RegExpHttp = /^https?:\/\//i;
diff --git a/src/js/commands/list.js b/src/js/commands/list.js
index 8474a1c49..a3f94ba5c 100644
--- a/src/js/commands/list.js
+++ b/src/js/commands/list.js
@@ -1,7 +1,8 @@
import TextFormatCommand from './text-format';
import { getSelectionBlockElement, selectNode, getSelectionTagName } from '../utils/selection-utils';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
+import win from '../utils/win';
function ListCommand(options) {
TextFormatCommand.call(this, options);
@@ -31,7 +32,7 @@ ListCommand.prototype.checkAutoFormat = function(node) {
text = node.textContent;
if (Type.LIST_ITEM.tag !== getSelectionTagName() && regex.test(text)) {
this.exec();
- window.getSelection().anchorNode.textContent = text.replace(regex, '');
+ win.getSelection().anchorNode.textContent = text.replace(regex, '');
return true;
}
}
diff --git a/src/js/commands/oembed.js b/src/js/commands/oembed.js
index 7e2cd978d..89d84cbf8 100644
--- a/src/js/commands/oembed.js
+++ b/src/js/commands/oembed.js
@@ -1,18 +1,22 @@
import Command from './base';
import Prompt from '../views/prompt';
import Message from '../views/message';
-import EmbedModel from 'node_modules/content-kit-compiler/src/models/embed';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import {
+ EmbedModel,
+ doc
+} from 'content-kit-compiler';
+import { inherit } from 'content-kit-utils';
import { OEmbedder } from '../utils/http-utils';
+import win from '../utils/win';
function loadTwitterWidgets(element) {
- if (window.twttr) {
- window.twttr.widgets.load(element);
+ if (win.twttr) {
+ win.twttr.widgets.load(element);
} else {
- var script = document.createElement('script');
+ var script = doc.createElement('script');
script.async = true;
script.src = 'http://platform.twitter.com/widgets.js';
- document.head.appendChild(script);
+ doc.head.appendChild(script);
}
}
diff --git a/src/js/commands/ordered-list.js b/src/js/commands/ordered-list.js
index 49395415c..c503aa140 100644
--- a/src/js/commands/ordered-list.js
+++ b/src/js/commands/ordered-list.js
@@ -1,6 +1,6 @@
import ListCommand from './list';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
function OrderedListCommand() {
ListCommand.call(this, {
diff --git a/src/js/commands/quote.js b/src/js/commands/quote.js
index 9b1a41bd3..a5917f03f 100644
--- a/src/js/commands/quote.js
+++ b/src/js/commands/quote.js
@@ -1,6 +1,6 @@
import FormatBlockCommand from './format-block';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
function QuoteCommand() {
FormatBlockCommand.call(this, {
diff --git a/src/js/commands/subheading.js b/src/js/commands/subheading.js
index a3b8ba58c..645db6969 100644
--- a/src/js/commands/subheading.js
+++ b/src/js/commands/subheading.js
@@ -1,6 +1,6 @@
import FormatBlockCommand from './format-block';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
function SubheadingCommand() {
FormatBlockCommand.call(this, {
diff --git a/src/js/commands/text-format.js b/src/js/commands/text-format.js
index ed03cf597..3a47e2157 100644
--- a/src/js/commands/text-format.js
+++ b/src/js/commands/text-format.js
@@ -1,5 +1,6 @@
import Command from './base';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
+import { doc } from 'content-kit-compiler';
function TextFormatCommand(options) {
options = options || {};
@@ -14,10 +15,10 @@ inherit(TextFormatCommand, Command);
TextFormatCommand.prototype = {
exec: function(value) {
- document.execCommand(this.action, false, value || null);
+ doc.execCommand(this.action, false, value || null);
},
unexec: function(value) {
- document.execCommand(this.removeAction, false, value || null);
+ doc.execCommand(this.removeAction, false, value || null);
}
};
diff --git a/src/js/commands/unordered-list.js b/src/js/commands/unordered-list.js
index ca4e2d800..8fe63c1bf 100644
--- a/src/js/commands/unordered-list.js
+++ b/src/js/commands/unordered-list.js
@@ -1,6 +1,6 @@
import ListCommand from './list';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import { inherit } from 'content-kit-utils';
+import { Type } from 'content-kit-compiler';
function UnorderedListCommand() {
ListCommand.call(this, {
diff --git a/src/js/editor/editor-factory.js b/src/js/editor/editor-factory.js
index 6cf2637b0..5970ede41 100644
--- a/src/js/editor/editor-factory.js
+++ b/src/js/editor/editor-factory.js
@@ -1,4 +1,5 @@
import Editor from './editor';
+import { doc } from 'content-kit-compiler';
/**
* @class EditorFactory
@@ -16,7 +17,7 @@ function EditorFactory(element, options) {
}
if (typeof element === 'string') {
- elements = document.querySelectorAll(element);
+ elements = doc.querySelectorAll(element);
} else if (element && element.length) {
elements = element;
} else if (element) {
diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js
index a3c03f2ca..d3ffc9800 100644
--- a/src/js/editor/editor.js
+++ b/src/js/editor/editor.js
@@ -16,10 +16,13 @@ import CardCommand from '../commands/card';
import Keycodes from '../utils/keycodes';
import { getSelectionBlockElement, getCursorOffsetInElement } from '../utils/selection-utils';
import EventEmitter from '../utils/event-emitter';
-import Compiler from 'node_modules/content-kit-compiler/src/compiler';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
-import { toArray } from 'node_modules/content-kit-utils/src/array-utils';
-import { merge, mergeWithOptions } from 'node_modules/content-kit-utils/src/object-utils';
+import {
+ Type,
+ Compiler,
+ doc
+} from 'content-kit-compiler';
+import { toArray, merge, mergeWithOptions } from 'content-kit-utils';
+import win from '../utils/win';
var defaults = {
placeholder: 'Write here...',
@@ -27,7 +30,7 @@ var defaults = {
autofocus: true,
model: null,
serverHost: '',
- stickyToolbar: !!('ontouchstart' in window),
+ stickyToolbar: !!('ontouchstart' in win),
textFormatCommands: [
new BoldCommand(),
new ItalicCommand(),
@@ -58,7 +61,7 @@ function bindContentEditableTypingListeners(editor) {
if (!getSelectionBlockElement() ||
!editor.element.textContent ||
(!e.shiftKey && e.which === Keycodes.ENTER) || (e.ctrlKey && e.which === Keycodes.M)) {
- document.execCommand('formatBlock', false, Type.PARAGRAPH.tag);
+ doc.execCommand('formatBlock', false, Type.PARAGRAPH.tag);
} //else if (e.which === Keycodes.BKSP) {
// TODO: Need to rerender when backspacing 2 blocks together
//var cursorIndex = editor.getCursorIndexInCurrentBlock();
@@ -74,7 +77,7 @@ function bindContentEditableTypingListeners(editor) {
var pastedHTML = data && data.getData && data.getData('text/html');
var sanitizedHTML = pastedHTML && editor.compiler.rerender(pastedHTML);
if (sanitizedHTML) {
- document.execCommand('insertHTML', false, sanitizedHTML);
+ doc.execCommand('insertHTML', false, sanitizedHTML);
editor.syncVisual();
}
e.preventDefault();
@@ -96,7 +99,7 @@ function bindAutoTypingListeners(editor) {
var selection, i;
if (count) {
- selection = window.getSelection();
+ selection = win.getSelection();
for (i = 0; i < count; i++) {
if (commands[i].checkAutoFormat(selection.anchorNode)) {
e.stopPropagation();
@@ -109,10 +112,10 @@ function bindAutoTypingListeners(editor) {
function bindDragAndDrop() {
// TODO. For now, just prevent redirect when dropping something on the page
- window.addEventListener('dragover', function(e) {
+ win.addEventListener('dragover', function(e) {
e.preventDefault(); // prevents showing cursor where to drop
});
- window.addEventListener('drop', function(e) {
+ win.addEventListener('drop', function(e) {
e.preventDefault(); // prevent page from redirecting
});
}
@@ -259,7 +262,7 @@ Editor.prototype.replaceBlock = function(block, index) {
Editor.prototype.renderBlockAt = function(index, replace) {
var modelAtIndex = this.model[index];
var html = this.compiler.render([modelAtIndex]);
- var dom = document.createElement('div');
+ var dom = doc.createElement('div');
dom.innerHTML = html;
var newEl = dom.firstChild;
newEl.dataset.modelIndex = index;
diff --git a/src/js/index.js b/src/js/index.js
index bc7779493..7529959c0 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -1,9 +1,12 @@
-import Type from 'node_modules/content-kit-compiler/src/types/type';
-import BlockModel from 'node_modules/content-kit-compiler/src/models/block';
-import EmbedModel from 'node_modules/content-kit-compiler/src/models/embed';
-import Compiler from 'node_modules/content-kit-compiler/src/compiler';
-import HTMLParser from 'node_modules/content-kit-compiler/src/parsers/html-parser';
-import HTMLRenderer from 'node_modules/content-kit-compiler/src/renderers/html-renderer';
+import {
+ Type,
+ BlockModel,
+ EmbedModel,
+ Compiler,
+ HTMLParser,
+ HTMLRenderer
+} from 'content-kit-compiler';
+
import EditorFactory from './editor/editor-factory';
// Create a namespace and selectivly expose public modules
@@ -16,4 +19,8 @@ ContentKit.HTMLParser = HTMLParser;
ContentKit.HTMLRenderer = HTMLRenderer;
ContentKit.Editor = EditorFactory;
-window.ContentKit = ContentKit;
+export function registerGlobal(global) {
+ global.ContentKit = ContentKit;
+}
+
+export default ContentKit;
diff --git a/src/js/renderers/editor-html-renderer.js b/src/js/renderers/editor-html-renderer.js
index beeaf4cdb..1b597f4dd 100644
--- a/src/js/renderers/editor-html-renderer.js
+++ b/src/js/renderers/editor-html-renderer.js
@@ -1,11 +1,13 @@
-import HTMLRenderer from 'node_modules/content-kit-compiler/src/renderers/html-renderer';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { HTMLRenderer } from 'content-kit-compiler';
+import { Type } from 'content-kit-compiler';
+import {
+ inherit,
+ merge
+} from 'content-kit-utils';
import YouTubeRenderer from './youtube';
import TwitterRenderer from './twitter';
import InstagramRenderer from './instagram';
import LinkImageRenderer from './link-image-renderer';
-import { merge } from 'node_modules/content-kit-utils/src/object-utils';
/**
* A dictionary of supported embeds types that we'll custom render
diff --git a/src/js/utils/element-utils.js b/src/js/utils/element-utils.js
index 15ce55dff..906047e29 100644
--- a/src/js/utils/element-utils.js
+++ b/src/js/utils/element-utils.js
@@ -1,5 +1,8 @@
+import { doc } from 'content-kit-compiler';
+import win from '../utils/win';
+
function createDiv(className) {
- var div = document.createElement('div');
+ var div = doc.createElement('div');
if (className) {
div.className = className;
}
@@ -49,9 +52,9 @@ function elementContentIsEmpty(element) {
}
function getElementRelativeOffset(element) {
- var offset = { left: 0, top: -window.pageYOffset };
+ var offset = { left: 0, top: -win.pageYOffset };
var offsetParent = element.offsetParent;
- var offsetParentPosition = window.getComputedStyle(offsetParent).position;
+ var offsetParentPosition = win.getComputedStyle(offsetParent).position;
var offsetParentRect;
if (offsetParentPosition === 'relative') {
@@ -63,7 +66,7 @@ function getElementRelativeOffset(element) {
}
function getElementComputedStyleNumericProp(element, prop) {
- return parseFloat(window.getComputedStyle(element)[prop]);
+ return parseFloat(win.getComputedStyle(element)[prop]);
}
function positionElementToRect(element, rect, topOffset, leftOffset) {
diff --git a/src/js/utils/http-utils.js b/src/js/utils/http-utils.js
index 2df74388d..868938214 100644
--- a/src/js/utils/http-utils.js
+++ b/src/js/utils/http-utils.js
@@ -1,3 +1,4 @@
+import win from '../utils/win';
function createXHR(options) {
var xhr = new XMLHttpRequest();
@@ -65,7 +66,7 @@ FileUploader.prototype.upload = function(options) {
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 (!file || !(file instanceof win.File)) { return; }
if (maxFileSize && file.size > maxFileSize) {
if (callback) { callback.call(this, null, { message: 'max file size is ' + maxFileSize + ' bytes' }); }
diff --git a/src/js/utils/selection-utils.js b/src/js/utils/selection-utils.js
index bc498fa8f..4cd642827 100644
--- a/src/js/utils/selection-utils.js
+++ b/src/js/utils/selection-utils.js
@@ -1,5 +1,8 @@
import { nodeIsDescendantOfElement } from './element-utils';
-import Type from 'node_modules/content-kit-compiler/src/types/type';
+import {
+ Type,
+ doc
+} from 'content-kit-compiler';
// TODO: remove, pass in Editor's current block set
var RootTags = [
@@ -29,13 +32,13 @@ function getDirectionOfSelection(selection) {
}
function getSelectionElement(selection) {
- selection = selection || window.getSelection();
+ selection = selection || win.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();
+ selection = selection || win.getSelection();
var element = getSelectionElement();
var tag = element && element.tagName.toLowerCase();
while (tag && RootTags.indexOf(tag) === -1) {
@@ -82,14 +85,14 @@ function selectionIsEditable(selection) {
}
function restoreRange(range) {
- var selection = window.getSelection();
+ var selection = win.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
function selectNode(node) {
- var range = document.createRange();
- var selection = window.getSelection();
+ var range = doc.createRange();
+ var selection = win.getSelection();
range.setStart(node, 0);
range.setEnd(node, node.length);
selection.removeAllRanges();
@@ -97,8 +100,8 @@ function selectNode(node) {
}
function setCursorIndexInElement(element, index) {
- var range = document.createRange();
- var selection = window.getSelection();
+ var range = doc.createRange();
+ var selection = win.getSelection();
range.setStart(element, index);
range.collapse(true);
selection.removeAllRanges();
@@ -112,7 +115,7 @@ function setCursorToStartOfElement(element) {
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();
+ var selection = win.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
var preCaretRange = range.cloneRange();
diff --git a/src/js/utils/win.js b/src/js/utils/win.js
new file mode 100644
index 000000000..3b9e454de
--- /dev/null
+++ b/src/js/utils/win.js
@@ -0,0 +1,11 @@
+import { doc } from 'content-kit-compiler';
+
+var win;
+if (typeof window !== 'undefined') {
+ win = window;
+} else {
+ // jsdom provides a defaultView
+ win = doc.defaultView;
+}
+
+export default win;
diff --git a/src/js/views/embed-intent.js b/src/js/views/embed-intent.js
index e22068c83..d9411fb70 100644
--- a/src/js/views/embed-intent.js
+++ b/src/js/views/embed-intent.js
@@ -1,10 +1,12 @@
import View from './view';
import Toolbar from './toolbar';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
import { getSelectionBlockElement } from '../utils/selection-utils';
import { elementContentIsEmpty, positionElementToLeftOf, positionElementCenteredIn } from '../utils/element-utils';
import { createDiv } from '../utils/element-utils';
import Keycodes from '../utils/keycodes';
+import { doc } from 'content-kit-compiler';
+import win from '../utils/win';
var LayoutStyle = {
GUTTER : 1,
@@ -27,7 +29,7 @@ function EmbedIntent(options) {
embedIntent.isActive = false;
embedIntent.editorContext = options.editorContext;
embedIntent.loadingIndicator = createDiv('ck-embed-loading');
- embedIntent.button = document.createElement('button');
+ embedIntent.button = doc.createElement('button');
embedIntent.button.className = 'ck-embed-intent-btn';
embedIntent.button.title = 'Insert image or embed...';
embedIntent.element.appendChild(embedIntent.button);
@@ -58,17 +60,17 @@ function EmbedIntent(options) {
}
rootElement.addEventListener('keyup', embedIntentHandler);
- document.addEventListener('mouseup', function() {
+ doc.addEventListener('mouseup', function() {
setTimeout(function() { embedIntentHandler(); });
});
- document.addEventListener('keyup', function(e) {
+ doc.addEventListener('keyup', function(e) {
if (e.keyCode === Keycodes.ESC) {
embedIntent.hide();
}
});
- window.addEventListener('resize', function() {
+ win.addEventListener('resize', function() {
if(embedIntent.isShowing) {
embedIntent.reposition();
}
diff --git a/src/js/views/message.js b/src/js/views/message.js
index 59d72a60f..37844b037 100644
--- a/src/js/views/message.js
+++ b/src/js/views/message.js
@@ -1,5 +1,5 @@
import View from './view';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
var defaultClassNames = ['ck-message'];
diff --git a/src/js/views/prompt.js b/src/js/views/prompt.js
index ed631cdb2..6626edc26 100644
--- a/src/js/views/prompt.js
+++ b/src/js/views/prompt.js
@@ -1,10 +1,12 @@
import View from './view';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
import { restoreRange } from '../utils/selection-utils';
import { createDiv, positionElementToRect } from '../utils/element-utils';
import Keycodes from '../utils/keycodes';
+import { doc } from 'content-kit-compiler';
+import win from '../utils/win';
-var container = document.body;
+var container = doc.body;
var hiliter = createDiv('ck-editor-hilite');
function positionHiliteRange(range) {
@@ -32,7 +34,7 @@ function Prompt(options) {
}
});
- window.addEventListener('resize', function() {
+ win.addEventListener('resize', function() {
var activeHilite = hiliter.parentNode;
var range = prompt.range;
if(activeHilite && range) {
@@ -45,7 +47,7 @@ inherit(Prompt, View);
Prompt.prototype.show = function(callback) {
var prompt = this;
var element = prompt.element;
- var selection = window.getSelection();
+ var selection = win.getSelection();
var range = selection && selection.rangeCount && selection.getRangeAt(0);
element.value = null;
prompt.range = range || null;
diff --git a/src/js/views/text-format-toolbar.js b/src/js/views/text-format-toolbar.js
index cd04aba69..fa61ca0bc 100644
--- a/src/js/views/text-format-toolbar.js
+++ b/src/js/views/text-format-toolbar.js
@@ -1,14 +1,16 @@
import Toolbar from './toolbar';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
import { selectionIsEditable, selectionIsInElement } from '../utils/selection-utils';
import Keycodes from '../utils/keycodes';
+import { doc } from 'content-kit-compiler';
+import win from '../utils/win';
function selectionIsEditableByToolbar(selection, toolbar) {
return selectionIsEditable(selection) && selectionIsInElement(selection, toolbar.rootElement);
}
function handleTextSelection(toolbar) {
- var selection = window.getSelection();
+ var selection = win.getSelection();
if (toolbar.sticky) {
toolbar.updateForSelection(selectionIsEditableByToolbar(selection, toolbar) ? selection : null);
} else {
@@ -27,13 +29,13 @@ function TextFormatToolbar(options) {
toolbar.rootElement = options.rootElement;
toolbar.rootElement.addEventListener('keyup', function() { handleTextSelection(toolbar); });
- document.addEventListener('mouseup', function() {
+ doc.addEventListener('mouseup', function() {
setTimeout(function() {
handleTextSelection(toolbar);
});
});
- document.addEventListener('keyup', function(e) {
+ doc.addEventListener('keyup', function(e) {
var key = e.keyCode;
if (key === 116) { //F5
toolbar.toggleSticky();
@@ -43,10 +45,10 @@ function TextFormatToolbar(options) {
}
});
- window.addEventListener('resize', function() {
+ win.addEventListener('resize', function() {
if(!toolbar.sticky && toolbar.isShowing) {
var activePromptRange = toolbar.activePrompt && toolbar.activePrompt.range;
- toolbar.positionToContent(activePromptRange ? activePromptRange : window.getSelection().getRangeAt(0));
+ toolbar.positionToContent(activePromptRange ? activePromptRange : win.getSelection().getRangeAt(0));
}
});
}
diff --git a/src/js/views/toolbar-button.js b/src/js/views/toolbar-button.js
index d2edef4a9..856c1d6d5 100644
--- a/src/js/views/toolbar-button.js
+++ b/src/js/views/toolbar-button.js
@@ -1,3 +1,4 @@
+import { doc } from 'content-kit-compiler';
var buttonClassName = 'ck-toolbar-btn';
@@ -6,7 +7,7 @@ function ToolbarButton(options) {
var toolbar = options.toolbar;
var command = options.command;
var prompt = command.prompt;
- var element = document.createElement('button');
+ var element = doc.createElement('button');
button.element = element;
button.command = command;
diff --git a/src/js/views/toolbar.js b/src/js/views/toolbar.js
index d63344c2e..3d89f96ee 100644
--- a/src/js/views/toolbar.js
+++ b/src/js/views/toolbar.js
@@ -1,8 +1,9 @@
import View from './view';
import ToolbarButton from './toolbar-button';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
import { tagsInSelection } from '../utils/selection-utils';
import { createDiv, swapElements, positionElementToRightOf, positionElementCenteredAbove } from '../utils/element-utils';
+import { doc } from 'content-kit-compiler';
var ToolbarDirection = {
TOP : 1,
@@ -57,7 +58,7 @@ function Toolbar(options) {
}
// Closes prompt if displayed when changing selection
- document.addEventListener('mouseup', function() {
+ doc.addEventListener('mouseup', function() {
toolbar.dismissPrompt();
});
}
@@ -103,7 +104,7 @@ Toolbar.prototype.dismissPrompt = function() {
Toolbar.prototype.updateForSelection = function(selection) {
var toolbar = this;
- selection = selection || window.getSelection();
+ selection = selection || win.getSelection();
if (toolbar.sticky) {
updateButtonsForSelection(toolbar.buttons, selection);
} else if (!selection.isCollapsed) {
@@ -123,7 +124,7 @@ Toolbar.prototype.positionToContent = function(content) {
positioningMethod = positionElementCenteredAbove;
}
position = positioningMethod(this.element, content);
- sideEdgeOffset = Math.min(Math.max(10, position.left), document.body.clientWidth - this.element.offsetWidth - 10);
+ sideEdgeOffset = Math.min(Math.max(10, position.left), doc.body.clientWidth - this.element.offsetWidth - 10);
this.contentElement.style.transform = 'translateX(' + (sideEdgeOffset - position.left) + 'px)';
};
diff --git a/src/js/views/tooltip.js b/src/js/views/tooltip.js
index b4f8d4309..0d5d05e09 100644
--- a/src/js/views/tooltip.js
+++ b/src/js/views/tooltip.js
@@ -1,5 +1,5 @@
import View from './view';
-import { inherit } from 'node_modules/content-kit-utils/src/object-utils';
+import { inherit } from 'content-kit-utils';
import { positionElementCenteredBelow, getEventTargetMatchingTag } from '../utils/element-utils';
function Tooltip(options) {
diff --git a/src/js/views/view.js b/src/js/views/view.js
index 09970877e..eec6bb307 100644
--- a/src/js/views/view.js
+++ b/src/js/views/view.js
@@ -1,3 +1,5 @@
+import { doc } from 'content-kit-compiler';
+
function renderClasses(view) {
var classNames = view.classNames;
if (classNames && classNames.length) {
@@ -11,8 +13,8 @@ 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.element = doc.createElement(this.tagName);
+ this.container = options.container || doc.body;
this.isShowing = false;
renderClasses(this);
}
From 532c974d3cde212e95178e30b89b201b6b6648cb Mon Sep 17 00:00:00 2001
From: Cory Forsyth
Date: Tue, 30 Jun 2015 14:27:47 -0400
Subject: [PATCH 07/77] use testem for tests
---
.travis.yml | 25 +++++++++++--
Brocfile.js | 8 ++++-
README.md | 17 ++++++---
bower.json | 2 +-
broccoli/test-tree-builder.js | 58 ++++++++++++++++++++++++++++++
package.json | 8 +++--
src/js/editor/editor-factory.js | 3 +-
src/js/index.js | 21 +++++------
testem.json | 16 +++++++++
tests/{tests.js => editor-test.js} | 26 ++++++++------
tests/index.html | 18 ++++++----
11 files changed, 162 insertions(+), 40 deletions(-)
create mode 100644 broccoli/test-tree-builder.js
create mode 100644 testem.json
rename tests/{tests.js => editor-test.js} (73%)
diff --git a/.travis.yml b/.travis.yml
index 6b7f05d7b..cc63c5c69 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,24 @@
+---
language: node_js
node_js:
- - "0.10"
-notifications:
- email: false
\ No newline at end of file
+ - "0.12"
+
+sudo: false
+
+cache:
+ directories:
+ - node_modules
+
+before_install:
+ - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
+ - "npm config set spin false"
+ - "npm install -g npm@^2"
+
+install:
+ - npm install -g broccoli-cli
+ - npm install -g bower
+ - bower install
+ - npm install
+
+script:
+ - npm test
diff --git a/Brocfile.js b/Brocfile.js
index 4aaeaa955..64270fd00 100644
--- a/Brocfile.js
+++ b/Brocfile.js
@@ -2,6 +2,7 @@
var multiBuilder = require('broccoli-multi-builder');
var mergeTrees = require('broccoli-merge-trees');
+var testTreeBuilder = require('./broccoli/test-tree-builder');
var jsSrc = './src/js';
var vendoredModules = ['content-kit-compiler', 'content-kit-utils'];
@@ -27,4 +28,9 @@ var cjsTree = multiBuilder.buildCJS({
packageName: packageName
});
-module.exports = mergeTrees([ amdTree, globalTree, cjsTree ]);
+module.exports = mergeTrees([
+ amdTree,
+ globalTree,
+ cjsTree,
+ testTreeBuilder.build()
+]);
diff --git a/README.md b/README.md
index c053fd950..3925ea36e 100644
--- a/README.md
+++ b/README.md
@@ -77,11 +77,20 @@ Running ContentKit tests and demo server locally requires the following dependen
* [node.js](http://nodejs.org/) ([install package](http://nodejs.org/download/)) or `brew install node`
* `gulp`, via `npm install -g gulp`
-To run tests:
+### Tests
-```
-gulp test
-```
+Install npm and bower:
+
+ * `bower i i`
+ * `npm i`
+
+Run tests via the built-in broccoli server:
+
+ * `npm run serve && open http://localhost:4200/tests`
+
+Or run tests via testem:
+
+ * `npm test`
To run the demo server:
diff --git a/bower.json b/bower.json
index 26e506cf4..26f54cbbc 100644
--- a/bower.json
+++ b/bower.json
@@ -16,6 +16,6 @@
"tests"
],
"dependencies": {
- "loader.js": "~3.2.1"
+ "ember-cli-test-loader": "~0.1.3"
}
}
diff --git a/broccoli/test-tree-builder.js b/broccoli/test-tree-builder.js
new file mode 100644
index 000000000..6ba62a0ce
--- /dev/null
+++ b/broccoli/test-tree-builder.js
@@ -0,0 +1,58 @@
+/* jshint node:true */
+var funnel = require('broccoli-funnel');
+var es6 = require('broccoli-babel-transpiler');
+var concat = require('broccoli-concat');
+var mergeTrees = require('broccoli-merge-trees');
+
+var pkg = require('../package.json');
+var packageName = pkg.name;
+var outputFileName = packageName + '-tests.amd.js';
+
+function buildTestTree() {
+ var testJSTree = funnel('./tests', {
+ include: ['**/*.js'],
+ destDir: '/tests'
+ });
+
+ testJSTree = es6(testJSTree, {
+ moduleIds: true,
+ modules: 'amdStrict'
+ });
+
+ testJSTree = concat(testJSTree, {
+ inputFiles: ['**/*.js'],
+ outputFile: '/tests/' + outputFileName
+ });
+
+ var testExtTree = funnel('./node_modules', {
+ include: [
+ 'qunitjs/qunit/qunit.js',
+ 'qunitjs/qunit/qunit.css'
+ ],
+ destDir: '/tests'
+ });
+
+ testExtTree = mergeTrees([testExtTree, funnel('./bower_components', {
+ include: [
+ 'ember-cli-test-loader/test-loader.js'
+ ],
+ destDir: '/tests'
+ })]);
+
+ var testHTMLTree = funnel('./tests', {
+ include: ['index.html'],
+ destDir: '/tests'
+ });
+
+ var testTree = mergeTrees([
+ testJSTree,
+ testExtTree,
+ testHTMLTree
+ ]);
+
+ return testTree;
+}
+
+module.exports = {
+ build: buildTestTree
+};
diff --git a/package.json b/package.json
index 608fef4b3..569f4d0e0 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
},
"scripts": {
"start": "node server/index.js",
- "test": "gulp test",
+ "test": "testem ci",
"build": "rm -rf dist && broccoli build dist",
"prepublish": "npm run build"
},
@@ -30,6 +30,9 @@
},
"devDependencies": {
"broccoli": "^0.16.3",
+ "broccoli-babel-transpiler": "^5.2.3",
+ "broccoli-concat": "0.0.13",
+ "broccoli-funnel": "^0.2.3",
"broccoli-merge-trees": "^0.2.1",
"broccoli-multi-builder": "^0.1.0",
"content-kit-compiler": "0.2.3",
@@ -46,6 +49,7 @@
"gulp-open": "^0.2.8",
"gulp-qunit": "^1.2.1",
"gulp-uglify": "^1.1.0",
- "qunitjs": "^1.17.1"
+ "qunitjs": "^1.17.1",
+ "testem": "^0.8.4"
}
}
diff --git a/src/js/editor/editor-factory.js b/src/js/editor/editor-factory.js
index 5970ede41..6cf2637b0 100644
--- a/src/js/editor/editor-factory.js
+++ b/src/js/editor/editor-factory.js
@@ -1,5 +1,4 @@
import Editor from './editor';
-import { doc } from 'content-kit-compiler';
/**
* @class EditorFactory
@@ -17,7 +16,7 @@ function EditorFactory(element, options) {
}
if (typeof element === 'string') {
- elements = doc.querySelectorAll(element);
+ elements = document.querySelectorAll(element);
} else if (element && element.length) {
elements = element;
} else if (element) {
diff --git a/src/js/index.js b/src/js/index.js
index 7529959c0..4f2926b35 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -7,20 +7,21 @@ import {
HTMLRenderer
} from 'content-kit-compiler';
-import EditorFactory from './editor/editor-factory';
+import Editor from './editor/editor-factory';
-// Create a namespace and selectivly expose public modules
-var ContentKit = {};
-ContentKit.Type = Type;
-ContentKit.BlockModel = BlockModel;
-ContentKit.EmbedModel = EmbedModel;
-ContentKit.Compiler = Compiler;
-ContentKit.HTMLParser = HTMLParser;
-ContentKit.HTMLRenderer = HTMLRenderer;
-ContentKit.Editor = EditorFactory;
+const ContentKit = {
+ Type,
+ BlockModel,
+ EmbedModel,
+ Compiler,
+ HTMLParser,
+ HTMLRenderer,
+ Editor
+};
export function registerGlobal(global) {
global.ContentKit = ContentKit;
}
+export { Editor };
export default ContentKit;
diff --git a/testem.json b/testem.json
new file mode 100644
index 000000000..2f920c5f0
--- /dev/null
+++ b/testem.json
@@ -0,0 +1,16 @@
+{
+ "framework": "qunit",
+ "test_page": "dist/tests/index.html",
+ "src_files": [
+ "tests/**/*.js",
+ "src/**/*.js"
+ ],
+ "before_tests": "npm run build",
+ "launch_in_ci": [
+ "PhantomJS"
+ ],
+ "launch_in_dev": [
+ "PhantomJS",
+ "Chrome"
+ ]
+}
diff --git a/tests/tests.js b/tests/editor-test.js
similarity index 73%
rename from tests/tests.js
rename to tests/editor-test.js
index d279d4abb..81afba0be 100644
--- a/tests/tests.js
+++ b/tests/editor-test.js
@@ -1,8 +1,12 @@
+/* global QUnit, test, asyncTest, ok, equal */
+
var fixture = document.getElementById('qunit-fixture');
var editorElement = document.createElement('div');
editorElement.id = 'editor1';
editorElement.className = 'editor';
+import { Editor } from 'content-kit-editor';
+
QUnit.module('Editor', {
setup: function() {
fixture.appendChild(editorElement);
@@ -13,35 +17,35 @@ QUnit.module('Editor', {
});
test('can create an editor', function() {
- var editor = new ContentKit.Editor();
- ok(editor instanceof ContentKit.Editor);
+ var editor = new Editor();
+ ok(editor instanceof Editor);
});
test('can create an editor via dom node reference', function() {
- var editor = new ContentKit.Editor(editorElement);
+ var editor = new Editor(editorElement);
equal(editor.element, editorElement);
});
test('can create an editor via dom node reference from getElementById', function() {
- var editor = new ContentKit.Editor(document.getElementById('editor1'));
+ var editor = new Editor(document.getElementById('editor1'));
equal(editor.element, editorElement);
});
test('can create an editor via id selector', function() {
- var editor = new ContentKit.Editor('#editor1');
+ var editor = new Editor('#editor1');
equal(editor.element, editorElement);
});
test('can create an editor via class selector', function() {
- var editor = new ContentKit.Editor('.editor');
+ var editor = new Editor('.editor');
equal(editor.element, editorElement);
});
test('can recreate an editor on the same element', function() {
- var editor = new ContentKit.Editor('#editor1');
+ var editor = new Editor('#editor1');
ok(editor.element === editorElement);
- editor = new ContentKit.Editor('.editor');
+ editor = new Editor('.editor');
equal(editor.element, editorElement);
equal(editor.element.className, 'editor ck-editor');
});
@@ -49,21 +53,21 @@ test('can recreate an editor on the same element', function() {
test('creating an editor doesn\'t trash existing class names', function() {
editorElement.className = 'some-class';
- var editor = new ContentKit.Editor('.some-class');
+ var editor = new Editor('.some-class');
equal(editor.element.className, 'some-class ck-editor');
});
test('creating an editor without a class name adds appropriate class', function() {
editorElement.className = '';
- var editor = new ContentKit.Editor(document.getElementById('editor1'));
+ var editor = new Editor(document.getElementById('editor1'));
equal(editor.element.className, 'ck-editor');
});
asyncTest('editor fires update event', function() {
expect(2);
- var editor = new ContentKit.Editor();
+ var editor = new Editor();
editor.on('update', function(data) {
equal (this, editor);
equal (data.index, 99);
diff --git a/tests/index.html b/tests/index.html
index 3b36ef372..dd6f7ad70 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -2,17 +2,23 @@
- QUnit
-
+ Content-Kit-Editor tests
+
-
-
-
-
+
+
+
+
+
+
From 26133874bff593a4565d9f4d3ce6ef361c87a503 Mon Sep 17 00:00:00 2001
From: Cory Forsyth
Date: Tue, 30 Jun 2015 18:38:44 -0400
Subject: [PATCH 08/77] use content-kit-compiler 0.3.1
---
.npmignore | 4 ++++
package.json | 4 ++--
2 files changed, 6 insertions(+), 2 deletions(-)
create mode 100644 .npmignore
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 000000000..5ed267a2b
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+bower_components/
+node_modules/
+tmp/
+tests/
diff --git a/package.json b/package.json
index 569f4d0e0..1f293f58d 100644
--- a/package.json
+++ b/package.json
@@ -35,8 +35,8 @@
"broccoli-funnel": "^0.2.3",
"broccoli-merge-trees": "^0.2.1",
"broccoli-multi-builder": "^0.1.0",
- "content-kit-compiler": "0.2.3",
- "content-kit-utils": "0.1.2",
+ "content-kit-compiler": "^0.3.1",
+ "content-kit-utils": "^0.2.0",
"del": "^1.1.1",
"esperanto": "^0.6.32",
"gulp": "^3.8.11",
From a0c5c563bb4211bce0fdb4fe8566fc2dff2efa5d Mon Sep 17 00:00:00 2001
From: Cory Forsyth
Date: Wed, 1 Jul 2015 13:26:04 -0400
Subject: [PATCH 09/77] update to work with broccoli multi builder 0.2.2, bring
in loader.js to tests
fix brocfile and tests to work with new dist structure
---
Brocfile.js | 34 +++++++++++-----------------------
broccoli/test-tree-builder.js | 13 +++++++++++++
package.json | 8 ++++----
tests/index.html | 3 ++-
4 files changed, 30 insertions(+), 28 deletions(-)
diff --git a/Brocfile.js b/Brocfile.js
index 64270fd00..f070ea8ed 100644
--- a/Brocfile.js
+++ b/Brocfile.js
@@ -1,36 +1,24 @@
/* jshint node:true */
-var multiBuilder = require('broccoli-multi-builder');
+var builder = require('broccoli-multi-builder');
var mergeTrees = require('broccoli-merge-trees');
var testTreeBuilder = require('./broccoli/test-tree-builder');
-var jsSrc = './src/js';
-var vendoredModules = ['content-kit-compiler', 'content-kit-utils'];
+var vendoredModules = [
+ {name: 'content-kit-compiler', options: {libDirName: 'src'}},
+ {name: 'content-kit-utils', options: {libDirName: 'src'}}
+];
var packageName = require('./package.json').name;
-var amdTree = multiBuilder.buildAMD({
- isGlobal: false,
- src: jsSrc,
+var buildOptions = {
+ libDirName: 'src/js',
vendoredModules: vendoredModules,
packageName: packageName
-});
-
-var globalTree = multiBuilder.buildAMD({
- isGlobal: true,
- src: jsSrc,
- vendoredModules: vendoredModules,
- packageName: packageName
-});
-
-var cjsTree = multiBuilder.buildCJS({
- src: jsSrc,
- vendoredModules: vendoredModules,
- packageName: packageName
-});
+};
module.exports = mergeTrees([
- amdTree,
- globalTree,
- cjsTree,
+ builder.build('amd', buildOptions),
+ builder.build('global', buildOptions),
+ builder.build('commonjs', buildOptions),
testTreeBuilder.build()
]);
diff --git a/broccoli/test-tree-builder.js b/broccoli/test-tree-builder.js
index 6ba62a0ce..6b1a893a6 100644
--- a/broccoli/test-tree-builder.js
+++ b/broccoli/test-tree-builder.js
@@ -7,6 +7,15 @@ var mergeTrees = require('broccoli-merge-trees');
var pkg = require('../package.json');
var packageName = pkg.name;
var outputFileName = packageName + '-tests.amd.js';
+var path = require('path');
+
+function loaderTree() {
+ var loaderDir = path.dirname(require.resolve('loader.js'));
+ return funnel(loaderDir, {
+ include: ['loader.js'],
+ destDir: '/tests/loader.js'
+ });
+}
function buildTestTree() {
var testJSTree = funnel('./tests', {
@@ -24,6 +33,7 @@ function buildTestTree() {
outputFile: '/tests/' + outputFileName
});
+ // bring in qunit
var testExtTree = funnel('./node_modules', {
include: [
'qunitjs/qunit/qunit.js',
@@ -32,6 +42,7 @@ function buildTestTree() {
destDir: '/tests'
});
+ // bring in test-loader
testExtTree = mergeTrees([testExtTree, funnel('./bower_components', {
include: [
'ember-cli-test-loader/test-loader.js'
@@ -39,12 +50,14 @@ function buildTestTree() {
destDir: '/tests'
})]);
+ // include HTML file
var testHTMLTree = funnel('./tests', {
include: ['index.html'],
destDir: '/tests'
});
var testTree = mergeTrees([
+ loaderTree(),
testJSTree,
testExtTree,
testHTMLTree
diff --git a/package.json b/package.json
index 1f293f58d..1e2bc3b06 100644
--- a/package.json
+++ b/package.json
@@ -3,15 +3,14 @@
"version": "0.1.3",
"description": "A modern, minimalist WYSIWYG editor.",
"repository": "https://github.com/bustlelabs/content-kit-editor",
- "main": "dist/content-kit-editor.js",
+ "main": "dist/commonjs/content-kit-editor/index.js",
"engines": {
"node": "0.10.x"
},
"scripts": {
"start": "node server/index.js",
"test": "testem ci",
- "build": "rm -rf dist && broccoli build dist",
- "prepublish": "npm run build"
+ "build": "rm -rf dist && broccoli build dist"
},
"keywords": [
"html",
@@ -34,7 +33,7 @@
"broccoli-concat": "0.0.13",
"broccoli-funnel": "^0.2.3",
"broccoli-merge-trees": "^0.2.1",
- "broccoli-multi-builder": "^0.1.0",
+ "broccoli-multi-builder": "^0.2.2",
"content-kit-compiler": "^0.3.1",
"content-kit-utils": "^0.2.0",
"del": "^1.1.1",
@@ -49,6 +48,7 @@
"gulp-open": "^0.2.8",
"gulp-qunit": "^1.2.1",
"gulp-uglify": "^1.1.0",
+ "loader.js": "^3.2.0",
"qunitjs": "^1.17.1",
"testem": "^0.8.4"
}
diff --git a/tests/index.html b/tests/index.html
index dd6f7ad70..8275ebfa9 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -12,7 +12,8 @@
-
+
+
+
-
-
+
+
+
+
+
From 43fd3c63053efbc5160d373276253bef1c1c78f5 Mon Sep 17 00:00:00 2001
From: Matthew Beale
Date: Wed, 8 Jul 2015 12:22:59 -0400
Subject: [PATCH 26/77] WIP: Start rewriting the demo
TODO:
* Watch the mobiledoc to load and re-start the editor
* Handle errors gracefully
* Show serialized output
* Show rendered output
* Style, make the code panes dark
---
demo/demo.css | 16 +++++----
demo/demo.js | 84 +++++++++-------------------------------------
demo/index.html | 88 +++++++++++++++++--------------------------------
src/js/index.js | 2 +-
4 files changed, 57 insertions(+), 133 deletions(-)
diff --git a/demo/demo.css b/demo/demo.css
index c3bcb4112..35fddad65 100644
--- a/demo/demo.css
+++ b/demo/demo.css
@@ -16,6 +16,14 @@ body {
}
}
+.container {
+ display: flex;
+ justify-content: space-around;
+}
+
+.pane {
+}
+
.editor-pane {
max-width: 50em;
margin: 0 auto;
@@ -55,13 +63,7 @@ body {
right: 0;
width: 50%;
}
-.code-pane {
- display: none;
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- width: 100%;
+#serialized-mobiledoc, #mobiledoc-to-load {
background-color: #2b303b;
overflow: hidden;
}
diff --git a/demo/demo.js b/demo/demo.js
index 3c9f1b1ee..d7c76d07d 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -3,67 +3,12 @@
'use strict';
var ContentKitDemo = exports.ContentKitDemo = {
- toggleCodePane: function() {
- if(document.body.className === 'code-pane-open') {
- this.closeCodePane();
- } else {
- this.openCodePane(editor);
- }
- },
-
- openCodePane: function() {
- window.getSelection().removeAllRanges();
- document.body.className = 'code-pane-open';
- location.hash = 'code';
- },
-
- closeCodePane: function() {
- window.getSelection().removeAllRanges();
- document.body.className = '';
- location.hash = '';
- },
-
syncCodePane: function(editor) {
- var codePaneJSON = document.getElementById('code-json');
- var codePaneHTML = document.getElementById('code-html');
+ var codePaneJSON = document.getElementById('serialized-mobiledoc');
var json = editor.serialize();
codePaneJSON.innerHTML = this.syntaxHighlight(json);
},
- formatXML: function(xml) {
- // https://gist.github.com/sente/1083506
- xml = xml.replace(/(>)(<)(\/*)/g, '$1\r\n$2$3');
- var formatted = '';
- var pad = 0;
- var nodes = xml.split('\r\n');
- var nodeLen = nodes.length;
- var node, indent, padding, i, j;
- for(i = 0; i < nodeLen; i++) {
- node = nodes[i];
- if (node.match( /.+<\/\w[^>]*>$/ )) {
- indent = 0;
- } else if (node.match( /^<\/\w/ )) {
- if (pad != 0) {
- pad -= 1;
- }
- } else if (node.match( /^<\w[^>]*[^\/]>.*$/ )) {
- indent = 1;
- } else {
- indent = 0;
- }
-
- padding = '';
- for (j = 0; j < pad; j++) {
- padding += ' ';
- }
-
- formatted += padding + node + '\r\n';
- pad += indent;
- }
-
- return formatted;
- },
-
syntaxHighlight: function(json) {
// http://stackoverflow.com/a/7220510/189440
if (typeof json !== 'string') {
@@ -89,19 +34,22 @@ var ContentKitDemo = exports.ContentKitDemo = {
};
-// Initialize
-if (window.editor) {
- ContentKitDemo.syncCodePane(editor);
- editor.on('update', function() {
- ContentKitDemo.syncCodePane(this);
+$(function() {
+ var textarea = $('#mobiledoc-to-load textarea');
+ var textPayload = textarea.val();
+ var payload = JSON.parse(textPayload);
+ var editorF = new ContentKit.Editor($('#editor')[0], {
+ mobiledoc: payload,
+ cards: {
+ 'pick-color': function renderPickColor(payload) {
+ return 'PICK A COLOR: '+payload.options.join(', ');
+ }
+ }
});
- var settingsBtn = document.getElementById('settings-btn');
- settingsBtn.addEventListener('click', function() {
- ContentKitDemo.toggleCodePane();
+
+ editorF.on('update', function(editor) {
+ ContentKitDemo.syncCodePane(editor);
});
-}
-if (location.hash === '#code') {
- ContentKitDemo.openCodePane();
-}
+});
}(this, document));
diff --git a/demo/index.html b/demo/index.html
index cf3797303..100354f38 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -15,73 +15,47 @@
-
-
-
-
-
-
ContentKit Editor
-
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
-
Pressing "H2" will create a subheading, like this one.
-
Create block quotes by selecting any text and pressing the "quote" button. Press it again to toggle back to a standard paragraph.
-
To create a list, start typing a dash followed by a space ("- ") on a new line and a list will be automatically created.
-
To create an ordered list, start typing a one followed by a period and a space ("1. ") and the list will be automatically created.
-
-
Tips & Tricks:
-
-
Close the formatting toolbar by clicking anywhere, or press ESC
-
Make the toolbar sticky by pressing F5
-
Double click a word to select it
-
You only have to select a portion of a paragraph if you want to change it to a heading, subheading, or quote
-
To create a soft line break, press shift + enter
-
Press enter twice to exit a list
-
-
-
Keyboard shortcuts:
-
-
bold: (cmd+B)
-
italic: (cmd+I)
-
undo: (cmd+z)
-
redo: (cmd+y)
-
select all text: (cmd+a)
-
select letters: (hold shift + arrow keys)
-
close toolbar: (ESC)
-
-
-
Enjoy focusing on your content and not worrying about formatting!