Skip to content

Commit

Permalink
Use normalizeTagName, Markup.create
Browse files Browse the repository at this point in the history
  • Loading branch information
bantic committed Aug 5, 2015
1 parent f3e99c6 commit dcf686e
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 75 deletions.
47 changes: 18 additions & 29 deletions src/js/commands/bold.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
import TextFormatCommand from './text-format';
import { inherit } from 'content-kit-utils';
import Markup from '../models/markup';
import {
any
} from '../utils/array-utils';

function BoldCommand(editor) {
TextFormatCommand.call(this, {
name: 'bold',
tag: 'strong',
mappedTags: ['b'],
button: '<i class="ck-icon-bold"></i>'
});
this.editor = editor;
export default class BoldCommand extends TextFormatCommand {
constructor(editor) {
super({
name: 'bold',
button: '<i class="ck-icon-bold"></i>'
});
this.editor = editor;
this.markup = Markup.create('b');
}
exec() {
this.editor.applyMarkupToSelection(this.markup);
}
unexec() {
this.editor.removeMarkupFromSelection(this.markup);
}
isActive() {
return any(this.editor.activeMarkers, m => m.hasMarkup(this.markup));
}
}
inherit(BoldCommand, TextFormatCommand);

BoldCommand.prototype.exec = function() {
const markup = Markup.ofType('b');
this.editor.applyMarkupToSelection(markup);
};

BoldCommand.prototype.isActive = function() {
let val = any(this.editor.activeMarkers, m => {
return any(this.mappedTags, tag => m.hasMarkup(tag));
});
return val;
};

BoldCommand.prototype.unexec = function() {
const markup = Markup.ofType('b');
this.editor.removeMarkupFromSelection(markup);
};

export default BoldCommand;
4 changes: 3 additions & 1 deletion src/js/commands/text-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export default class TextFormatCommand extends Command {

this.tag = options.tag;
this.mappedTags = options.mappedTags || [];
this.mappedTags.push(this.tag);
if (this.tag) {
this.mappedTags.push(this.tag);
}
this.action = options.action || this.name;
this.removeAction = options.removeAction || this.action;
}
Expand Down
6 changes: 1 addition & 5 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,7 @@ class Editor {
}

removeMarkupFromSelection(markup) {
const markers = this.activeMarkers;
// FIXME-NEXT Now we need to ensure we are using the singleton
// markup for the 'B' tag
// in order to get http://localhost:4200/tests/?testId=8cb07cab
// to pass
const markers = this.splitMarkersFromSelection();
markers.forEach(marker => {
marker.removeMarkup(markup);
marker.section.renderNode.markDirty();
Expand Down
14 changes: 11 additions & 3 deletions src/js/models/marker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const MARKER_TYPE = 'marker';

import {
normalizeTagName
} from '../utils/dom-utils';
import { detect } from 'content-kit-editor/utils/array-utils';

const Marker = class Marker {
Expand Down Expand Up @@ -55,9 +58,14 @@ const Marker = class Marker {
this.value = left + right;
}

hasMarkup(tagName) {
tagName = tagName.toLowerCase();
return detect(this.markups, markup => markup.tagName === tagName);
hasMarkup(tagNameOrMarkup) {
if (typeof tagNameOrMarkup === 'string') {
let tagName = normalizeTagName(tagNameOrMarkup);
return detect(this.markups, markup => markup.tagName === tagName);
} else {
let targetMarkup = tagNameOrMarkup;
return detect(this.markups, markup => markup === targetMarkup);
}
}

getMarkup(tagName) {
Expand Down
23 changes: 15 additions & 8 deletions src/js/models/markup.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ export const VALID_MARKUP_TAGNAMES = [
'li'
].map(normalizeTagName);

let markupsOfType = {};
const markupMap = {};

class Markup {
/*
* @param {attributes} array flat array of key1,value1,key2,value2,...
*/
constructor(tagName, attributes=[]) {
this.tagName = tagName.toLowerCase();
this.tagName = normalizeTagName(tagName);
this.attributes = attributes;
this.type = MARKUP_TYPE;

Expand All @@ -27,17 +27,24 @@ class Markup {
}
}

static ofType(tagName) {
// Use `create` to make a new markup so that we can use singletons in the
// markupMap
static create(tagName, attributes=[]) {
tagName = normalizeTagName(tagName);
if (!markupsOfType[tagName]) {
markupsOfType[tagName] = new Markup(tagName);
}

return markupsOfType[tagName];
if (attributes.length === 0) {
if (!markupMap[tagName]) {
markupMap[tagName] = new Markup(tagName);
}

return markupMap[tagName];
} else {
return new Markup(tagName, attributes);
}
}

static isValidElement(element) {
let tagName = element.tagName.toLowerCase();
let tagName = normalizeTagName(element.tagName);
return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1;
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/js/parsers/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { generateBuilder } from '../utils/post-builder';
import { trim } from 'content-kit-utils';
import { VALID_MARKUP_SECTION_TAGNAMES } from '../models/markup-section';
import { VALID_MARKUP_TAGNAMES } from '../models/markup';
import { normalizeTagName } from '../utils/dom-utils';

const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
Expand Down Expand Up @@ -58,7 +59,8 @@ function readAttributes(node) {
}

function isValidMarkerElement(element) {
return VALID_MARKUP_TAGNAMES.indexOf(element.tagName.toLowerCase()) !== -1;
let tagName = normalizeTagName(element.tagName);
return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1;
}

function parseMarkers(section, postBuilder, topNode) {
Expand Down Expand Up @@ -128,9 +130,9 @@ NewHTMLParser.prototype = {
var section;
switch(sectionElement.nodeType) {
case ELEMENT_NODE:
var tagName = sectionElement.tagName;
let tagName = normalizeTagName(sectionElement.tagName);
// <p> <h2>, etc
if (VALID_MARKUP_SECTION_TAGNAMES.indexOf(tagName.toLowerCase()) !== -1) {
if (VALID_MARKUP_SECTION_TAGNAMES.indexOf(tagName) !== -1) {
section = postBuilder.generateMarkupSection(tagName, readAttributes(sectionElement));
var node = sectionElement.firstChild;
while (node) {
Expand Down
13 changes: 8 additions & 5 deletions src/js/parsers/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
import Marker from 'content-kit-editor/models/marker';
import Markup from 'content-kit-editor/models/markup';
import { VALID_MARKUP_TAGNAMES } from 'content-kit-editor/models/markup';
import { getAttributes } from 'content-kit-editor/utils/dom-utils';
import {
getAttributes,
normalizeTagName
} from 'content-kit-editor/utils/dom-utils';
import { forEach } from 'content-kit-editor/utils/array-utils';
import { generateBuilder } from 'content-kit-editor/utils/post-builder';

Expand Down Expand Up @@ -88,19 +91,19 @@ export default {

isSectionElement(element) {
return element.nodeType === ELEMENT_NODE &&
VALID_MARKUP_SECTION_TAGNAMES.indexOf(element.tagName.toLowerCase()) !== -1;
VALID_MARKUP_SECTION_TAGNAMES.indexOf(normalizeTagName(element.tagName)) !== -1;
},

markupFromElement(element) {
const tagName = element.tagName.toLowerCase();
const tagName = normalizeTagName(element.tagName);
if (VALID_MARKUP_TAGNAMES.indexOf(tagName) === -1) { return null; }

return new Markup(tagName, getAttributes(element));
return Markup.create(tagName, getAttributes(element));
},

sectionTagNameFromElement(element) {
let tagName = element.tagName;
tagName = tagName && tagName.toLowerCase();
tagName = tagName && normalizeTagName(tagName);
if (VALID_MARKUP_SECTION_TAGNAMES.indexOf(tagName) === -1) { tagName = DEFAULT_TAG_NAME; }
return tagName;
}
Expand Down
8 changes: 6 additions & 2 deletions src/js/utils/element-utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { UNPRINTABLE_CHARACTER } from 'content-kit-editor/renderers/editor-dom';
import { dasherize } from 'content-kit-editor/utils/string-utils';
import {
normalizeTagName
} from 'content-kit-editor/utils/dom-utils';

function createDiv(className) {
var div = document.createElement('div');
Expand All @@ -22,10 +25,11 @@ function swapElements(elementToShow, elementToHide) {
showElement(elementToShow);
}

function getEventTargetMatchingTag(tag, target, container) {
function getEventTargetMatchingTag(tagName, target, container) {
tagName = normalizeTagName(tagName);
// Traverses up DOM from an event target to find the node matching specifed tag
while (target && target !== container) {
if (target.tagName.toLowerCase() === tag) {
if (normalizeTagName(target.tagName) === tagName) {
return target;
}
target = target.parentNode;
Expand Down
4 changes: 2 additions & 2 deletions src/js/utils/post-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ var builder = {
generateMarkup(tagName, attributes) {
if (attributes) {
// FIXME: This could also be cached
return new Markup(tagName, attributes);
return Markup.create(tagName, attributes);
}
var markerType = this._markerTypeCache[tagName];
if (!markerType) {
this._markerTypeCache[tagName] = markerType = new Markup(tagName);
this._markerTypeCache[tagName] = markerType = Markup.create(tagName);
}
return markerType;
}
Expand Down
15 changes: 9 additions & 6 deletions src/js/utils/selection-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { containsNode } from './dom-utils';
import {
containsNode,
normalizeTagName
} from './dom-utils';

// TODO: remove, pass in Editor's current block set
var RootTags = [
Expand Down Expand Up @@ -54,25 +57,25 @@ function isSelectionInElement(element) {
function getSelectionBlockElement(selection) {
selection = selection || window.getSelection();
var element = getSelectionElement();
var tag = element && element.tagName.toLowerCase();
let tag = element && normalizeTagName(element.tagName);
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();
tag = element.tagName && normalizeTagName(element.tagName);
}
return element;
}

function getSelectionTagName() {
var element = getSelectionElement();
return element ? element.tagName.toLowerCase() : null;
return element ? normalizeTagName(element.tagName) : null;
}

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

function tagsInSelection(selection) {
Expand All @@ -81,7 +84,7 @@ function tagsInSelection(selection) {
while(element) {
if (element.contentEditable === 'true') { break; } // Stop traversing up dom when hitting an editor element
if (element.tagName) {
tags.push(element.tagName.toLowerCase());
tags.push(normalizeTagName(element.tagName));
}
element = element.parentNode;
}
Expand Down
27 changes: 27 additions & 0 deletions tests/acceptance/editor-commands-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,5 +276,32 @@ test('click bold button applies bold to selected text', (assert) => {
});
});

test('can unbold part of a larger set of bold text', (assert) => {
const done = assert.async();

setTimeout(() => {
assertInactiveToolbarButton(assert, 'bold', 'precond - bold button is not active');
clickToolbarButton(assert, 'bold');
assertActiveToolbarButton(assert, 'bold');

assert.hasElement('#editor b:contains(IS A)');

Helpers.dom.selectText('S A', editorElement);
Helpers.dom.triggerEvent(document, 'mouseup');

setTimeout(() => {
assertToolbarVisible(assert);
assertActiveToolbarButton(assert, 'bold');
clickToolbarButton(assert, 'bold');

assert.hasElement('#editor b:contains(I)', 'unselected text is bold');
assert.hasNoElement('#editor b:contains(IS A)', 'unselected text is bold');
assert.hasElement('#editor p:contains(S A)', 'unselected text is bold');

done();
});
});
});

// test selecting across markers and boldening
// test selecting across markers in sections and bolding
Loading

0 comments on commit dcf686e

Please sign in to comment.