Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/js/parsers/dom.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NO_BREAK_SPACE } from '../renderers/editor-dom';
import {
MARKUP_SECTION_TYPE,
LIST_SECTION_TYPE,
Expand All @@ -18,6 +19,13 @@ import Markup from 'content-kit-editor/models/markup';

const GOOGLE_DOCS_CONTAINER_ID_REGEX = /^docs\-internal\-guid/;

const NO_BREAK_SPACE_REGEX = new RegExp(NO_BREAK_SPACE, 'g');
export function transformHTMLText(textContent) {
let text = textContent;
text = text.replace(NO_BREAK_SPACE_REGEX, ' ');
return text;
}

function isGoogleDocsContainer(element) {
return !isTextNode(element) &&
normalizeTagName(element.tagName) === normalizeTagName('b') &&
Expand Down Expand Up @@ -154,7 +162,7 @@ export default class DOMParser {
let previousMarker;

walkTextNodes(element, (textNode) => {
const text = textNode.textContent;
const text = transformHTMLText(textNode.textContent);
let markups = this.collectMarkups(textNode, element);

let marker;
Expand Down
10 changes: 7 additions & 3 deletions src/js/parsers/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import {
contains
} from 'content-kit-editor/utils/array-utils';

import {
transformHTMLText
} from '../parsers/dom';

function isListSection(section) {
return section.type === LIST_SECTION_TYPE;
}
Expand Down Expand Up @@ -164,8 +168,7 @@ export default class SectionParser {

// close a trailing text node if it exists
if (state.text.length) {
let marker = this.builder.createMarker(state.text, state.markups);
state.section.markers.append(marker);
this._createMarker();
}

sections.push(state.section);
Expand Down Expand Up @@ -222,7 +225,8 @@ export default class SectionParser {

_createMarker() {
let { state } = this;
let marker = this.builder.createMarker(state.text, state.markups);
let text = transformHTMLText(state.text);
let marker = this.builder.createMarker(text, state.markups);
state.section.markers.append(marker);
state.text = '';
}
Expand Down
33 changes: 21 additions & 12 deletions src/js/renderers/editor-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ function createElementFromMarkup(doc, markup) {
return element;
}

// FIXME: This can be done more efficiently with a single pass
// building a correct string based on the original.
function renderHTMLText(marker) {
let text = marker.value;
// If the first marker has a leading space or the last marker has a
// trailing space, the browser will collapse the space when we position
// the cursor.
// See https://github.com/bustlelabs/content-kit-editor/issues/68
// and https://github.com/bustlelabs/content-kit-editor/issues/75
if (!marker.next && endsWith(text, SPACE)) {
text = text.substr(0, text.length - 1) + NO_BREAK_SPACE;
} else if ((!marker.prev || endsWith(marker.prev.value, SPACE)) && startsWith(text, SPACE)) {
text = NO_BREAK_SPACE + text.substr(1);
}
text = text.replace(/ ( )/g, () => {
return ' '+NO_BREAK_SPACE;
});
return text;
}

// ascends from element upward, returning the last parent node that is not
// parentElement
function penultimateParentOf(element, parentElement) {
Expand Down Expand Up @@ -79,18 +99,7 @@ function getNextMarkerElement(renderNode) {
}

function renderMarker(marker, element, previousRenderNode) {
let text = marker.value;

// If the first marker has a leading space or the last marker has a
// trailing space, the browser will collapse the space when we position
// the cursor.
// See https://github.com/bustlelabs/content-kit-editor/issues/68
// and https://github.com/bustlelabs/content-kit-editor/issues/75
if (!marker.next && endsWith(text, SPACE)) {
text = text.substr(0, text.length - 1) + NO_BREAK_SPACE;
} else if (!marker.prev && startsWith(text, SPACE)) {
text = NO_BREAK_SPACE + text.substr(1);
}
let text = renderHTMLText(marker);

let textNode = document.createTextNode(text);
let currentElement = textNode;
Expand Down
27 changes: 27 additions & 0 deletions tests/acceptance/editor-selections-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ const { test, module } = Helpers;

let fixture, editor, editorElement;

const mobileDocWithSection = {
version: MOBILEDOC_VERSION,
sections: [
[],
[
[1, "P", [
[[], 0, "one trick pony"]
]]
]
]
};

const mobileDocWith2Sections = {
version: MOBILEDOC_VERSION,
sections: [
Expand Down Expand Up @@ -252,6 +264,21 @@ test('keystroke of printable character while text is selected deletes the text',
'updates the section');
});

test('selecting text bounded by space and typing replaces it', (assert) => {
editor = new Editor({mobiledoc: mobileDocWithSection});
editor.render(editorElement);

Helpers.dom.selectText('trick', editorElement);
Helpers.dom.insertText(editor, 'X');

assert.hasElement('#editor p:contains(one X pony)',
'new text present');

Helpers.dom.insertText(editor, 'Y');
assert.hasElement('#editor p:contains(one XY pony)',
'cursor positioned correctly');
});

test('selecting all text across sections and hitting enter deletes and moves cursor to empty section', (assert) => {
editor = new Editor({mobiledoc: mobileDocWith2Sections});
editor.render(editorElement);
Expand Down
39 changes: 37 additions & 2 deletions tests/unit/parsers/dom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import DOMParser from 'content-kit-editor/parsers/dom';
import PostNodeBuilder from 'content-kit-editor/models/post-node-builder';
import Helpers from '../../test-helpers';
import { Editor } from 'content-kit-editor';
import { NO_BREAK_SPACE } from 'content-kit-editor/renderers/editor-dom';

const {module, test} = Helpers;

Expand Down Expand Up @@ -50,8 +51,17 @@ test('#parse can parse multiple elements', (assert) => {
assert.equal(s2.markers.head.value, 'some other text');
});

test('#parse can parse spaces and breaking spaces', (assert) => {
let element = buildDOM("<p>some &nbsp;text &nbsp;&nbsp;for &nbsp; &nbsp;you</p>");

const post = parser.parse(element);
const s1 = post.sections.head;
assert.equal(s1.markers.length, 1, 's1 has 1 marker');
assert.equal(s1.markers.head.value, 'some text for you', 'has text');
});

test('editor#reparse catches changes to section', (assert) => {
const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) =>
const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) =>
post([
markupSection('p', [marker('the marker')])
])
Expand All @@ -75,8 +85,33 @@ test('editor#reparse catches changes to section', (assert) => {
assert.equal(section.text, 'the NEW marker');
});

test('editor#reparse parses spaces and breaking spaces', (assert) => {
const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) =>
post([
markupSection('p', [marker('the marker')])
])
);
editor = new Editor({mobiledoc});
const editorElement = $('<div id="editor"></div>')[0];
$('#qunit-fixture').append(editorElement);
editor.render(editorElement);

assert.hasElement('#editor p:contains(the marker)', 'precond - rendered correctly');

const p = $('#editor p:eq(0)')[0];
p.childNodes[0].textContent = `some ${NO_BREAK_SPACE}text ${NO_BREAK_SPACE}${NO_BREAK_SPACE}for ${NO_BREAK_SPACE} ${NO_BREAK_SPACE}you`;

// In Firefox, changing the text content changes the selection, so re-set it
Helpers.dom.moveCursorTo(p.childNodes[0]);

editor.reparse();

const section = editor.post.sections.head;
assert.equal(section.text, 'some text for you');
});

test('editor#reparse catches changes to list section', (assert) => {
const mobiledoc = Helpers.mobiledoc.build(({post, listSection, listItem, marker}) =>
const mobiledoc = Helpers.mobiledoc.build(({post, listSection, listItem, marker}) =>
post([
listSection('ul', [
listItem([marker('the list item')])
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/renderers/editor-dom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PostNodeBuilder from 'content-kit-editor/models/post-node-builder';
import Renderer from 'content-kit-editor/renderers/editor-dom';
import RenderTree from 'content-kit-editor/models/render-tree';
import Helpers from '../../test-helpers';
import { NO_BREAK_SPACE } from 'content-kit-editor/renderers/editor-dom';
const { module, test } = Helpers;

const DATA_URL = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=";
Expand Down Expand Up @@ -589,6 +590,20 @@ test('renders markup section "pull-quote" as <div class="pull-quote"></div>', (a
assert.equal(renderTree.rootElement.innerHTML, expectedDOM.outerHTML);
});

test('renders a bunch of spaces with nbsp', (assert) => {
const post = Helpers.postAbstract.build(({post, markupSection, marker}) => {
return post([markupSection('p', [marker('a b c d ')])]);
});
const renderTree = new RenderTree(post);
render(renderTree);

const expectedDOM = Helpers.dom.build(t => {
return t('p', {}, [t.text(`a b ${NO_BREAK_SPACE}c ${NO_BREAK_SPACE} ${NO_BREAK_SPACE}d${NO_BREAK_SPACE}`)]);
});

assert.equal(renderTree.rootElement.innerHTML, expectedDOM.outerHTML);
});

/*
test("It renders a renderTree with rendered dirty section", (assert) => {
/*
Expand Down