Skip to content

Commit

Permalink
Add toRange and toPosition methods to Position, Section, Post
Browse files Browse the repository at this point in the history
  * Allow `Editor#selectRange`, `postEditor#setRange`,
  * `postEditor#toggleSection`, and `postEditor#toggleMarkup` to receive
  * Range *or* Position
  * Add `Position#toRange`, `Section#toRange` and `Post#toRange` methods
  * Add `toRange` util to convert range|position into range
  * Remove unused `Position#clone`, `Range.fromSection`
  * Add `section#toPosition(offset)`
  * Expose `Position` on global MobiledocKit export
  * docs: label `postEditor#setRange` public fixes #258
  • Loading branch information
bantic committed Aug 3, 2016
1 parent 2b65ad5 commit c7ec6eb
Show file tree
Hide file tree
Showing 27 changed files with 290 additions and 194 deletions.
8 changes: 4 additions & 4 deletions src/js/editor/edit-history.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import mobiledocParsers from 'mobiledoc-kit/parsers/mobiledoc';
import Range from 'mobiledoc-kit/utils/cursor/range';
import Position from 'mobiledoc-kit/utils/cursor/position';
import FixedQueue from 'mobiledoc-kit/utils/fixed-queue';

function findLeafSectionAtIndex(post, index) {
Expand Down Expand Up @@ -40,8 +38,10 @@ export class Snapshot {
let headSection = findLeafSectionAtIndex(post, headLeafSectionIndex);
let tailSection = findLeafSectionAtIndex(post, tailLeafSectionIndex);

return new Range(new Position(headSection, headOffset),
new Position(tailSection, tailOffset));
head = headSection.toPosition(headOffset);
tail = tailSection.toPosition(tailOffset);

return head.toRange(tail);
}
}
}
Expand Down
31 changes: 18 additions & 13 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import HTMLRenderer from 'mobiledoc-html-renderer';
import TextRenderer from 'mobiledoc-text-renderer';
import LifecycleCallbacks from 'mobiledoc-kit/models/lifecycle-callbacks';
import LogManager from 'mobiledoc-kit/utils/log-manager';
import toRange from 'mobiledoc-kit/utils/to-range';
import MobiledocError from 'mobiledoc-kit/utils/mobiledoc-error';

export const EDITOR_ELEMENT_CLASS_NAME = '__mobiledoc-editor';

Expand All @@ -44,10 +46,10 @@ const defaults = {
atoms: [],
cardOptions: {},
unknownCardHandler: ({env}) => {
throw new Error(`Unknown card encountered: ${env.name}`);
throw new MobiledocError(`Unknown card encountered: ${env.name}`);
},
unknownAtomHandler: ({env}) => {
throw new Error(`Unknown atom encountered: ${env.name}`);
throw new MobiledocError(`Unknown atom encountered: ${env.name}`);
},
mobiledoc: null,
html: null
Expand Down Expand Up @@ -249,7 +251,7 @@ class Editor {
this._eventManager.init();

if (this.autofocus) {
this.selectRange(new Range(this.post.headPosition()));
this.selectRange(this.post.headPosition());
}
}

Expand Down Expand Up @@ -286,19 +288,20 @@ class Editor {
deleteAtPosition(position, direction, {unit}) {
this.run(postEditor => {
let nextPosition = postEditor.deleteAtPosition(position, direction, {unit});
postEditor.setRange(new Range(nextPosition));
postEditor.setRange(nextPosition);
});
}

/**
* Convenience for {@link PostEditor#deleteRange}. Deletes and puts the
* cursor in the new position.
* @param {Range} range
* @public
*/
deleteRange(range) {
this.run(postEditor => {
let nextPosition = postEditor.deleteRange(range);
postEditor.setRange(new Range(nextPosition));
postEditor.setRange(nextPosition);
});
}

Expand Down Expand Up @@ -327,12 +330,12 @@ class Editor {
let nextPosition = postEditor.deleteRange(range);
cursorSection = nextPosition.section;
if (cursorSection && cursorSection.isBlank) {
postEditor.setRange(new Range(cursorSection.headPosition()));
postEditor.setRange(cursorSection.headPosition());
return;
}
}
cursorSection = postEditor.splitSection(range.head)[1];
postEditor.setRange(new Range(cursorSection.headPosition()));
postEditor.setRange(cursorSection.headPosition());
});
}

Expand All @@ -350,12 +353,14 @@ class Editor {
}

/**
* Selects the given range. If range is collapsed, this positions the cursor
* at the range's position, otherwise a selection is created in the editor
* surface.
* @param {Range}
* Selects the given range or position. If given a collapsed range or a position, this positions the cursor
* at the range's position. Otherwise a selection is created in the editor
* surface encompassing the range.
* @param {Range|Position} range
*/
selectRange(range) {
range = toRange(range);

this.cursor.selectRange(range);
this.range = range;
}
Expand Down Expand Up @@ -766,7 +771,7 @@ class Editor {
this.run(postEditor => {
const section = postEditor.builder.createMarkupSection('p');
postEditor.insertSectionBefore(this.post.sections, section);
postEditor.setRange(Range.fromSection(section));
postEditor.setRange(section.toRange());
});
}

Expand Down Expand Up @@ -964,7 +969,7 @@ class Editor {
// will cause an unexpected DOM mutation (which can wipe out the
// card).
// See: https://github.com/bustlelabs/mobiledoc-kit/issues/286
postEditor.setRange(new Range(card.tailPosition()));
postEditor.setRange(card.tailPosition());
});
return card;
}
Expand Down
5 changes: 2 additions & 3 deletions src/js/editor/event-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
setClipboardData,
parsePostFromDrop
} from 'mobiledoc-kit/utils/parse-utils';
import Range from 'mobiledoc-kit/utils/cursor/range';
import { filter, forEach } from 'mobiledoc-kit/utils/array-utils';
import Key from 'mobiledoc-kit/utils/key';
import { TAB } from 'mobiledoc-kit/utils/characters';
Expand Down Expand Up @@ -224,7 +223,7 @@ export default class EventManager {

editor.run(postEditor => {
let nextPosition = postEditor.insertPost(position, pastedPost);
postEditor.setRange(new Range(nextPosition));
postEditor.setRange(nextPosition);
});
}

Expand All @@ -248,7 +247,7 @@ export default class EventManager {

editor.run(postEditor => {
let nextPosition = postEditor.insertPost(position, post);
postEditor.setRange(new Range(nextPosition));
postEditor.setRange(nextPosition);
});
}

Expand Down
13 changes: 6 additions & 7 deletions src/js/editor/key-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,26 @@ import Key from '../utils/key';
import { MODIFIERS, SPECIAL_KEYS } from '../utils/key';
import { filter, reduce } from '../utils/array-utils';
import assert from '../utils/assert';
import Range from '../utils/cursor/range';
import Browser from '../utils/browser';

function selectAll(editor) {
let { post } = editor;
let allRange = new Range(post.headPosition(), post.tailPosition());
editor.selectRange(allRange);
editor.selectRange(post.toRange());
}

function gotoStartOfLine(editor) {
let {range} = editor;
let {tail: {section}} = range;
editor.run(postEditor => {
postEditor.setRange(new Range(section.headPosition()));
postEditor.setRange(section.headPosition());
});
}

function gotoEndOfLine(editor) {
let {range} = editor;
let {tail: {section}} = range;
editor.run(postEditor => {
postEditor.setRange(new Range(section.tailPosition()));
postEditor.setRange(section.tailPosition());
});
}

Expand Down Expand Up @@ -52,11 +50,12 @@ export const DEFAULT_KEY_COMMANDS = [{
run(editor) {
let { range } = editor;
if (range.isCollapsed) {
range = new Range(range.head, range.head.section.tailPosition());
let { head, head: { section } } = range;
range = head.toRange(section.tailPosition());
}
editor.run(postEditor => {
let nextPosition = postEditor.deleteRange(range);
postEditor.setRange(new Range(nextPosition));
postEditor.setRange(nextPosition);
});
}
}, {
Expand Down
68 changes: 48 additions & 20 deletions src/js/editor/post.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Position from '../utils/cursor/position';
import Range from 'mobiledoc-kit/utils/cursor/range';
import { forEach, reduce, filter, values, commonItems } from '../utils/array-utils';
import { DIRECTION } from '../utils/key';
import LifecycleCallbacks from '../models/lifecycle-callbacks';
import assert from '../utils/assert';
import { normalizeTagName } from '../utils/dom-utils';
import Range from '../utils/cursor/range';
import PostInserter from './post/post-inserter';
import deprecate from 'mobiledoc-kit/utils/deprecate';
import toRange from 'mobiledoc-kit/utils/to-range';

const { FORWARD, BACKWARD } = DIRECTION;

function isListSectionTagName(tagName) {
return tagName === 'ul' || tagName === 'ol';
Expand Down Expand Up @@ -64,7 +67,27 @@ class PostEditor {
this._range = this.editor.range;
}

/**
* Schedules to select the given range on the editor after the postEditor
* has completed its work. This also updates the postEditor's active range
* (so that multiple calls to range-changing methods on the postEditor will
* update the correct range).
*
* Usage:
* let range = editor.range;
* editor.run(postEditor => {
* let nextPosition = postEditor.deleteRange(range);
*
* // Will position the editor's cursor at `nextPosition` after
* // the postEditor finishes work and the editor rerenders.
* postEditor.setRange(nextPosition);
* });
* @param {Range|Position} range
* @public
*/
setRange(range) {
range = toRange(range);

// TODO validate that the range is valid
// (does not contain marked-for-removal head or tail sections?)
this._range = range;
Expand All @@ -79,14 +102,16 @@ class PostEditor {
* let { range } = editor;
* editor.run((postEditor) => {
* let nextPosition = postEditor.deleteRange(range);
* postEditor.setRange(new Range(nextPosition));
* postEditor.setRange(nextPosition);
* });
* ```
* @param {Range} range Cursor Range object with head and tail Positions
* @return {Position} The position where the cursor would go after deletion
* @public
*/
deleteRange(range) {
assert("Must pass MobiledocKit Range to `deleteRange`", range instanceof Range);

let {
head, head: {section: headSection},
tail, tail: {section: tailSection}
Expand Down Expand Up @@ -174,7 +199,7 @@ class PostEditor {
}
}

let range = new Range(head, tail);
let range = head.toRange(tail);
this.splitMarkers(range).forEach(m => this.removeMarker(m));

return head;
Expand Down Expand Up @@ -285,13 +310,13 @@ class PostEditor {
}
this._joinListSections(list, listSection);
if (prevPosition) {
updatedHead = prevPosition.moveRight();
updatedHead = prevPosition.move(FORWARD);
}
});
});

if (updatedHead) {
this.setRange(new Range(updatedHead, updatedHead, range.direction));
this.setRange(updatedHead);
}
}

Expand Down Expand Up @@ -361,18 +386,18 @@ class PostEditor {

_deleteAtPositionBackward(position, unit) {
if (position.isHead() && position.section.isListItem) {
this.toggleSection('p', new Range(position));
this.toggleSection('p', position);
return this._range.head;
} else {
let prevPosition = unit === 'word' ? position.moveWord(-1) : position.move(-1);
let range = new Range(prevPosition, position);
let prevPosition = unit === 'word' ? position.moveWord(BACKWARD) : position.move(BACKWARD);
let range = prevPosition.toRange(position);
return this.deleteRange(range);
}
}

_deleteAtPositionForward(position, unit) {
let nextPosition = unit === 'word' ? position.moveWord(1) : position.move(1);
let range = new Range(position, nextPosition);
let nextPosition = unit === 'word' ? position.moveWord(FORWARD) : position.move(FORWARD);
let range = position.toRange(nextPosition);
return this.deleteRange(range);
}

Expand Down Expand Up @@ -566,8 +591,8 @@ class PostEditor {
this._coalesceMarkers(section);
this._markDirty(section);

let nextPosition = new Position(position.section, offset);
this.setRange(new Range(nextPosition));
let nextPosition = section.toPosition(offset);
this.setRange(nextPosition);
return nextPosition;
}

Expand Down Expand Up @@ -694,9 +719,9 @@ class PostEditor {
}

/**
* Toggle the given markup on the current selection. If anything in the current
* selection has the markup, the markup will be removed from it. If nothing in the selection
* has the markup, the markup will be added to everything in the selection.
* Toggle the given markup in the given range (or at the position given). If the range/position
* has the markup, the markup will be removed. If nothing in the range/position
* has the markup, the markup will be added to everything in the range/position.
*
* Usage:
* ```
Expand All @@ -713,10 +738,11 @@ class PostEditor {
* @param {Markup|String} markupOrString Either a markup object created using
* the builder (useful when adding a markup with attributes, like an 'a' markup),
* or, if a string, the tag name of the markup (e.g. 'strong', 'em') to toggle.
* @param {Range} range in which to toggle, defaults to current editor range
* @param {Range|Position} range in which to toggle. Defaults to current editor range.
* @public
*/
toggleMarkup(markupOrMarkupString, range=this._range) {
range = toRange(range);
const markup = typeof markupOrMarkupString === 'string' ?
this.builder.createMarkup(markupOrMarkupString) :
markupOrMarkupString;
Expand All @@ -735,17 +761,19 @@ class PostEditor {
}

/**
* Toggles the tagName of the active section or sections.
* Toggles the tagName of the active section or sections in the given range/position.
* If every section has the tag name, they will all be reset to default sections.
* Otherwise, every section will be changed to the requested type
*
* @param {String} sectionTagName A valid markup section or
* list section tag name (e.g. 'blockquote', 'h2', 'ul')
* @param {Range} range The range over which to toggle.
* Defaults to the current editor's offsets
* @param {Range|Position} range The range over which to toggle.
* Defaults to the current editor range.
* @public
*/
toggleSection(sectionTagName, range=this._range) {
range = toRange(range);

sectionTagName = normalizeTagName(sectionTagName);
let { post } = this.editor;
let nextRange = range;
Expand All @@ -765,7 +793,7 @@ class PostEditor {
});

if (firstChanged) {
nextRange = new Range(firstChanged.headPosition());
nextRange = firstChanged.headPosition().toRange();
}
this.setRange(nextRange);
}
Expand Down
3 changes: 1 addition & 2 deletions src/js/editor/post/post-inserter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
IMAGE_SECTION_TYPE,
LIST_ITEM_TYPE,
} from 'mobiledoc-kit/models/types';
import Range from 'mobiledoc-kit/utils/cursor/range';

const MARKERABLE = 'markerable',
NESTED_MARKERABLE = 'nested_markerable',
Expand All @@ -30,7 +29,7 @@ class Visitor {

set cursorPosition(position) {
this._cursorPosition = position;
this.postEditor.setRange(new Range(position));
this.postEditor.setRange(position);
}

visit(node) {
Expand Down
Loading

0 comments on commit c7ec6eb

Please sign in to comment.