Skip to content

Commit

Permalink
abstract auto typing text formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
gpoitch committed Aug 26, 2014
1 parent 074f846 commit f16e8b2
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 181 deletions.
267 changes: 141 additions & 126 deletions dist/content-kit-editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/js/content-kit-editor/commands/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function createFileInput(command) {
return fileInput;
}

function ImageCommand(options) {
function ImageCommand() {
Command.call(this, {
name: 'image',
button: '<i class="ck-icon-image"></i>'
Expand Down
21 changes: 19 additions & 2 deletions src/js/content-kit-editor/commands/list.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import TextFormatCommand from './text-format';
import { getSelectionBlockElement, selectNode, getSelectionTagName } from '../utils/selection-utils';
import { inherit } from '../../content-kit-utils/object-utils';
import { getSelectionBlockElement, selectNode } from '../utils/selection-utils';
import Type from '../../content-kit-compiler/types/type';

function ListCommand(options) {
TextFormatCommand.call(this, options);
Expand All @@ -10,7 +11,8 @@ inherit(ListCommand, TextFormatCommand);
ListCommand.prototype.exec = function() {
ListCommand._super.prototype.exec.call(this);

// After creation, lists need to be unwrapped from the default formatter P tag
// 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) {
Expand All @@ -21,4 +23,19 @@ ListCommand.prototype.exec = function() {
}
};

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 (Type.LIST_ITEM.tag !== getSelectionTagName() && regex.test(text)) {
this.exec();
window.getSelection().anchorNode.textContent = text.replace(regex, '');
return true;
}
}
return false;
};

export default ListCommand;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Prompt from '../views/prompt';
import Message from '../views/message';
import EmbedModel from '../../content-kit-compiler/models/embed';
import { inherit } from '../../content-kit-utils/object-utils';
import { RegEx } from '../constants';
import { OEmbedder } from '../../ext/content-kit-services';

function loadTwitterWidgets(element) {
Expand All @@ -17,7 +16,7 @@ function loadTwitterWidgets(element) {
}
}

function EmbedCommand(options) {
function OEmbedCommand() {
Command.call(this, {
name: 'embed',
button: '<i class="ck-icon-embed"></i>',
Expand All @@ -29,9 +28,9 @@ function EmbedCommand(options) {

this.embedService = new OEmbedder({ url: '/embed' });
}
inherit(EmbedCommand, Command);
inherit(OEmbedCommand, Command);

EmbedCommand.prototype.exec = function(url) {
OEmbedCommand.prototype.exec = function(url) {
var command = this;
var editorContext = command.editorContext;
var embedIntent = command.embedIntent;
Expand All @@ -50,6 +49,7 @@ EmbedCommand.prototype.exec = function(url) {
errorMsg = 'Embed error';
}
new Message().show(errorMsg);
embedIntent.show();
} else {
var embedModel = new EmbedModel(response);
editorContext.insertBlockAt(embedModel, index);
Expand All @@ -62,4 +62,4 @@ EmbedCommand.prototype.exec = function(url) {
});
};

export default EmbedCommand;
export default OEmbedCommand;
2 changes: 2 additions & 0 deletions src/js/content-kit-editor/commands/ordered-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ function OrderedListCommand() {
}
inherit(OrderedListCommand, ListCommand);

OrderedListCommand.prototype.autoFormatRegex = /^1\.\s/;

export default OrderedListCommand;
1 change: 0 additions & 1 deletion src/js/content-kit-editor/commands/text-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ inherit(TextFormatCommand, Command);
TextFormatCommand.prototype = {
exec: function(value) {
document.execCommand(this.action, false, value || null);
this.editorContext.syncModelAt(this.editorContext.getCurrentBlockIndex());
},
unexec: function(value) {
document.execCommand(this.removeAction, false, value || null);
Expand Down
2 changes: 2 additions & 0 deletions src/js/content-kit-editor/commands/unordered-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ function UnorderedListCommand() {
}
inherit(UnorderedListCommand, ListCommand);

UnorderedListCommand.prototype.autoFormatRegex = /^[-*]\s/;

export default UnorderedListCommand;
4 changes: 1 addition & 3 deletions src/js/content-kit-editor/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ var Keycodes = {
var RegEx = {
NEWLINE : /[\r\n]/g,
HTTP_PROTOCOL : /^https?:\/\//i,
HEADING_TAG : /^(h1|h2|h3|h4|h5|h6)$/i,
UL_START : /^[-*]\s/,
OL_START : /^1\.\s/
HEADING_TAG : /^(h1|h2|h3|h4|h5|h6)$/i
};

var SelectionDirection = {
Expand Down
74 changes: 37 additions & 37 deletions src/js/content-kit-editor/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import SubheadingCommand from '../commands/subheading';
import UnorderedListCommand from '../commands/unordered-list';
import OrderedListCommand from '../commands/ordered-list';
import ImageCommand from '../commands/image';
import EmbedCommand from '../commands/embed';
import OEmbedCommand from '../commands/oembed';
import TextFormatCommand from '../commands/text-format';
import { RootTags, Keycodes, RegEx } from '../constants';
import { moveCursorToBeginningOfSelection, getSelectionTagName, getSelectionBlockElement, getSelectionBlockTagName } from '../utils/selection-utils';
import { RootTags, Keycodes } from '../constants';
import { getSelectionBlockElement, getSelectionBlockTagName } from '../utils/selection-utils';
import { cleanPastedContent } from '../utils/paste-utils';
import Compiler from '../../content-kit-compiler/compiler';
import TextModel from '../../content-kit-compiler/models/text';
Expand All @@ -36,11 +36,15 @@ var defaults = {
],
embedCommands: [
new ImageCommand(),
new EmbedCommand()
new OEmbedCommand()
],
autoTypingCommands: [
new UnorderedListCommand(),
new OrderedListCommand()
],
compiler: new Compiler({
includeTypeNames: true, // output type names for easier debugging
renderer: new EditorHTMLRenderer()
includeTypeNames: true, // outputs models with type names, i.e. 'BOLD', for easier debugging
renderer: new EditorHTMLRenderer() // subclassed HTML renderer that adds structure for editor interactivity
})
};

Expand All @@ -51,6 +55,7 @@ function bindTypingEvents(editor) {
var editorEl = editor.element;

// Breaks out of blockquotes when pressing enter.
// TODO: remove when direction model manip. complete
editorEl.addEventListener('keyup', function(e) {
if(!e.shiftKey && e.which === Keycodes.ENTER) {
if(Type.QUOTE.tag === getSelectionBlockTagName()) {
Expand All @@ -60,47 +65,42 @@ function bindTypingEvents(editor) {
}
});

// Creates unordered list when block starts with '- ', or ordered if starts with '1. '
editorEl.addEventListener('keyup', function(e) {
var selection = window.getSelection();
var selectionNode = selection.anchorNode;
if (!selectionNode) { return; }

var selectedText = selectionNode.textContent;
var command, replaceRegex;

if (Type.LIST_ITEM.tag !== getSelectionTagName()) {
if (RegEx.UL_START.test(selectedText)) {
command = new UnorderedListCommand();
replaceRegex = RegEx.UL_START;
} else if (RegEx.OL_START.test(selectedText)) {
command = new OrderedListCommand();
replaceRegex = RegEx.OL_START;
}

if (command) {
command.editorContext = editor;
command.exec();
selection = window.getSelection();
selection.anchorNode.textContent = selectedText.replace(replaceRegex, '');
moveCursorToBeginningOfSelection(selection);
e.stopPropagation();
}
}
});

// Assure there is always a supported root tag, and not empty text nodes or divs.
// TODO: remove when direction model manip. complete
editorEl.addEventListener('keyup', function() {
if (this.innerHTML.length && RootTags.indexOf(getSelectionBlockTagName()) === -1) {
document.execCommand('formatBlock', false, Type.TEXT.tag);
}
});

// Experimental: Live update - sync model with textual content as you type
// Watch typing patterns for auto format commands (e.g. lists '- ', '1. ')
editorEl.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;
}
}
}
});

// Experimental: Live update
editorEl.addEventListener('keyup', function() {
var index = editor.getCurrentBlockIndex();
editor.syncModelAt(index);
});
document.addEventListener('mouseup', function() {
setTimeout(function() {
var index = editor.getCurrentBlockIndex();
editor.syncModelAt(index);
});
});
}

/**
Expand Down Expand Up @@ -143,7 +143,7 @@ function Editor(element, options) {
}
});

editor.textFormatToolbar = new TextFormatToolbar({ rootElement: element, editor: editor, commands: editor.textFormatCommands });
editor.textFormatToolbar = new TextFormatToolbar({ rootElement: element, commands: editor.textFormatCommands });
var linkTooltips = new Tooltip({ rootElement: element, showForTag: Type.LINK.tag });

if(editor.embedCommands) {
Expand Down
2 changes: 1 addition & 1 deletion src/js/content-kit-editor/utils/element-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function swapElements(elementToShow, elementToHide) {
function getEventTargetMatchingTag(tag, target, container) {
// Traverses up DOM from an event target to find the node matching specifed tag
while (target && target !== container) {
if (target.tagName === tag) {
if (target.tagName.toLowerCase() === tag) {
return target;
}
target = target.parentNode;
Expand Down
4 changes: 2 additions & 2 deletions src/js/content-kit-editor/utils/selection-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function getSelectionBlockElement(selection) {
while (tag && RootTags.indexOf(tag) === -1) {
if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
element = element.parentNode;
tag = element.tagName.toLowerCase();
tag = element.tagName && element.tagName.toLowerCase();
}
return element;
}
Expand All @@ -37,7 +37,7 @@ function getSelectionTagName() {

function getSelectionBlockTagName() {
var element = getSelectionBlockElement();
return element ? element.tagName.toLowerCase() : null;
return element ? element.tagName && element.tagName.toLowerCase() : null;
}

function tagsInSelection(selection) {
Expand Down
2 changes: 1 addition & 1 deletion src/js/content-kit-editor/views/toolbar-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function ToolbarButton(options) {
element.title = command.name;
element.className = buttonClassName;
element.innerHTML = command.button;
element.addEventListener('click', function(e) {
element.addEventListener('click', function() {
if (!button.isActive && prompt) {
toolbar.displayPrompt(prompt);
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/js/content-kit-editor/views/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ function updateButtonsForSelection(buttons, selection) {
function Toolbar(options) {
var toolbar = this;
var commands = options.commands;
var commandCount = commands && commands.length;
var i, button, command;
var commandCount = commands && commands.length, i;
toolbar.editor = options.editor || null;
toolbar.embedIntent = options.embedIntent || null;
toolbar.direction = options.direction || ToolbarDirection.TOP;
Expand Down

0 comments on commit f16e8b2

Please sign in to comment.