Skip to content

Commit

Permalink
Atom deletion with keystrokes
Browse files Browse the repository at this point in the history
  • Loading branch information
mixonic committed Feb 2, 2016
1 parent 568eef6 commit add705f
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 15 deletions.
32 changes: 24 additions & 8 deletions src/js/editor/post.js
Expand Up @@ -212,7 +212,10 @@ class PostEditor {
while (marker && marker.next) {
nextMarker = marker.next;

if (isArrayEqual(marker.markups, nextMarker.markups)) {
if (
marker.type === nextMarker.type &&
isArrayEqual(marker.markups, nextMarker.markups)
) {
nextMarker.value = marker.value + nextMarker.value;
this._markDirty(nextMarker);
this.removeMarker(marker);
Expand Down Expand Up @@ -450,6 +453,15 @@ class PostEditor {
return nextPosition;
}

/**
* delete 1 character forward from the markerPosition, which in turn is
* a {marker, offset} object.
*
* @method _deleteForwardFromMarkerPosition
* @param {Object} markerPosition {marker, offset}
* @return {Position} The position the cursor should be put after this deletion
* @private
*/
_deleteForwardFromMarkerPosition(markerPosition) {
const {marker, offset} = markerPosition;
const {section} = marker;
Expand All @@ -472,6 +484,8 @@ class PostEditor {
this.removeSection(nextSection);
}
}
} else if (marker.length === 1 && offset === 0) {
this.removeMarker(marker);
} else {
marker.deleteValueAtOffset(offset);
this._markDirty(marker);
Expand Down Expand Up @@ -501,22 +515,24 @@ class PostEditor {
}
}

let nextPosition = position.clone();

// if position is end of a card, replace the card with a markup section
if (section.isCardSection) {
let newSection = this.builder.createMarkupSection();
this.replaceSection(section, newSection);
return newSection.headPosition();
}

const { marker, offset:markerOffset } = position.markerPosition;

let nextPosition = position.moveLeft();

const { marker, offset:markerOffset } = position.markerPosition;
const offsetToDeleteAt = markerOffset - 1;

let lengthChange = marker.deleteValueAtOffset(offsetToDeleteAt);
nextPosition.offset -= lengthChange;
this._markDirty(marker);
if (marker.length === 1 && offsetToDeleteAt === 0) {
this.removeMarker(marker);
} else {
marker.deleteValueAtOffset(offsetToDeleteAt);
this._markDirty(marker);
}

return nextPosition;
}
Expand Down
4 changes: 2 additions & 2 deletions src/js/models/marker.js
Expand Up @@ -11,8 +11,8 @@ import assert from '../utils/assert';
// high- and low-surrogate characters.
// See "high surrogate" and "low surrogate" on
// https://en.wikipedia.org/wiki/Unicode_block
const HIGH_SURROGATE_RANGE = [0xD800, 0xDBFF];
const LOW_SURROGATE_RANGE = [0xDC00, 0xDFFF];
export const HIGH_SURROGATE_RANGE = [0xD800, 0xDBFF];
export const LOW_SURROGATE_RANGE = [0xDC00, 0xDFFF];

const Marker = class Marker extends LinkedItem {
constructor(value='', markups=[]) {
Expand Down
22 changes: 20 additions & 2 deletions src/js/utils/cursor/position.js
Expand Up @@ -3,6 +3,10 @@ import {
} from 'mobiledoc-kit/utils/dom-utils';
import { DIRECTION } from 'mobiledoc-kit/utils/key';
import assert from 'mobiledoc-kit/utils/assert';
import {
HIGH_SURROGATE_RANGE,
LOW_SURROGATE_RANGE
} from 'mobiledoc-kit/models/marker';

function findParentSectionFromNode(renderTree, node) {
let renderNode = renderTree.findRenderNodeFromElement(
Expand Down Expand Up @@ -134,7 +138,14 @@ const Position = class Position {
let prev = this.section.previousLeafSection();
return prev && prev.tailPosition();
} else {
return new Position(this.section, this.offset - 1);
let offset = this.offset - 1;
if (!this.section.isCardSection && this.marker.value) {
let code = this.marker.value.charCodeAt(offset);
if (code >= LOW_SURROGATE_RANGE[0] && code <= LOW_SURROGATE_RANGE[1]) {
offset = offset - 1;
}
}
return new Position(this.section, offset);
}
}

Expand All @@ -146,7 +157,14 @@ const Position = class Position {
let next = this.section.nextLeafSection();
return next && next.headPosition();
} else {
return new Position(this.section, this.offset + 1);
let offset = this.offset + 1;
if (!this.section.isCardSection && this.marker.value) {
let code = this.marker.value.charCodeAt(offset);
if (code >= HIGH_SURROGATE_RANGE[0] && code <= HIGH_SURROGATE_RANGE[1]) {
offset = offset + 1;
}
}
return new Position(this.section, offset);
}
}

Expand Down
50 changes: 50 additions & 0 deletions tests/acceptance/editor-atoms-test.js
Expand Up @@ -62,3 +62,53 @@ test('keystroke of character in section with atom keeps atom', (assert) => {

assert.hasElement(`#editor #simple-atom`, 'still has atom');
});

test('keystroke of delete removes character after atom', (assert) => {
editor = new Editor({mobiledoc: mobiledocWithAtom, atoms: [simpleAtom]});
editor.render(editorElement);

let pNode = $('#editor p')[0];
Helpers.dom.moveCursorTo(pNode.lastChild, 1);
Helpers.dom.triggerDelete(editor);

assert.postIsSimilar(editor.post, Helpers.postAbstract.build(
({post, markupSection, atom, marker}) => {
return post([markupSection('p', [
marker('text before atom'),
atom('simple-atom', 'Bob'),
marker('ext after atom')
])]);
}));
});

test('keystroke of delete removes atom', (assert) => {
editor = new Editor({mobiledoc: mobiledocWithAtom, atoms: [simpleAtom]});
editor.render(editorElement);

let pNode = $('#editor p')[0];
Helpers.dom.moveCursorTo(pNode.lastChild, 0);
Helpers.dom.triggerDelete(editor);

assert.postIsSimilar(editor.post, Helpers.postAbstract.build(
({post, markupSection, atom, marker}) => {
return post([markupSection('p', [
marker('text before atomtext after atom')
])]);
}));
});

test('keystroke of forward delete removes atom', (assert) => {
editor = new Editor({mobiledoc: mobiledocWithAtom, atoms: [simpleAtom]});
editor.render(editorElement);

let pNode = $('#editor p')[0];
Helpers.dom.moveCursorTo(pNode.firstChild, 16);
Helpers.dom.triggerForwardDelete(editor);

assert.postIsSimilar(editor.post, Helpers.postAbstract.build(
({post, markupSection, atom, marker}) => {
return post([markupSection('p', [
marker('text before atomtext after atom')
])]);
}));
});
4 changes: 2 additions & 2 deletions tests/acceptance/editor-sections-test.js
Expand Up @@ -276,7 +276,7 @@ test('keystroke of delete removes emoji character', (assert) => {
editor = new Editor({mobiledoc});
editor.render(editorElement);
let textNode = editorElement.firstChild. // section
firstChild; // marker
firstChild; // marker
assert.equal(textNode.textContent, monkey, 'precond - correct text');

Helpers.dom.moveCursorTo(textNode, monkey.length);
Expand All @@ -293,7 +293,7 @@ test('keystroke of forward delete removes emoji character', (assert) => {
editor = new Editor({mobiledoc});
editor.render(editorElement);
let textNode = editorElement.firstChild. // section
firstChild; // marker
firstChild; // marker
assert.equal(textNode.textContent, monkey, 'precond - correct text');

Helpers.dom.moveCursorTo(textNode, 'monkey'.length);
Expand Down
4 changes: 3 additions & 1 deletion tests/helpers/assertions.js
Expand Up @@ -10,7 +10,8 @@ import {
POST_TYPE,
LIST_ITEM_TYPE,
CARD_TYPE,
IMAGE_SECTION_TYPE
IMAGE_SECTION_TYPE,
ATOM_TYPE
} from 'mobiledoc-kit/models/types';

function comparePostNode(actual, expected, assert, path='root', deepCompare=false) {
Expand All @@ -36,6 +37,7 @@ function comparePostNode(actual, expected, assert, path='root', deepCompare=fals
});
}
break;
case ATOM_TYPE:
case MARKER_TYPE:
if (actual.value !== expected.value) {
assert.equal(actual.value, expected.value, `wrong value at ${path}`);
Expand Down

0 comments on commit add705f

Please sign in to comment.