From 74cab690e3c541b765050a20aa54040f0da3d482 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Wed, 22 Jun 2016 18:42:49 -0700 Subject: [PATCH] broke lots of stuff, but added tests --- Makefile | 18 +- examples/auto-markdown/index.js | 13 +- examples/links/index.css | 48 ++ examples/links/index.html | 12 + examples/links/index.js | 159 ++++ examples/links/state.json | 57 ++ examples/table/index.html | 2 +- lib/components/content.js | 2 +- lib/index.js | 1 + lib/models/node.js | 795 +++++++++++++----- lib/models/selection.js | 34 +- lib/models/state.js | 97 ++- lib/models/transform.js | 24 +- lib/plugins/core.js | 3 +- lib/serializers/raw.js | 32 +- lib/utils/environment.js | 34 +- package.json | 4 +- test/{browser.js => browser/index.js} | 0 test/{ => browser}/support/browser.html | 0 test/{ => browser}/support/mocha.css | 0 test/{ => browser}/support/mocha.js | 0 test/helpers/assert-json.js | 185 ++++ test/server.js | 11 - test/server/index.js | 2 + .../delete-at-range/first-character/index.js | 17 + .../first-character/input.yaml | 8 + .../first-character/output.yaml | 8 + .../join-blocks-and-trim/index.js | 18 + .../join-blocks-and-trim/input.yaml | 14 + .../join-blocks-and-trim/output.yaml | 8 + .../delete-at-range/join-blocks/index.js | 18 + .../delete-at-range/join-blocks/input.yaml | 14 + .../delete-at-range/join-blocks/output.yaml | 8 + .../delete-at-range/last-character/index.js | 17 + .../delete-at-range/last-character/input.yaml | 8 + .../last-character/output.yaml | 8 + .../delete-at-range/middle-character/index.js | 17 + .../middle-character/input.yaml | 8 + .../middle-character/output.yaml | 8 + .../delete-at-range/whole-word/index.js | 17 + .../delete-at-range/whole-word/input.yaml | 8 + .../delete-at-range/whole-word/output.yaml | 8 + .../first-character/index.js | 17 + .../first-character/input.yaml | 8 + .../first-character/output.yaml | 8 + .../join-blocks/index.js | 17 + .../join-blocks/input.yaml | 14 + .../join-blocks/output.yaml | 8 + .../last-character/index.js | 17 + .../last-character/input.yaml | 8 + .../last-character/output.yaml | 8 + .../middle-character/index.js | 17 + .../middle-character/input.yaml | 8 + .../middle-character/output.yaml | 8 + .../start-of-document/index.js | 17 + .../start-of-document/input.yaml | 8 + .../start-of-document/output.yaml | 8 + .../end-of-document/index.js | 17 + .../end-of-document/input.yaml | 8 + .../end-of-document/output.yaml | 8 + .../first-character/index.js | 17 + .../first-character/input.yaml | 8 + .../first-character/output.yaml | 8 + .../join-blocks/index.js | 17 + .../join-blocks/input.yaml | 14 + .../join-blocks/output.yaml | 8 + .../last-character/index.js | 17 + .../last-character/input.yaml | 8 + .../last-character/output.yaml | 8 + .../middle-character/index.js | 17 + .../middle-character/input.yaml | 8 + .../middle-character/output.yaml | 8 + .../insert-text-at-range/after-mark/index.js | 17 + .../after-mark/input.yaml | 12 + .../after-mark/output.yaml | 12 + .../insert-text-at-range/before-mark/index.js | 17 + .../before-mark/input.yaml | 12 + .../before-mark/output.yaml | 12 + .../insert-text-at-range/during-mark/index.js | 17 + .../during-mark/input.yaml | 12 + .../during-mark/output.yaml | 12 + .../first-character/index.js | 17 + .../first-character/input.yaml | 8 + .../first-character/output.yaml | 8 + .../insert-text-at-range/first-space/index.js | 17 + .../first-space/input.yaml | 8 + .../first-space/output.yaml | 8 + .../insert-text-at-range/first-words/index.js | 17 + .../first-words/input.yaml | 8 + .../first-words/output.yaml | 8 + .../last-character/index.js | 17 + .../last-character/input.yaml | 8 + .../last-character/output.yaml | 8 + .../insert-text-at-range/last-space/index.js | 17 + .../last-space/input.yaml | 8 + .../last-space/output.yaml | 8 + .../insert-text-at-range/last-words/index.js | 17 + .../last-words/input.yaml | 8 + .../last-words/output.yaml | 8 + .../middle-character/index.js | 17 + .../middle-character/input.yaml | 8 + .../middle-character/output.yaml | 8 + .../middle-space/index.js | 17 + .../middle-space/input.yaml | 8 + .../middle-space/output.yaml | 8 + .../middle-words/index.js | 17 + .../middle-words/input.yaml | 8 + .../middle-words/output.yaml | 8 + .../mark-at-range/across-blocks/index.js | 18 + .../mark-at-range/across-blocks/input.yaml | 14 + .../mark-at-range/across-blocks/output.yaml | 20 + .../mark-at-range/across-inlines/index.js | 18 + .../mark-at-range/across-inlines/input.yaml | 20 + .../mark-at-range/across-inlines/output.yaml | 26 + .../mark-at-range/existing-marks/index.js | 17 + .../mark-at-range/existing-marks/input.yaml | 10 + .../mark-at-range/existing-marks/output.yaml | 14 + .../mark-at-range/first-character/index.js | 17 + .../mark-at-range/first-character/input.yaml | 8 + .../mark-at-range/first-character/output.yaml | 11 + .../mark-at-range/last-character/index.js | 17 + .../mark-at-range/last-character/input.yaml | 8 + .../mark-at-range/last-character/output.yaml | 11 + .../mark-at-range/middle-character/index.js | 17 + .../mark-at-range/middle-character/input.yaml | 8 + .../middle-character/output.yaml | 12 + .../mark-at-range/whole-word/index.js | 17 + .../mark-at-range/whole-word/input.yaml | 8 + .../mark-at-range/whole-word/output.yaml | 10 + .../mark-at-range/with-data-object/index.js | 19 + .../mark-at-range/with-data-object/input.yaml | 8 + .../with-data-object/output.yaml | 11 + .../fixtures/mark-at-range/with-data/index.js | 19 + .../mark-at-range/with-data/input.yaml | 8 + .../mark-at-range/with-data/output.yaml | 11 + .../set-block-at-range/across-blocks/index.js | 18 + .../across-blocks/input.yaml | 14 + .../across-blocks/output.yaml | 14 + .../across-inlines/index.js | 18 + .../across-inlines/input.yaml | 20 + .../across-inlines/output.yaml | 20 + .../set-block-at-range/data-only/index.js | 19 + .../set-block-at-range/data-only/input.yaml | 8 + .../set-block-at-range/data-only/output.yaml | 10 + .../set-block-at-range/nested-block/index.js | 17 + .../nested-block/input.yaml | 11 + .../nested-block/output.yaml | 11 + .../set-block-at-range/single-block/index.js | 17 + .../single-block/input.yaml | 8 + .../single-block/output.yaml | 8 + .../set-block-at-range/with-data/index.js | 19 + .../set-block-at-range/with-data/input.yaml | 8 + .../set-block-at-range/with-data/output.yaml | 10 + .../across-inlines/index.js | 18 + .../across-inlines/input.yaml | 20 + .../across-inlines/output.yaml | 20 + .../set-inline-at-range/data-only/index.js | 19 + .../set-inline-at-range/data-only/input.yaml | 11 + .../set-inline-at-range/data-only/output.yaml | 13 + .../nested-inline/index.js | 17 + .../nested-inline/input.yaml | 14 + .../nested-inline/output.yaml | 14 + .../single-inline/index.js | 17 + .../single-inline/input.yaml | 11 + .../single-inline/output.yaml | 11 + .../set-inline-at-range/with-data/index.js | 19 + .../set-inline-at-range/with-data/input.yaml | 11 + .../set-inline-at-range/with-data/output.yaml | 13 + .../split-block-at-range/block-end/index.js | 17 + .../split-block-at-range/block-end/input.yaml | 8 + .../block-end/output.yaml | 14 + .../block-middle/index.js | 17 + .../block-middle/input.yaml | 8 + .../block-middle/output.yaml | 14 + .../split-block-at-range/block-start/index.js | 17 + .../block-start/input.yaml | 8 + .../block-start/output.yaml | 14 + .../with-delete-across-blocks/index.js | 18 + .../with-delete-across-blocks/input.yaml | 14 + .../with-delete-across-blocks/output.yaml | 14 + .../split-block-at-range/with-delete/index.js | 17 + .../with-delete/input.yaml | 8 + .../with-delete/output.yaml | 14 + .../unmark-at-range/across-blocks/index.js | 18 + .../unmark-at-range/across-blocks/input.yaml | 18 + .../unmark-at-range/across-blocks/output.yaml | 20 + .../unmark-at-range/across-inlines/index.js | 18 + .../unmark-at-range/across-inlines/input.yaml | 24 + .../across-inlines/output.yaml | 26 + .../unmark-at-range/existing-marks/index.js | 17 + .../unmark-at-range/existing-marks/input.yaml | 11 + .../existing-marks/output.yaml | 14 + .../unmark-at-range/first-character/index.js | 17 + .../first-character/input.yaml | 10 + .../first-character/output.yaml | 11 + .../unmark-at-range/last-character/index.js | 17 + .../unmark-at-range/last-character/input.yaml | 10 + .../last-character/output.yaml | 11 + .../unmark-at-range/middle-character/index.js | 17 + .../middle-character/input.yaml | 10 + .../middle-character/output.yaml | 14 + .../unmark-at-range/whole-word/index.js | 17 + .../unmark-at-range/whole-word/input.yaml | 10 + .../unmark-at-range/whole-word/output.yaml | 8 + .../across-blocks/index.js | 18 + .../across-blocks/input.yaml | 17 + .../across-blocks/output.yaml | 14 + .../across-inlines/index.js | 18 + .../across-inlines/input.yaml | 20 + .../across-inlines/output.yaml | 23 + .../nested-block/index.js | 17 + .../nested-block/input.yaml | 11 + .../nested-block/output.yaml | 14 + .../single-block/index.js | 17 + .../single-block/input.yaml | 8 + .../single-block/output.yaml | 11 + .../with-data-object/index.js | 17 + .../with-data-object/input.yaml | 8 + .../with-data-object/output.yaml | 13 + .../unwrap-block-at-range/with-data/index.js | 19 + .../with-data/input.yaml | 8 + .../with-data/output.yaml | 13 + .../across-blocks/index.js | 18 + .../across-blocks/input.yaml | 14 + .../across-blocks/output.yaml | 17 + .../across-inlines/index.js | 18 + .../across-inlines/input.yaml | 23 + .../across-inlines/output.yaml | 20 + .../wrap-block-at-range/nested-block/index.js | 17 + .../nested-block/input.yaml | 14 + .../nested-block/output.yaml | 11 + .../wrap-block-at-range/single-block/index.js | 17 + .../single-block/input.yaml | 11 + .../single-block/output.yaml | 8 + .../with-data-object/index.js | 17 + .../with-data-object/input.yaml | 18 + .../with-data-object/output.yaml | 13 + .../wrap-block-at-range/with-data/index.js | 19 + .../wrap-block-at-range/with-data/input.yaml | 18 + .../wrap-block-at-range/with-data/output.yaml | 13 + test/server/transforms/index.js | 38 + 241 files changed, 4149 insertions(+), 323 deletions(-) create mode 100644 examples/links/index.css create mode 100644 examples/links/index.html create mode 100644 examples/links/index.js create mode 100644 examples/links/state.json rename test/{browser.js => browser/index.js} (100%) rename test/{ => browser}/support/browser.html (100%) rename test/{ => browser}/support/mocha.css (100%) rename test/{ => browser}/support/mocha.js (100%) create mode 100644 test/helpers/assert-json.js delete mode 100644 test/server.js create mode 100644 test/server/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/output.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/join-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/whole-word/index.js create mode 100644 test/server/transforms/fixtures/delete-at-range/whole-word/input.yaml create mode 100644 test/server/transforms/fixtures/delete-at-range/whole-word/output.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/join-blocks/index.js create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/join-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/join-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/start-of-document/index.js create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/start-of-document/input.yaml create mode 100644 test/server/transforms/fixtures/delete-backward-at-range/start-of-document/output.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/end-of-document/index.js create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/end-of-document/input.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/end-of-document/output.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/join-blocks/index.js create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/join-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/join-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/delete-forward-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/after-mark/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/after-mark/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/after-mark/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/before-mark/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/before-mark/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/before-mark/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/during-mark/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/during-mark/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/during-mark/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-space/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-space/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-space/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-words/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-words/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/first-words/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-space/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-space/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-space/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-words/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-words/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/last-words/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-space/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-space/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-space/output.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-words/index.js create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-words/input.yaml create mode 100644 test/server/transforms/fixtures/insert-text-at-range/middle-words/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/across-blocks/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/existing-marks/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/existing-marks/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/existing-marks/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/whole-word/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/whole-word/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/whole-word/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data-object/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data-object/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data-object/output.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data/index.js create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data/input.yaml create mode 100644 test/server/transforms/fixtures/mark-at-range/with-data/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-blocks/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/data-only/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/data-only/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/data-only/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/nested-block/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/nested-block/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/nested-block/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/single-block/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/single-block/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/single-block/output.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/with-data/index.js create mode 100644 test/server/transforms/fixtures/set-block-at-range/with-data/input.yaml create mode 100644 test/server/transforms/fixtures/set-block-at-range/with-data/output.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/set-inline-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/data-only/index.js create mode 100644 test/server/transforms/fixtures/set-inline-at-range/data-only/input.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/data-only/output.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/nested-inline/index.js create mode 100644 test/server/transforms/fixtures/set-inline-at-range/nested-inline/input.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/nested-inline/output.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/single-inline/index.js create mode 100644 test/server/transforms/fixtures/set-inline-at-range/single-inline/input.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/single-inline/output.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/with-data/index.js create mode 100644 test/server/transforms/fixtures/set-inline-at-range/with-data/input.yaml create mode 100644 test/server/transforms/fixtures/set-inline-at-range/with-data/output.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-end/index.js create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-end/input.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-end/output.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-middle/index.js create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-middle/input.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-middle/output.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-start/index.js create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-start/input.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/block-start/output.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/index.js create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete/index.js create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete/input.yaml create mode 100644 test/server/transforms/fixtures/split-block-at-range/with-delete/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-blocks/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/existing-marks/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/existing-marks/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/existing-marks/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/first-character/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/first-character/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/first-character/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/last-character/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/last-character/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/last-character/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/middle-character/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/middle-character/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/middle-character/output.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/whole-word/index.js create mode 100644 test/server/transforms/fixtures/unmark-at-range/whole-word/input.yaml create mode 100644 test/server/transforms/fixtures/unmark-at-range/whole-word/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/nested-block/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/nested-block/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/nested-block/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/single-block/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/single-block/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/single-block/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/output.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data/index.js create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data/input.yaml create mode 100644 test/server/transforms/fixtures/unwrap-block-at-range/with-data/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-blocks/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-blocks/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-blocks/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-inlines/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-inlines/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/across-inlines/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/nested-block/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/nested-block/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/nested-block/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/single-block/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/single-block/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/single-block/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data-object/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data-object/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data-object/output.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data/index.js create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data/input.yaml create mode 100644 test/server/transforms/fixtures/wrap-block-at-range/with-data/output.yaml create mode 100644 test/server/transforms/index.js diff --git a/Makefile b/Makefile index e3c666ad0a..c96626153d 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ dist: $(shell find ./lib) example-auto-markdown: @ $(browserify) --debug --transform babelify --outfile ./examples/auto-markdown/build.js ./examples/auto-markdown/index.js +# Build the links example. +example-links: + @ $(browserify) --debug --transform babelify --outfile ./examples/links/build.js ./examples/links/index.js + # Build the plain-text example. example-plain-text: @ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js @@ -52,24 +56,28 @@ lint: @ $(standard) ./lib # Build the test source. -test/support/build.js: $(shell find ./lib) ./test/browser.js - @ $(browserify) --debug --transform babelify --outfile ./test/support/build.js ./test/browser.js +test/browser/support/build.js: $(shell find ./lib) ./test/browser/index.js + @ $(browserify) --debug --transform babelify --outfile ./test/browser/support/build.js ./test/browser/index.js # Run the tests. test: test-browser test-server # Run the browser-side tests. -test-browser: ./test/support/build.js - @ $(mocha-phantomjs) --reporter spec --timeout 5000 ./test/support/browser.html +test-browser: ./test/browser/support/build.js + @ $(mocha-phantomjs) --reporter spec --timeout 5000 ./test/browser/support/browser.html # Run the server-side tests. test-server: - @ $(mocha) --reporter spec --timeout 5000 ./test/server.js + @ $(mocha) --compilers js:babel-core/register --reporter spec --timeout 5000 ./test/server # Watch the auto-markdown example. watch-example-auto-markdown: @ $(MAKE) example-auto-markdown browserify=$(watchify) +# Watch the links example. +watch-example-links: + @ $(MAKE) example-links browserify=$(watchify) + # Watch the plain-text example. watch-example-plain-text: @ $(MAKE) example-plain-text browserify=$(watchify) diff --git a/examples/auto-markdown/index.js b/examples/auto-markdown/index.js index b060b43f44..562118d444 100644 --- a/examples/auto-markdown/index.js +++ b/examples/auto-markdown/index.js @@ -24,6 +24,7 @@ class App extends React.Component { }; /** + * * Render the example. * * @return {Component} component @@ -153,12 +154,10 @@ class App extends React.Component { case '*': case '-': case '+': - if (node.type == 'list-item') break - transform = node.type == 'list-item' - ? transform - : transform - .setType('list-item') - .wrap('bulleted-list') + if (node.type == 'list-item') return + transform = transform + .setType('list-item') + .wrapBlock('bulleted-list') break default: return @@ -196,7 +195,7 @@ class App extends React.Component { .transform() .setType('paragraph') - if (node.type == 'list-item') transform = transform.unwrap('bulleted-list') + if (node.type == 'list-item') transform = transform.unwrapBlock('bulleted-list') state = transform.apply() return state diff --git a/examples/links/index.css b/examples/links/index.css new file mode 100644 index 0000000000..6f22e194a4 --- /dev/null +++ b/examples/links/index.css @@ -0,0 +1,48 @@ + +html { + background: #eee; + padding: 20px; +} + +main { + background: #fff; + padding: 10px; + max-width: 40em; + margin: 0 auto; +} + +p { + margin: 0; +} + +.editor > * > * + * { + margin-top: 1em; +} + +.menu { + margin: 0 -10px; + padding: 1px 0 9px 8px; + border-bottom: 2px solid #eee; + margin-bottom: 10px; +} + +.menu > * { + display: inline-block; +} + +.menu > * + * { + margin-left: 10px; +} + +.button { + color: #ccc; + cursor: pointer; +} + +.button[data-active="true"] { + color: black; +} + +.material-icons { + font-size: 18px; +} diff --git a/examples/links/index.html b/examples/links/index.html new file mode 100644 index 0000000000..8fe25ddcd6 --- /dev/null +++ b/examples/links/index.html @@ -0,0 +1,12 @@ + + + + Editor | Links Example + + + + +
+ + + diff --git a/examples/links/index.js b/examples/links/index.js new file mode 100644 index 0000000000..0ff9d8ab94 --- /dev/null +++ b/examples/links/index.js @@ -0,0 +1,159 @@ + +import Editor, { Mark, Raw } from '../..' +import React from 'react' +import ReactDOM from 'react-dom' +import state from './state.json' +import { Map } from 'immutable' + +/** + * App. + */ + +class App extends React.Component { + + state = { + state: Raw.deserialize(state) + }; + + /** + * Check whether the current selection has a link in it. + * + * @return {Boolean} hasLinks + */ + + hasLinks() { + let { state } = this.state + const { currentInlineNodes } = state + const hasLinks = currentInlineNodes.some(inline => inline.type == 'link') + return hasLinks + } + + /** + * When clicking a link, if the selection has a link in it, remove the link. + * Otherwise, add a new link with an href and text. + * + * @param {Event} e + */ + + onClickLink(e) { + e.preventDefault() + let { state } = this.state + const hasLinks = this.hasLinks() + + if (hasLinks) { + state = state + .transform() + .unwrapInline('link') + .apply() + } + + else if (state.isCurrentlyExpanded) { + // const href = window.prompt('Enter the URL of the link:') + state = state + .transform() + .wrapInline('link', new Map({ href: 'https://google.com' })) + .apply() + } + + else { + const href = window.prompt('Enter the URL of the link:') + const text = window.prompt('Enter the text for the link:') + state = state + .transform() + .insertText(text) + .extendBackward(text.length) + .wrapInline('link', new Map({ href })) + .apply() + } + + this.setState({ state }) + } + + /** + * Render the app. + * + * @return {Component} component + */ + + render() { + return ( +
+ {this.renderToolbar()} + {this.renderEditor()} +
+ ) + } + + /** + * Render the toolbar. + * + * @return {Component} component + */ + + renderToolbar() { + const hasLinks = this.hasLinks() + return ( +
+ this.onClickLink(e)} data-active={hasLinks}> + link + +
+ ) + } + + /** + * Render the editor. + * + * @return {Component} component + */ + + renderEditor() { + return ( +
+ this.renderNode(node)} + onChange={(state) => { + console.groupCollapsed('Change!') + console.log('Document:', state.document.toJS()) + console.log('Selection:', state.selection.toJS()) + console.log('Content:', Raw.serialize(state)) + console.groupEnd() + this.setState({ state }) + }} + /> +
+ ) + } + + /** + * Render our custom `node`. + * + * @param {Node} node + * @return {Component} component + */ + + renderNode(node) { + switch (node.type) { + case 'link': { + return (props) => { + const { data } = props.node + const href = data.get('href') + return {props.children} + } + } + case 'paragraph': { + return (props) =>

{props.children}

+ } + } + } + +} + +/** + * Attach. + */ + +const app = +const root = document.body.querySelector('main') +ReactDOM.render(app, root) diff --git a/examples/links/state.json b/examples/links/state.json new file mode 100644 index 0000000000..812b3e2859 --- /dev/null +++ b/examples/links/state.json @@ -0,0 +1,57 @@ +{ + "nodes": [ + { + "kind": "block", + "type": "paragraph", + "nodes": [ + { + "kind": "text", + "ranges": [ + { + "text": "In addition to block nodes, you can create inline nodes, like " + }, + ] + }, + { + "kind": "inline", + "type": "link", + "data": { + "href": "https://en.wikipedia.org/wiki/Hypertext" + }, + "nodes": [ + { + "kind": "text", + "ranges": [ + { + "text": "hyperlinks" + }, + ] + }, + ] + }, + { + "kind": "text", + "ranges": [ + { + "text": "!" + }, + ] + } + ] + }, + { + "kind": "block", + "type": "paragraph", + "nodes": [ + { + "kind": "text", + "ranges": [ + { + "text": "This example shows hyperlinks in action. It features two ways to add links. You can either add a link via the toolbar icon above, or if you want in on a little secret, copy a URL to your keyboard and paste it while a range of text is selected." + } + ] + } + ] + } + ] +} diff --git a/examples/table/index.html b/examples/table/index.html index 2ee7e77ddb..a876036f65 100644 --- a/examples/table/index.html +++ b/examples/table/index.html @@ -1,7 +1,7 @@ - Editor | Auto-markdown Example + Editor | Table Example diff --git a/lib/components/content.js b/lib/components/content.js index 1a4cd8aaea..7b81260c5b 100644 --- a/lib/components/content.js +++ b/lib/components/content.js @@ -83,7 +83,7 @@ class Content extends React.Component { const { anchorNode, anchorOffset, focusNode, focusOffset } = native const anchor = OffsetKey.findPoint(anchorNode, anchorOffset) const focus = OffsetKey.findPoint(focusNode, focusOffset) - const edges = document.filterNodes((node) => { + const edges = document.filterDeep((node) => { return node.key == anchor.key || node.key == focus.key }) diff --git a/lib/index.js b/lib/index.js index fad869927d..5cc8af9fe3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ export default Editor export { default as Block } from './models/block' export { default as Character } from './models/character' +export { default as Data } from './models/data' export { default as Document } from './models/document' export { default as Inline } from './models/inline' export { default as Mark } from './models/mark' diff --git a/lib/models/node.js b/lib/models/node.js index 71ee7629bf..48c702fc4e 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -1,10 +1,11 @@ import Block from './block' import Character from './character' +import Data from './data' import Mark from './mark' import Selection from './selection' import Text from './text' -import { List, OrderedMap, OrderedSet, Set } from 'immutable' +import { List, Map, OrderedMap, OrderedSet, Set } from 'immutable' /** * Node. @@ -21,8 +22,8 @@ const Node = { * @param {String or Node} key */ - assertHasNode(key) { - if (!this.hasNode(key)) throw new Error('Could not find that child node.') + assertHasDeep(key) { + if (!this.hasDeep(key)) throw new Error('Could not find that child node.') }, /** @@ -41,10 +42,10 @@ const Node = { // Make sure the children exist. const { startKey, startOffset, endKey, endOffset } = range - node.assertHasNode(startKey) - node.assertHasNode(endKey) + node.assertHasDeep(startKey) + node.assertHasDeep(endKey) - let startNode = node.getNode(startKey) + let startNode = node.getDeep(startKey) // If the start and end nodes are the same, remove the matching characters. if (startKey == endKey) { @@ -55,7 +56,7 @@ const Node = { }) startNode = startNode.merge({ characters }) - node = node.updateNode(startNode) + node = node.updateDeep(startNode) return node } @@ -78,15 +79,15 @@ const Node = { node = node.deleteAtRange(endRange) // Then remove any nodes in between the top-most start and end nodes... - let startParent = node.getParentNode(startKey) - let endParent = node.getParentNode(endKey) + let startParent = node.getParent(startKey) + let endParent = node.getParent(endKey) const startGrandestParent = node.nodes.find((child) => { - return child == startParent || child.hasNode(startParent) + return child == startParent || child.hasDeep(startParent) }) const endGrandestParent = node.nodes.find((child) => { - return child == endParent || child.hasNode(endParent) + return child == endParent || child.hasDeep(endParent) }) const nodes = node.nodes @@ -99,15 +100,15 @@ const Node = { // Then add the end parent's nodes to the start parent node. const newNodes = startParent.nodes.concat(endParent.nodes) startParent = startParent.merge({ nodes: newNodes }) - node = node.updateNode(startParent) + node = node.updateDeep(startParent) // Then remove the end parent. - let endGrandparent = node.getParentNode(endParent) + let endGrandparent = node.getParent(endParent) if (endGrandparent == node) { - node = node.removeNode(endParent) + node = node.removeDeep(endParent) } else { - endGrandparent = endGrandparent.removeNode(endParent) - node = node.updateNode(endGrandparent) + endGrandparent = endGrandparent.removeDeep(endParent) + node = node.updateDeep(endGrandparent) } // Normalize the node. @@ -134,11 +135,10 @@ const Node = { // When at start of a text node, merge forwards into the next text node. const { startKey } = range - const startNode = node.getNode(startKey) + const startNode = node.getDeep(startKey) if (range.isAtStartOf(startNode)) { - const parent = node.getParentNode(startNode) - const previous = node.getPreviousNode(parent).nodes.first() + const previous = node.getPreviousText(startNode) range = range.extendBackwardToEndOf(previous) node = node.deleteAtRange(range) return node @@ -172,11 +172,10 @@ const Node = { // When at end of a text node, merge forwards into the next text node. const { startKey } = range - const startNode = node.getNode(startKey) + const startNode = node.getDeep(startKey) if (range.isAtEndOf(startNode)) { - const parent = node.getParentNode(startNode) - const next = node.getNextNode(parent).nodes.first() + const next = node.getNextText(startNode) range = range.extendForwardToStartOf(next) node = node.deleteAtRange(range) return node @@ -197,12 +196,12 @@ const Node = { * @return {Node} node */ - findNode(iterator) { + findDeep(iterator) { const shallow = this.nodes.find(iterator) if (shallow != null) return shallow return this.nodes - .map(node => node.kind == 'text' ? null : node.findNode(iterator)) + .map(node => node.kind == 'text' ? null : node.findDeep(iterator)) .filter(node => node) .first() }, @@ -214,16 +213,35 @@ const Node = { * @return {OrderedMap} matches */ - filterNodes(iterator) { - const shallow = this.nodes.filter(iterator) - const deep = this.nodes - .map(node => node.kind == 'text' ? null : node.filterNodes(iterator)) - .filter(node => node) - .reduce((all, map) => { - return all.concat(map) - }, shallow) + filterDeep(iterator) { + return this.nodes.reduce((matches, child) => { + if (iterator(child, child.key, this.nodes)) { + matches = matches.set(child.key, child) + } - return deep + if (child.kind != 'text') { + matches = matches.concat(child.filterDeep(iterator)) + } + + return matches + }, new OrderedMap()) + }, + + /** + * Get the closest block nodes for each text node in a `range`. + * + * @param {Selection} range + * @return {OrderedMap} nodes + */ + + getBlocksAtRange(range) { + range = range.normalize(this) + const texts = this.getTextNodesAtRange(range) + const blocks = texts.map((text) => { + return this.getClosest(text, p => p.kind == 'block') + }) + + return blocks }, /** @@ -247,14 +265,86 @@ const Node = { return list }, + /** + * Get closest parent of `node` that matches `iterator`. + * + * @param {Node} node + * @param {Function} iterator + * @return {Node or Null} node + */ + + getClosest(node, iterator) { + while (node) { + if (node == this) return null + if (iterator(node)) return node + node = this.getParent(node) + } + + return null + }, + + /** + * Get a child node by `key`. + * + * @param {String} key + * @return {Node or Null} + */ + + getDeep(key) { + key = normalizeKey(key) + const match = this.findDeep(node => node.key == key) + return match || null + }, + + /** + * Get the depth of a child node by `key`, with optional `startAt`. + * + * @param {String or Node} key + * @param {Number} startAt (optional) + * @return {Number} depth + */ + + getDepth(key, startAt = 1) { + key = normalizeKey(key) + if (this.nodes.has(key)) return startAt + + const child = this.nodes.find(node => { + return node.kind == 'text' + ? null + : node.hasDeep(key) + }) + + return child + ? child.getDepth(key, startAt + 1) + : null + }, + /** * Get the first text child node. * * @return {Text or Null} text */ - getFirstTextNode() { - return this.findNode(node => node.kind == 'text') || null + getFirstText() { + return this.getTextNodes().first() || null + }, + + /** + * Get the closest inline nodes for each text node in a `range`. + * + * @param {Selection} range + * @return {OrderedMap} nodes + */ + + getInlinesAtRange(range) { + range = range.normalize(this) + const node = this + const texts = node.getTextNodesAtRange(range) + const inlines = texts + .map(text => node.getClosest(text, p => p.kind == 'inline')) + .filter(inline => inline) + + return inlines }, /** @@ -263,9 +353,8 @@ const Node = { * @return {Text or Null} text */ - getLastTextNode() { - const texts = this.filterNodes(node => node.kind == 'text') - return texts.last() || null + getLastText() { + return this.getTextNodes().last() || null }, /** @@ -285,7 +374,7 @@ const Node = { // If the range is collapsed, and at the start of the node, check the // previous text node. if (range.isCollapsed && startOffset == 0) { - const previous = this.getPreviousTextNode(startKey) + const previous = this.getPreviousText(startKey) if (!previous) return new Set() const char = text.characters.get(previous.length - 1) return char.marks @@ -293,7 +382,7 @@ const Node = { // If the range is collapsed, check the character before the start. if (range.isCollapsed) { - const text = this.getNode(startKey) + const text = this.getDeep(startKey) const char = text.characters.get(range.startOffset - 1) return char.marks } @@ -310,15 +399,41 @@ const Node = { }, /** - * Get a child node by `key`. + * Get the child node after the one by `key`. * - * @param {String} key + * @param {String or Node} key * @return {Node or Null} */ - getNode(key) { + getNextSibling(key) { key = normalizeKey(key) - return this.findNode(node => node.key == key) || null + + const shallow = this.nodes + .skipUntil(node => node.key == key) + .rest() + .first() + + if (shallow != null) return shallow + + return this.nodes + .map(node => node.kind == 'text' ? null : node.getNextSibling(key)) + .filter(node => node) + .first() + }, + + /** + * Get the text node after a text node by `key`. + * + * @param {String or Node} key + * @return {Node or Null} node + */ + + getNextText(key) { + key = normalizeKey(key) + return this.getTextNodes() + .skipUntil(text => text.key == key) + .take(2) + .last() }, /** @@ -328,16 +443,16 @@ const Node = { * @return {Number} offset */ - getNodeOffset(key) { - this.assertHasNode(key) - const match = this.getNode(key) + getOffset(key) { + this.assertHasDeep(key) + const match = this.getDeep(key) // Get all of the nodes that come before the matching child. const child = this.nodes.find((node) => { if (node == match) return true return node.kind == 'text' ? false - : node.hasNode(match) + : node.hasDeep(match) }) const befores = this.nodes.takeUntil(node => node.key == child.key) @@ -351,30 +466,29 @@ const Node = { // before it, otherwise recurse. return this.nodes.has(match.key) ? offset - : offset + child.getNodeOffset(key) + : offset + child.getOffset(key) }, /** - * Get the child node after the one by `key`. + * Get the parent of a child node by `key`. * * @param {String or Node} key * @return {Node or Null} */ - getNextNode(key) { + getParent(key) { key = normalizeKey(key) - const shallow = this.nodes - .skipUntil(node => node.key == key) - .rest() - .first() + if (this.nodes.get(key)) return this + let node = null - if (shallow != null) return shallow + this.nodes.forEach((child) => { + if (child.kind == 'text') return + const match = child.getParent(key) + if (match) node = match + }) - return this.nodes - .map(node => node.kind == 'text' ? null : node.getNextNode(key)) - .filter(node => node) - .first() + return node }, /** @@ -384,7 +498,7 @@ const Node = { * @return {Node or Null} */ - getPreviousNode(key) { + getPreviousSibling(key) { key = normalizeKey(key) const matches = this.nodes.get(key) @@ -396,55 +510,23 @@ const Node = { } return this.nodes - .map(node => node.kind == 'text' ? null : node.getPreviousNode(key)) + .map(node => node.kind == 'text' ? null : node.getPreviousSibling(key)) .filter(node => node) .first() }, /** - * Get the previous text node by `key`. - * - * @param {String or Node} key - * @return {Node or Null} - */ - - getPreviousTextNode(key) { - key = normalizeKey(key) - - // Create a new selection starting at the first text node. - const first = this.findNode(node => node.kind == 'text') - const range = Selection.create({ - anchorKey: first.key, - anchorOffset: 0, - focusKey: key, - focusOffset: 0 - }) - - const texts = this.getTextNodesAtRange(range) - const previous = texts.get(texts.size - 2) - return previous - }, - - /** - * Get the parent of a child node by `key`. + * Get the text node before a text node by `key`. * * @param {String or Node} key - * @return {Node or Null} + * @return {Node or Null} node */ - getParentNode(key) { + getPreviousText(key) { key = normalizeKey(key) - - if (this.nodes.get(key)) return this - let node = null - - this.nodes.forEach((child) => { - if (child.kind == 'text') return - const match = child.getParentNode(key) - if (match) node = match - }) - - return node + return this.getTextNodes() + .takeUntil(text => text.key == key) + .last() }, /** @@ -454,9 +536,9 @@ const Node = { * @return {Node or Null} */ - getTextNodeAtOffset(offset) { + getTextAtOffset(offset) { let length = 0 - let texts = this.filterNodes(node => node.kind == 'text') + let texts = this.filterDeep(node => node.kind == 'text') let match = texts.find((node) => { length += node.length return length >= offset @@ -465,6 +547,20 @@ const Node = { return match }, + /** + * Recursively get all of the child text nodes in order of appearance. + * + * @return {OrderedMap} nodes + */ + + getTextNodes() { + return this.nodes.reduce((texts, node) => { + return node.kind == 'text' + ? texts.set(node.key, node) + : texts.concat(node.getTextNodes()) + }, new OrderedMap()) + }, + /** * Get all of the text nodes in a `range`. * @@ -476,70 +572,22 @@ const Node = { range = range.normalize(this) const { startKey, endKey } = range - // If the selection isn't formed, return an empty map. - if (startKey == null || endKey == null) return new OrderedMap() + // If the selection is unset, return an empty map. + if (range.isUnset) return new OrderedMap() // Assert that the nodes exist before searching. - this.assertHasNode(startKey) - this.assertHasNode(endKey) + this.assertHasDeep(startKey) + this.assertHasDeep(endKey) // Return the text nodes after the start offset and before the end offset. - const endNode = this.getNode(endKey) - const texts = this.filterNodes(node => node.kind == 'text') + const texts = this.getTextNodes() + const endNode = this.getDeep(endKey) const afterStart = texts.skipUntil(node => node.key == startKey) const upToEnd = afterStart.takeUntil(node => node.key == endKey) - let matches = upToEnd.set(endNode.key, endNode) + const matches = upToEnd.set(endNode.key, endNode) return matches }, - /** - * Get the closets block nodes for each text node in a `range`. - * - * @param {Selection} range - * @return {OrderedMap} nodes - */ - - getBlockNodesAtRange(range) { - const node = this - range = range.normalize(node) - const texts = node.getTextNodesAtRange(range) - const blocks = texts.map(text => node.getClosestBlockNode(text)) - return blocks - }, - - /** - * Get the node's closest block parent node. - * - * @param {Node} node - * @return {Node} node - */ - - getClosestBlockNode(node) { - let parent = this.getParentNode(node) - - while (parent && parent.kind != 'block') { - parent = this.getParentNode(parent) - } - - return parent - }, - - /** - * Get the node's closest inline parent node. - * - * @return {Node} node - */ - - getClosestInlineNode() { - let parent = this.getParentNode(node) - - while (parent && parent.kind != 'inline') { - parent = this.getParentNode(parent) - } - - return parent - }, - /** * Recursively check if a child node exists by `key`. * @@ -547,12 +595,12 @@ const Node = { * @return {Boolean} true */ - hasNode(key) { + hasDeep(key) { key = normalizeKey(key) return !! this.nodes.find((node) => { return node.kind == 'text' ? node.key == key - : node.key == key || node.hasNode(key) + : node.key == key || node.hasDeep(key) }) }, @@ -575,31 +623,29 @@ const Node = { } let { startKey, startOffset } = range - let startNode = node.getNode(startKey) + let startNode = node.getDeep(startKey) let { characters } = startNode // Create a list of the new characters, with the marks from the previous // character if one exists. - const prevOffset = startOffset - 1 - const marks = characters.has(prevOffset) - ? characters.get(prevOffset).marks - : null - - const newCharacters = text.split('').reduce((list, char) => { - const obj = { text } - if (marks) obj.marks = marks - return list.push(Character.create(obj)) - }, Character.createList()) + const prev = characters.get(startOffset - 1) + const marks = prev ? prev.marks : null + const newChars = Character.createList( + text.split('').map((char) => { + const obj = { text: char } + if (marks) obj.marks = marks + return Character.create(obj) + }) + ) // Splice in the new characters. - const resumeOffset = startOffset + text.length - 1 characters = characters.slice(0, startOffset) - .concat(newCharacters) - .concat(characters.slice(resumeOffset, Infinity)) + .concat(newChars) + .concat(characters.slice(startOffset)) // Update the existing text node. startNode = startNode.merge({ characters }) - node = node.updateNode(startNode) + node = node.updateDeep(startNode) // Normalize the node. return node.normalize() @@ -643,7 +689,7 @@ const Node = { // Update each of the text nodes. texts.forEach((text) => { - node = node.updateNode(text) + node = node.updateDeep(text) }) return node @@ -659,10 +705,10 @@ const Node = { let node = this // See if there are any adjacent text nodes. - let firstAdjacent = node.findNode((child) => { + let firstAdjacent = node.findDeep((child) => { if (child.kind != 'text') return - const parent = node.getParentNode(child) - const next = parent.getNextNode(child) + const parent = node.getParent(child) + const next = parent.getNextSibling(child) return next && next.kind == 'text' }) @@ -670,18 +716,18 @@ const Node = { if (!firstAdjacent) return node // Fix an adjacent text node if one exists. - let parent = node.getParentNode(firstAdjacent) - const second = parent.getNextNode(firstAdjacent) + let parent = node.getParent(firstAdjacent) + const second = parent.getNextSibling(firstAdjacent) const characters = firstAdjacent.characters.concat(second.characters) firstAdjacent = firstAdjacent.merge({ characters }) - parent = parent.updateNode(firstAdjacent) + parent = parent.updateDeep(firstAdjacent) // Then remove the second node. - parent = parent.removeNode(second) + parent = parent.removeDeep(second) // If the parent isn't this node, it needs to be updated. if (parent != node) { - node = node.updateNode(parent) + node = node.updateDeep(parent) } else { node = parent } @@ -715,7 +761,7 @@ const Node = { * @return {Node} node */ - removeNode(key) { + removeDeep(key) { key = normalizeKey(key) let nodes = this.nodes.remove(key) @@ -723,46 +769,83 @@ const Node = { }, /** - * Set the direct parent of text nodes in a range to `type`. + * Set the block nodes in a range to `type`, with optional `data`. * * @param {Selection} range + * @param {String} type + * @param {Data} data (optional) * @return {Node} node */ - setTypeAtRange(range, type) { + setBlockAtRange(range, type, data) { let node = this range = range.normalize(node) - const texts = node.getTextNodesAtRange(range) - let parents = new OrderedSet() + // Allow for passing data only. + if (typeof type == 'object') { + data = type + type = null + } - // Find the direct parent of each text node. - texts.forEach((text) => { - const parent = node.has(text.key) ? node : node.getParentNode(text) - parents = parents.add(parent) + // If data is passed, ensure it's immutable. + if (data) data = Data.create(data) + + // Update each of the blocks. + const blocks = node.getBlocksAtRange(range) + blocks.forEach((block) => { + const obj = {} + if (type) obj.type = type + if (data) obj.data = data + block = block.merge(obj) + node = node.updateDeep(block) }) - // Set the new type for each parent. - parents = parents.forEach((parent) => { - if (parent == node) { - node = node.merge({ type }) - } else { - parent = parent.merge({ type }) - node = node.updateNode(parent) - } + return node + }, + + /** + * Set the inline nodes in a range to `type`, with optional `data`. + * + * @param {Selection} range + * @param {String} type + * @param {Data} data (optional) + * @return {Node} node + */ + + setInlineAtRange(range, type, data) { + let node = this + range = range.normalize(node) + + // Allow for passing data only. + if (typeof type == 'object') { + data = type + type = null + } + + // If data is passed, ensure it's immutable. + if (data) data = Data.create(data) + + // Update each of the inlines. + const inlines = node.getInlinesAtRange(range) + inlines.forEach((inline) => { + const obj = {} + if (type) obj.type = type + if (data) obj.data = data + inline = inline.merge(obj) + node = node.updateDeep(inline) }) return node }, /** - * Split the nodes at a `range`. + * Split the block nodes at a `range`. * * @param {Selection} range * @return {Node} node */ - splitAtRange(range) { + splitBlockAtRange(range) { let node = this range = range.normalize(node) @@ -773,7 +856,7 @@ const Node = { } const { startKey, startOffset } = range - const startNode = node.getNode(startKey) + const startNode = node.getDeep(startKey) // Split the text node's characters. const { characters, length } = startNode @@ -781,9 +864,9 @@ const Node = { const secondCharacters = characters.takeLast(length - startOffset) // Create a new first element with only the first set of characters. - const parent = node.getParentNode(startNode) + const parent = node.getParent(startNode) const firstText = startNode.set('characters', firstCharacters) - const firstElement = parent.updateNode(firstText) + const firstElement = parent.updateDeep(firstText) // Create a brand new second element with the second set of characters. let secondText = Text.create({}) @@ -793,10 +876,12 @@ const Node = { }) secondText = secondText.set('characters', secondCharacters) - secondElement = secondElement.pushNode(secondText) + secondElement = secondElement.merge({ + nodes: secondElement.nodes.set(secondText.key, secondText) + }); // Replace the old parent node in the grandparent with the two new ones. - let grandparent = node.getParentNode(parent) + let grandparent = node.getParent(parent) const befores = grandparent.nodes.takeUntil(child => child.key == parent.key) const afters = grandparent.nodes.skipUntil(child => child.key == parent.key).rest() const nodes = befores @@ -809,7 +894,7 @@ const Node = { node = node.merge({ nodes }) } else { grandparent = grandparent.merge({ nodes }) - node = node.updateNode(grandparent) + node = node.updateDeep(grandparent) } // Normalize the node. @@ -853,7 +938,7 @@ const Node = { // Update each of the text nodes. texts.forEach((text) => { - node = node.updateNode(text) + node = node.updateDeep(text) }) return node @@ -867,7 +952,7 @@ const Node = { * @return {Node} node */ - updateNode(key, node) { + updateDeep(key, node) { if (arguments.length == 1) { node = key key = normalizeKey(key) @@ -879,51 +964,297 @@ const Node = { } const nodes = this.nodes.map((child) => { - return child.kind == 'text' ? child : child.updateNode(key, node) + return child.kind == 'text' ? child : child.updateDeep(key, node) }) return this.merge({ nodes }) }, /** - * Wrap all of the nodes in a `range` in a new parent node of `type`. + * Wrap all of the blocks in a `range` in a new block node of `type`. * * @param {Selection} range * @param {String} type + * @param {Data} data (optional) * @return {Node} node */ - wrapAtRange(range, type) { + wrapBlockAtRange(range, type, data) { range = range.normalize(this) + data = Data.create(data) let node = this - let blocks = node.getBlockNodesAtRange(range) - // Iterate each of the block nodes, wrapping them. - blocks.forEach((block) => { - let isDirectChild = node.nodes.has(block.key) - let parent = isDirectChild ? node : node.getParentNode(block) + // Get the block nodes, sorted by depth. + const blocks = node.getBlocksAtRange(range) + const sorted = blocks.sort((a, b) => { + const da = node.getDepth(a) + const db = node.getDepth(b) + if (da == db) return 0 + if (da > db) return -1 + if (da < db) return 1 + }) - // Create a new wrapper containing the block. - let nodes = Block.createMap([ block ]) - let wrapper = Block.create({ type, nodes }) + // Get the lowest common siblings, relative to the highest block. + const highest = sorted.first() + const depth = node.getDepth(highest) + const siblings = blocks.reduce((siblings, block) => { + const sibling = node.getDepth(block) == depth + ? block + : node.getClosest(block, (p) => node.getDepth(p) == depth) + siblings = siblings.set(sibling.key, sibling) + return siblings + }, new OrderedMap()) + + // Wrap the siblings in a new block. + const wrapper = Block.create({ + nodes: siblings, + type, + data + }) - // Replace the block in it's parent with the wrapper. - nodes = parent.nodes.takeUntil(node => node == block) - .set(wrapper.key, wrapper) - .concat(parent.nodes.skipUntil(node => node == block).rest()) + // Replace the siblings with the wrapper. + const isDirectChild = node.nodes.has(highest.key) + let parent = isDirectChild ? node : node.getParent(highest) + const nodes = parent.nodes + .takeUntil(node => node == highest) + .set(wrapper.key, wrapper) + .concat(parent.nodes.skipUntil(node => node == highest).rest()) + + // Update the parent. + if (isDirectChild) { + node = node.merge({ nodes }) + } else { + parent = parent.merge({ nodes }) + node = node.updateDeep(parent) + } + + return node + }, + + /** + * Unwrap all of the block nodes in a `range` from a block node of `type.` + * + * @param {Selection} range + * @param {String} type (optional) + * @param {Data or Object} data (optional) + * @return {Node} node + */ + + unwrapBlockAtRange(range, type, data) { + range = range.normalize(this) + let node = this + const blocks = node.getBlocksAtRange(range) + + // Allow for only data. + if (typeof type == 'object') { + data = type + type = null + } + + // Ensure that data is immutable. + if (data) data = Data.create(data) + + // Find the closest wrappers of each block. + const wrappers = blocks.reduce((wrappers, block) => { + const match = node.getClosest(block, (parent) => { + if (type && parent.type != type) return false + if (data && !parent.data.isSuperset(data)) return false + return true + }) + + if (match) wrappers = wrappers.set(match.key, match) + return wrappers + }, new OrderedMap()) + + // Replace each of the wrappers with their child nodes. + wrappers.forEach((wrapper) => { + const isDirectChild = node.nodes.has(wrapper.key) + let parent = isDirectChild ? node : node.getParent(wrapper) + + // Replace the wrapper in the parent's nodes with the block. + const nodes = parent.nodes.takeUntil(n => n == wrapper) + .concat(wrapper.nodes) + .concat(parent.nodes.skipUntil(n => n == wrapper).rest()) // Update the parent. if (isDirectChild) { node = node.merge({ nodes }) } else { parent = parent.merge({ nodes }) - node = node.updateNode(parent) + node = node.updateDeep(parent) } }) return node.normalize() }, + + + getClosestInline(child) { + return this.getClosest(child, p => p.kind == 'inline') + }, + + getFurthestInline(child) { + let furthest = null + let next + + while (next = this.getClosestInline(child)) { + furthest = next + child = next + } + + return furthest + }, + + splitTextAtRange(range) { + range = range.normalize(this) + let node = this + + // If the range is expanded, remove it first. + if (range.isExpanded) { + node = node.deleteAtRange(range) + range = range.moveToStart() + } + + // Split the text node's characters. + const { startKey, startOffset } = range + const text = node.getDeep(startKey) + const { characters } = text + const firstChars = characters.take(startOffset) + const secondChars = characters.skip(startOffset) + let firstChild = text.merge({ characters: firstChars }) + let secondChild = Text.create({ characters: secondChars }) + + // Split the text nodes. + let parent = node.getParent(text) + const nodes = parent.nodes + .takeUntil(c => c.key == firstChild.key) + .set(firstChild.key, firstChild) + .set(secondChild.key, secondChild) + .concat(parent.nodes.skipUntil(n => n.key == firstChild.key).rest()) + + // Update the nodes. + parent = parent.merge({ nodes }) + node = node.updateDeep(parent) + return node + }, + + splitInlineAtRange(range) { + range = range.normalize(this) + const Inline = require('./inline').default + let node = this + + // If the range is expanded, remove it first. + if (range.isExpanded) { + node = node.deleteAtRange(range) + range = range.moveToStart() + } + + // First split the text nodes. + node = node.splitTextAtRange(range) + let firstChild = node.getDeep(range.startKey) + let secondChild = node.getNextText(firstChild) + let parent + + // While the parent is an inline parent, split the inline nodes. + while (parent = node.getClosestInline(firstChild)) { + const firstNodes = Inline.createMap([firstChild]) + const secondNodes = Inline.createMap([secondChild]) + firstChild = parent.merge({ nodes: firstNodes }) + secondChild = Inline.create({ + nodes: secondNodes, + type: parent.type, + data: parent.data + }) + + // Split the children. + const isGrandparent = node.nodes.has(parent.key) + const grandparent = isGrandparent ? node : node.getParent(parent) + const nodes = grandparent.nodes + .takeUntil(c => c.key == firstChild.key) + .set(firstChild.key, firstChild) + .set(secondChild.key, secondChild) + .concat(parent.nodes.skipUntil(n => n.key == firstChild.key).rest()) + + // Update the parent. + node = isGrandparent + ? node.merge({ nodes }) + : node.updateDeep(parent.merge({ nodes })) + } + + return node + }, + + wrapInlineAtRange(range, type, data = new Map()) { + range = range.normalize(this) + + const Inline = require('./inline').default + let node = this + let blocks = node.getBlocksAtRange(range) + + // If collapsed or unset, there's nothing to wrap. + if (range.isCollapsed || range.isUnset) return node + + // Split at the start of the range. + const start = range.moveToStart() + node = node.splitInlineAtRange(start) + + // Split at the end of the range. + let { startKey, startOffset, endKey, endOffset } = range + let firstNode = node.getDeep(startKey) + let nextNode = node.getNextText(startKey) + + const end = Selection.create({ + anchorKey: nextNode.key, + anchorOffset: firstNode.length - startOffset, + focusKey: endKey, + focusOffset: endOffset + }) + + node = node.splitInlineAtRange(end) + + // Update the range... + startKey = range.startKey + endKey = range.endKey + startNode = node.getNextText(startKey) + startOffset = 0 + let endNode = startKey == endKey ? startNode : node.getDeep(endKey) + endOffset = endNode.length + + range = Selection.create({ + anchorKey: startKey, + anchorOffset: startOffset, + focusKey: endKey, + focusOffset: endOffset + }) + + // Get the furthest inline nodes in the range. + const texts = node.getTextNodesAtRange(range) + const children = texts.map(text => node.getFurthestInline(text) || text) + + // Iterate each of the child nodes, wrapping them. + children.forEach((child) => { + let isDirectChild = node.nodes.has(child.key) + let parent = isDirectChild ? node : node.getParent(child) + + // Create a new wrapper containing the child. + let nodes = Inline.createMap([child]) + let wrapper = Inline.create({ nodes, type, data }) + + // Replace the child in it's parent with the wrapper. + nodes = parent.nodes.takeUntil(n => n == child) + .set(wrapper.key, wrapper) + .concat(parent.nodes.skipUntil(n => n == child).rest()) + + // Update the parent. + node = isDirectChild + ? node.merge({ nodes }) + : node.updateDeep(parent.merge({ nodes })) + }) + + return node + }, + /** * Unwrap all of the block nodes in a `range` from a parent node of `type.` * @@ -932,16 +1263,16 @@ const Node = { * @return {Node} node */ - unwrapAtRange(range, type) { + unwrapInlineAtRange(range, type) { range = range.normalize(this) let node = this - let blocks = node.getBlockNodesAtRange(range) + let blocks = node.getInlinesAtRange(range) // Iterate each of the block nodes, unwrapping them. blocks.forEach((block) => { - let wrapper = node.getParentNode(block) + let wrapper = node.getParent(block) while (wrapper && wrapper.type != type) { - wrapper = node.getParentNode(wrapper) + wrapper = node.getParent(wrapper) } // If no wrapper was found, do skip this block. @@ -949,7 +1280,7 @@ const Node = { // Take the child nodes of the wrapper, and move them to the block. const isDirectChild = node.nodes.has(wrapper.key) - let parent = isDirectChild ? node : node.getParentNode(wrapper) + let parent = isDirectChild ? node : node.getParent(wrapper) // Replace the wrapper in the parent's nodes with the block. const nodes = parent.nodes.takeUntil(node => node == wrapper) @@ -961,7 +1292,7 @@ const Node = { node = node.merge({ nodes }) } else { parent = parent.merge({ nodes }) - node = node.updateNode(parent) + node = node.updateDeep(parent) } }) diff --git a/lib/models/selection.js b/lib/models/selection.js index badd032506..32fa28e704 100644 --- a/lib/models/selection.js +++ b/lib/models/selection.js @@ -60,6 +60,16 @@ class Selection extends SelectionRecord { return this.isExpanded } + /** + * Get whether the range's anchor of focus keys are not set yet. + * + * @return {Boolean} isUnset + */ + + get isUnset() { + return this.anchorKey == null || this.focusKey == null + } + /** * Get whether the selection is forward. * @@ -109,7 +119,7 @@ class Selection extends SelectionRecord { isAtStartOf(node) { const { startKey, startOffset } = this - const first = node.kind == 'text' ? node : node.getFirstTextNode() + const first = node.kind == 'text' ? node : node.getFirstText() return startKey == first.key && startOffset == 0 } @@ -122,7 +132,7 @@ class Selection extends SelectionRecord { isAtEndOf(node) { const { endKey, endOffset } = this - const last = node.kind == 'text' ? node : node.getLastTextNode() + const last = node.kind == 'text' ? node : node.getLastText() return endKey == last.key && endOffset == last.length } @@ -142,25 +152,25 @@ class Selection extends SelectionRecord { if (anchorKey == null || focusKey == null) return selection // Asset that the anchor and focus nodes exist in the node tree. - node.assertHasNode(anchorKey) - node.assertHasNode(focusKey) - let anchorNode = node.getNode(anchorKey) - let focusNode = node.getNode(focusKey) + node.assertHasDeep(anchorKey) + node.assertHasDeep(focusKey) + let anchorNode = node.getDeep(anchorKey) + let focusNode = node.getDeep(focusKey) // If the anchor node isn't a text node, match it to one. if (anchorNode.kind != 'text') { - anchorNode = node.getTextNodeAtOffset(anchorOffset) - let parent = node.getParentNode(anchorNode) - let offset = parent.getNodeOffset(anchorNode) + anchorNode = node.getTextAtOffset(anchorOffset) + let parent = node.getParent(anchorNode) + let offset = parent.getOffset(anchorNode) anchorOffset = anchorOffset - offset anchorKey = anchorNode.key } // If the focus node isn't a text node, match it to one. if (focusNode.kind != 'text') { - focusNode = node.getTextNodeAtOffset(focusOffset) - let parent = node.getParentNode(focusNode) - let offset = parent.getNodeOffset(focusNode) + focusNode = node.getTextAtOffset(focusOffset) + let parent = node.getParent(focusNode) + let offset = parent.getOffset(focusNode) focusOffset = focusOffset - offset focusKey = focusNode.key } diff --git a/lib/models/state.js b/lib/models/state.js index 6324230b9b..ec377343cb 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -32,8 +32,16 @@ const NODE_LIKE_METHODS = [ 'deleteAtRange', 'deleteBackwardAtRange', 'deleteForwardAtRange', - 'insertAtRange', - 'splitAtRange' + 'insertTextAtRange', + 'markAtRange', + 'setBlockAtRange', + 'setInlineAtRange', + 'splitBlockAtRange', + 'splitInlineAtRange', + 'unmarkAtRange', + 'unwrapBlockAtRange', + 'wrapBlockAtRange', + 'wrapInlineAtRange' ] /** @@ -140,7 +148,17 @@ class State extends Record(DEFAULTS) { */ get currentBlockNodes() { - return this.document.getBlockNodesAtRange(this.selection) + return this.document.getBlocksAtRange(this.selection) + } + + /** + * Get the inline nodes in the current selection. + * + * @return {OrderedMap} nodes + */ + + get currentInlineNodes() { + return this.document.getInlinesAtRange(this.selection) } /** @@ -198,7 +216,7 @@ class State extends Record(DEFAULTS) { // Determine what the selection should be after deleting. const { startKey } = selection - const startNode = document.getNode(startKey) + const startNode = document.getDeep(startKey) if (selection.isExpanded) { after = selection.moveToStart() @@ -209,8 +227,8 @@ class State extends Record(DEFAULTS) { } else if (selection.isAtStartOf(startNode)) { - const parent = document.getParentNode(startNode) - const previous = document.getPreviousNode(parent).nodes.first() + const parent = document.getParent(startNode) + const previous = document.getPrevious(parent).nodes.first() after = selection.moveToEndOf(previous) } @@ -283,16 +301,31 @@ class State extends Record(DEFAULTS) { } /** - * Set the nodes in the current selection to `type`. + * Set the block nodes in the current selection to `type`. + * + * @param {String} type + * @return {State} state + */ + + setBlock(type, data) { + let state = this + let { document, selection } = state + document = document.setBlockAtRange(selection, type, data) + state = state.merge({ document }) + return state + } + + /** + * Set the inline nodes in the current selection to `type`. * * @param {String} type * @return {State} state */ - setType(type) { + setInline(type, data) { let state = this let { document, selection } = state - document = document.setTypeAtRange(selection, type) + document = document.setInlineAtRange(selection, type, data) state = state.merge({ document }) return state } @@ -313,9 +346,9 @@ class State extends Record(DEFAULTS) { // Determine what the selection should be after splitting. const { startKey } = selection - const startNode = document.getNode(startKey) - const parent = document.getParentNode(startNode) - const next = document.getNextNode(parent) + const startNode = document.getDeep(startKey) + const parent = document.getParent(startNode) + const next = document.getNext(parent) const text = next.nodes.first() selection = selection.moveToStartOf(text) @@ -345,10 +378,10 @@ class State extends Record(DEFAULTS) { * @return {State} state */ - wrap(type) { + wrapBlock(type) { let state = this let { document, selection } = state - document = document.wrapAtRange(selection, type) + document = document.wrapBlockAtRange(selection, type) state = state.merge({ document }) return state } @@ -360,11 +393,43 @@ class State extends Record(DEFAULTS) { * @return {State} state */ - unwrap(type) { + unwrapBlock(type) { + let state = this + let { document, selection } = state + selection = selection.normalize(document) + document = document.unwrapBlockAtRange(selection, type) + state = state.merge({ document, selection }) + return state + } + + /** + * Wrap the current selection in new inline nodes of `type`. + * + * @param {String} type + * @param {Map} data + * @return {State} state + */ + + wrapInline(type, data) { + let state = this + let { document, selection } = state + document = document.wrapInlineAtRange(selection, type, data) + state = state.merge({ document }) + return state + } + + /** + * Unwrap the current selection from a parent of `type`. + * + * @param {String} type + * @return {State} state + */ + + unwrapInline(type) { let state = this let { document, selection } = state selection = selection.normalize(document) - document = document.unwrapAtRange(selection, type) + document = document.unwrapInlineAtRange(selection, type) state = state.merge({ document, selection }) return state } diff --git a/lib/models/transform.js b/lib/models/transform.js index 9b35826254..29e4f2b1dc 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -46,16 +46,24 @@ const TRANSFORM_TYPES = [ 'insertTextAtRange', 'mark', 'markAtRange', - 'setType', - 'setTypeAtRange', - 'split', - 'splitAtRange', + 'setBlock', + 'setBlockAtRange', + 'setInline', + 'setInlineAtRange', + 'splitBlock', + 'splitBlockAtRange', + 'splitInline', + 'splitInlineAtRange', 'unmark', 'unmarkAtRange', - 'unwrap', - 'unwrapAtRange', - 'wrap', - 'wrapAtRange' + 'unwrapBlock', + 'unwrapBlockAtRange', + 'unwrapInline', + 'unwrapInlineAtRange', + 'wrapBlock', + 'wrapBlockAtRange', + 'wrapInline', + 'wrapInlineAtRange' ] /** diff --git a/lib/plugins/core.js b/lib/plugins/core.js index e2d7c49b48..b9cea3ad3e 100644 --- a/lib/plugins/core.js +++ b/lib/plugins/core.js @@ -1,7 +1,7 @@ import React from 'react' import keycode from 'keycode' -import { IS_WINDOWS, IS_MAC } from '../utils/environment' +import environment from '../utils/environment' /** * Export. @@ -20,6 +20,7 @@ export default { onKeyDown(e, state, editor) { const key = keycode(e.which) + const { IS_WINDOWS, IS_MAC } = environment() switch (key) { case 'enter': { diff --git a/lib/serializers/raw.js b/lib/serializers/raw.js index 178cee7bbe..0508e698cd 100644 --- a/lib/serializers/raw.js +++ b/lib/serializers/raw.js @@ -17,9 +17,7 @@ import { Map } from 'immutable' */ function serialize(state) { - return { - nodes: serializeNode(state.document) - } + return serializeNode(state.document) } /** @@ -44,12 +42,12 @@ function serializeNode(node) { } case 'block': case 'inline': { - return { - data: node.data.toJSON(), - kind: node.kind, - nodes: node.nodes.toArray().map(node => serializeNode(node)), - type: node.type - } + const obj = {} + obj.kind = node.kind + obj.type = node.type + obj.nodes = node.nodes.toArray().map(node => serializeNode(node)) + if (node.data.size) obj.data = node.data.toJSON() + return obj } } } @@ -65,10 +63,10 @@ function serializeCharacters(characters) { return groupByMarks(characters) .toArray() .map((range) => { - return { - text: range.text, - marks: range.marks.map(serializeMark) - } + const obj = {} + obj.text = range.text + if (range.marks.size) obj.marks = range.marks.toArray().map(serializeMark) + return obj }) } @@ -80,10 +78,10 @@ function serializeCharacters(characters) { */ function serializeMark(mark) { - return { - type: mark.type, - data: mark.data.toJSON() - } + const obj = {} + obj.type = mark.type + if (mark.data.size) obj.data = mark.data.toJSON() + return obj } /** diff --git a/lib/utils/environment.js b/lib/utils/environment.js index 50146d20dd..c80182ff3d 100644 --- a/lib/utils/environment.js +++ b/lib/utils/environment.js @@ -3,16 +3,28 @@ import browser from 'detect-browser' import Parser from 'ua-parser-js' /** - * Detections. + * Read the environment. + * + * @return {Object} environment */ -export const IS_ANDROID = browser.name === 'android' -export const IS_CHROME = browser.name === 'chrome' -export const IS_EDGE = browser.name === 'edge' -export const IS_FIREFOX = browser.name === 'firefox' -export const IS_IE = browser.name === 'ie' -export const IS_IOS = browser.name === 'ios' -export const IS_MAC = new Parser().getOS().name === 'Mac OS' -export const IS_UBUNTU = new Parser().getOS().name === 'Ubuntu' -export const IS_SAFARI = browser.name === 'safari' -export const IS_WINDOWS = new Parser().getOS().name.includes('Windows') +function environment() { + return { + IS_ANDROID: browser.name === 'android', + IS_CHROME: browser.name === 'chrome', + IS_EDGE: browser.name === 'edge', + IS_FIREFOX: browser.name === 'firefox', + IS_IE: browser.name === 'ie', + IS_IOS: browser.name === 'ios', + IS_MAC: new Parser().getOS().name === 'Mac OS', + IS_UBUNTU: new Parser().getOS().name === 'Ubuntu', + IS_SAFARI: browser.name === 'safari', + IS_WINDOWS: new Parser().getOS().name.includes('Windows') + } +} + +/** + * Export. + */ + +export default environment diff --git a/package.json b/package.json index b7cc6924ac..443b2aaedc 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "keycode": "^2.1.2", "lodash": "^4.13.1", "react": "^15.1.0", - "to-camel-case": "^1.0.0", "ua-parser-js": "^0.7.10", "uid": "0.0.2" }, @@ -21,10 +20,13 @@ "babel-preset-stage-0": "^6.5.0", "babelify": "^7.3.0", "browserify": "^13.0.1", + "component-type": "^1.2.1", "mocha": "^2.5.3", "mocha-phantomjs": "^4.0.2", "react-dom": "^15.1.0", + "read-metadata": "^1.0.0", "standard": "^7.1.2", + "to-camel-case": "^1.0.0", "watchify": "^3.7.0" } } diff --git a/test/browser.js b/test/browser/index.js similarity index 100% rename from test/browser.js rename to test/browser/index.js diff --git a/test/support/browser.html b/test/browser/support/browser.html similarity index 100% rename from test/support/browser.html rename to test/browser/support/browser.html diff --git a/test/support/mocha.css b/test/browser/support/mocha.css similarity index 100% rename from test/support/mocha.css rename to test/browser/support/mocha.css diff --git a/test/support/mocha.js b/test/browser/support/mocha.js similarity index 100% rename from test/support/mocha.js rename to test/browser/support/mocha.js diff --git a/test/helpers/assert-json.js b/test/helpers/assert-json.js new file mode 100644 index 0000000000..699b49eaac --- /dev/null +++ b/test/helpers/assert-json.js @@ -0,0 +1,185 @@ + +import assert from 'assert' +import type from 'component-type' + +/** + * Assertion error. + */ + +const AssertionError = assert.AssertionError + +/** + * Assert that an `actual` JSON object equals an `expected` value. + * + * @param {Object} actual + * @param {Object} expected + * @throws {AssertionError} + */ + +export function equal(actual, expected, message) { + if (!test(actual, expected)) { + throw new AssertionError({ + actual: actual, + expected: wrap(actual, expected), + operator: '==', + stackStartFunction: equal + }) + } +} + +/** + * Assert that an `actual` JSON object does not equal an `expected` value. + * + * @param {Object} actual + * @param {Object} expected + * @throws {AssertionError} + */ + +export function notEqual(actual, expected, message) { + if (test(actual, expected)) { + throw new AssertionError({ + actual: actual, + expected: wrap(actual, expected), + operator: '!=', + stackStartFunction: notEqual + }) + } +} + +/** + * Assert that an `actual` JSON object strict equals an `expected` value. + * + * @param {Object} actual + * @param {Object} expected + * @throws {AssertionError} + */ + +export function strictEqual(actual, expected, message) { + if (!test(actual, expected, true)) { + throw new AssertionError({ + actual: actual, + expected: wrap(actual, expected), + operator: '===', + stackStartFunction: equal + }) + } +} + +/** + * Assert that an `actual` JSON object does not strict equal an `expected` value. + * + * @param {Object} actual + * @param {Object} expected + * @throws {AssertionError} + */ + +export function notStrictEqual(actual, expected, message) { + if (test(actual, expected, true)) { + throw new AssertionError({ + actual: actual, + expected: wrap(actual, expected), + operator: '!==', + stackStartFunction: notEqual + }) + } +} + +/** + * Test that an `actual` JSON value equals an `expected` JSON value. + * + * If a function is passed as any value, it is called with the actual value and + * must return a boolean. + * + * Strict mode uses strict equality, forces arrays to be of the same length, and + * objects to have the same keys. + * + * @param {Mixed} actual + * @param {Mixed} expected + * @param {Boolean} strict + * @return {Boolean} + */ + +function test(actual, expected, strict) { + if (type(expected) == 'function') return !! expected(actual) + if (type(actual) != type(expected)) return false + + switch (type(expected)) { + case 'object': + return object(actual, expected, strict) + case 'array': + return array(actual, expected, strict) + default: + return strict ? actual === expected : actual == expected + } +} + +/** + * Test that an `actual` object equals an `expected` object. + * + * @param {Object} object + * @param {Object} expected + * @param {Boolean} strict + * @return {Boolean} + */ + +function object(actual, expected, strict) { + if (strict) { + var ka = Object.keys(actual).sort() + var ke = Object.keys(expected).sort() + if (!test(ka, ke, strict)) return false + } + + for (var key in expected) { + if (!test(actual[key], expected[key], strict)) return false + } + + return true +} + +/** + * Test that an `actual` array equals an `expected` array. + * + * @param {Array} actual + * @param {Array} expected + * @param {Boolean} strict + * @return {Boolean} + */ + +function array(actual, expected, strict) { + if (strict) { + if (!test(actual.length, expected.length, strict)) return false + } + + for (var i = 0; i < expected.length; i++) { + if (!test(actual[i], expected[i], strict)) return false + } + + return true +} + +/** + * Wrap an expected value to remove annoying false negatives. + * + * @param {Mixed} actual + * @param {Mixed} expected + * @return {Mixed} + */ + +function wrap(actual, expected) { + if (type(expected) == 'function') return expected(actual) ? actual : expected + if (type(actual) != type(expected)) return expected + + if (type(expected) == 'object') { + for (var key in expected) { + expected[key] = wrap(actual[key], expected[key]) + } + } + + if (type(expected) == 'array') { + for (var i = 0; i < expected.length; i++) { + expected[i] = wrap(actual[i], expected[i]) + } + } + + return expected +} diff --git a/test/server.js b/test/server.js deleted file mode 100644 index d1a6772072..0000000000 --- a/test/server.js +++ /dev/null @@ -1,11 +0,0 @@ - -const assert = require('assert') -const Editor = require('..') - -/** - * Tests. - */ - -describe('server', () => { - -}) diff --git a/test/server/index.js b/test/server/index.js new file mode 100644 index 0000000000..f36494c7bb --- /dev/null +++ b/test/server/index.js @@ -0,0 +1,2 @@ + +import './transforms' diff --git a/test/server/transforms/fixtures/delete-at-range/first-character/index.js b/test/server/transforms/fixtures/delete-at-range/first-character/index.js new file mode 100644 index 0000000000..2ccb4d9970 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/first-character/input.yaml b/test/server/transforms/fixtures/delete-at-range/first-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-at-range/first-character/output.yaml b/test/server/transforms/fixtures/delete-at-range/first-character/output.yaml new file mode 100644 index 0000000000..9414f68ce0 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/first-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: ord diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/index.js b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/index.js new file mode 100644 index 0000000000..e75dc450c7 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/input.yaml b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/output.yaml b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/output.yaml new file mode 100644 index 0000000000..0a088f2365 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks-and-trim/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: woother diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks/index.js b/test/server/transforms/fixtures/delete-at-range/join-blocks/index.js new file mode 100644 index 0000000000..19d23aea7e --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: second.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks/input.yaml b/test/server/transforms/fixtures/delete-at-range/join-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/delete-at-range/join-blocks/output.yaml b/test/server/transforms/fixtures/delete-at-range/join-blocks/output.yaml new file mode 100644 index 0000000000..3d5aad120c --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/join-blocks/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wordanother diff --git a/test/server/transforms/fixtures/delete-at-range/last-character/index.js b/test/server/transforms/fixtures/delete-at-range/last-character/index.js new file mode 100644 index 0000000000..bc44a43136 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length - 1, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/last-character/input.yaml b/test/server/transforms/fixtures/delete-at-range/last-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-at-range/last-character/output.yaml b/test/server/transforms/fixtures/delete-at-range/last-character/output.yaml new file mode 100644 index 0000000000..870bf02c6e --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/last-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor diff --git a/test/server/transforms/fixtures/delete-at-range/middle-character/index.js b/test/server/transforms/fixtures/delete-at-range/middle-character/index.js new file mode 100644 index 0000000000..2a7343df75 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/delete-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/delete-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..b82be33b41 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/middle-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wrd diff --git a/test/server/transforms/fixtures/delete-at-range/whole-word/index.js b/test/server/transforms/fixtures/delete-at-range/whole-word/index.js new file mode 100644 index 0000000000..7c64c23b54 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/whole-word/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .deleteAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-at-range/whole-word/input.yaml b/test/server/transforms/fixtures/delete-at-range/whole-word/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/whole-word/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-at-range/whole-word/output.yaml b/test/server/transforms/fixtures/delete-at-range/whole-word/output.yaml new file mode 100644 index 0000000000..a1bc55c380 --- /dev/null +++ b/test/server/transforms/fixtures/delete-at-range/whole-word/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: "" diff --git a/test/server/transforms/fixtures/delete-backward-at-range/first-character/index.js b/test/server/transforms/fixtures/delete-backward-at-range/first-character/index.js new file mode 100644 index 0000000000..0e559de284 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-backward-at-range/first-character/input.yaml b/test/server/transforms/fixtures/delete-backward-at-range/first-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-backward-at-range/first-character/output.yaml b/test/server/transforms/fixtures/delete-backward-at-range/first-character/output.yaml new file mode 100644 index 0000000000..9414f68ce0 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/first-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: ord diff --git a/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/index.js b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/index.js new file mode 100644 index 0000000000..6c314641d0 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const second = texts.last() + const range = selection.merge({ + anchorKey: second.key, + anchorOffset: 0, + focusKey: second.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/input.yaml b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/output.yaml b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/output.yaml new file mode 100644 index 0000000000..3d5aad120c --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/join-blocks/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wordanother diff --git a/test/server/transforms/fixtures/delete-backward-at-range/last-character/index.js b/test/server/transforms/fixtures/delete-backward-at-range/last-character/index.js new file mode 100644 index 0000000000..818a8efcd9 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-backward-at-range/last-character/input.yaml b/test/server/transforms/fixtures/delete-backward-at-range/last-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-backward-at-range/last-character/output.yaml b/test/server/transforms/fixtures/delete-backward-at-range/last-character/output.yaml new file mode 100644 index 0000000000..870bf02c6e --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/last-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor diff --git a/test/server/transforms/fixtures/delete-backward-at-range/middle-character/index.js b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/index.js new file mode 100644 index 0000000000..8ec89ecdb6 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-backward-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-backward-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..b82be33b41 --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/middle-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wrd diff --git a/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/index.js b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/index.js new file mode 100644 index 0000000000..4abaece20b --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteBackwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/input.yaml b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/output.yaml b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/output.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-backward-at-range/start-of-document/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/index.js b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/index.js new file mode 100644 index 0000000000..a9306c233c --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/input.yaml b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/output.yaml b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/output.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/end-of-document/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/first-character/index.js b/test/server/transforms/fixtures/delete-forward-at-range/first-character/index.js new file mode 100644 index 0000000000..1179208a4c --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-forward-at-range/first-character/input.yaml b/test/server/transforms/fixtures/delete-forward-at-range/first-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/first-character/output.yaml b/test/server/transforms/fixtures/delete-forward-at-range/first-character/output.yaml new file mode 100644 index 0000000000..9414f68ce0 --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/first-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: ord diff --git a/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/index.js b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/index.js new file mode 100644 index 0000000000..a9306c233c --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/input.yaml b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/output.yaml b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/output.yaml new file mode 100644 index 0000000000..3d5aad120c --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/join-blocks/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wordanother diff --git a/test/server/transforms/fixtures/delete-forward-at-range/last-character/index.js b/test/server/transforms/fixtures/delete-forward-at-range/last-character/index.js new file mode 100644 index 0000000000..e04d9cca9a --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length - 1, + focusKey: first.key, + focusOffset: first.length - 1 + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-forward-at-range/last-character/input.yaml b/test/server/transforms/fixtures/delete-forward-at-range/last-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/last-character/output.yaml b/test/server/transforms/fixtures/delete-forward-at-range/last-character/output.yaml new file mode 100644 index 0000000000..870bf02c6e --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/last-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor diff --git a/test/server/transforms/fixtures/delete-forward-at-range/middle-character/index.js b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/index.js new file mode 100644 index 0000000000..4f6b36f859 --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .deleteForwardAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/delete-forward-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/delete-forward-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..b82be33b41 --- /dev/null +++ b/test/server/transforms/fixtures/delete-forward-at-range/middle-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wrd diff --git a/test/server/transforms/fixtures/insert-text-at-range/after-mark/index.js b/test/server/transforms/fixtures/insert-text-at-range/after-mark/index.js new file mode 100644 index 0000000000..d8e4847fff --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/after-mark/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 3, + focusKey: first.key, + focusOffset: 3 + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/after-mark/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/after-mark/input.yaml new file mode 100644 index 0000000000..9516919f3a --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/after-mark/input.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: or + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/after-mark/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/after-mark/output.yaml new file mode 100644 index 0000000000..721088c8c5 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/after-mark/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: ora + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/before-mark/index.js b/test/server/transforms/fixtures/insert-text-at-range/before-mark/index.js new file mode 100644 index 0000000000..5362167b39 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/before-mark/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/before-mark/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/before-mark/input.yaml new file mode 100644 index 0000000000..9516919f3a --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/before-mark/input.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: or + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/before-mark/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/before-mark/output.yaml new file mode 100644 index 0000000000..456a7d85e3 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/before-mark/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wa + - text: or + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/during-mark/index.js b/test/server/transforms/fixtures/insert-text-at-range/during-mark/index.js new file mode 100644 index 0000000000..c7fc109682 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/during-mark/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/during-mark/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/during-mark/input.yaml new file mode 100644 index 0000000000..9516919f3a --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/during-mark/input.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: or + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/during-mark/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/during-mark/output.yaml new file mode 100644 index 0000000000..d0108de731 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/during-mark/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: oar + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-character/index.js b/test/server/transforms/fixtures/insert-text-at-range/first-character/index.js new file mode 100644 index 0000000000..063803a21d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-character/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-character/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-character/output.yaml new file mode 100644 index 0000000000..a373de6284 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: aword diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-space/index.js b/test/server/transforms/fixtures/insert-text-at-range/first-space/index.js new file mode 100644 index 0000000000..41a1c6af35 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-space/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .insertTextAtRange(range, ' ') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-space/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-space/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-space/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-space/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-space/output.yaml new file mode 100644 index 0000000000..f994c96531 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-space/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: " word" diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-words/index.js b/test/server/transforms/fixtures/insert-text-at-range/first-words/index.js new file mode 100644 index 0000000000..6611e86763 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-words/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .insertTextAtRange(range, 'a few words ') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-words/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-words/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-words/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/first-words/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/first-words/output.yaml new file mode 100644 index 0000000000..b5278cbc6c --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/first-words/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: a few words word diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-character/index.js b/test/server/transforms/fixtures/insert-text-at-range/last-character/index.js new file mode 100644 index 0000000000..ba23ad5819 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-character/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-character/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-character/output.yaml new file mode 100644 index 0000000000..ad45f0c9b1 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: worda diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-space/index.js b/test/server/transforms/fixtures/insert-text-at-range/last-space/index.js new file mode 100644 index 0000000000..cee51a5333 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-space/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .insertTextAtRange(range, ' ') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-space/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-space/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-space/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-space/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-space/output.yaml new file mode 100644 index 0000000000..2061412e0b --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-space/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: "word " diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-words/index.js b/test/server/transforms/fixtures/insert-text-at-range/last-words/index.js new file mode 100644 index 0000000000..18a4214435 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-words/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .insertTextAtRange(range, ' a few words') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-words/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-words/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-words/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/last-words/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/last-words/output.yaml new file mode 100644 index 0000000000..b95f2bb1a2 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/last-words/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word a few words diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-character/index.js b/test/server/transforms/fixtures/insert-text-at-range/middle-character/index.js new file mode 100644 index 0000000000..5362167b39 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .insertTextAtRange(range, 'a') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..b0873c9567 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-character/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: waord diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-space/index.js b/test/server/transforms/fixtures/insert-text-at-range/middle-space/index.js new file mode 100644 index 0000000000..4c332b20ff --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-space/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .insertTextAtRange(range, ' ') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-space/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-space/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-space/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-space/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-space/output.yaml new file mode 100644 index 0000000000..8b72a232f6 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-space/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w ord diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-words/index.js b/test/server/transforms/fixtures/insert-text-at-range/middle-words/index.js new file mode 100644 index 0000000000..f88fb59e79 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-words/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .insertTextAtRange(range, ' a few words ') + .apply() +} diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-words/input.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-words/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-words/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/insert-text-at-range/middle-words/output.yaml b/test/server/transforms/fixtures/insert-text-at-range/middle-words/output.yaml new file mode 100644 index 0000000000..f5df738510 --- /dev/null +++ b/test/server/transforms/fixtures/insert-text-at-range/middle-words/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w a few words ord diff --git a/test/server/transforms/fixtures/mark-at-range/across-blocks/index.js b/test/server/transforms/fixtures/mark-at-range/across-blocks/index.js new file mode 100644 index 0000000000..f0a762f29c --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/across-blocks/input.yaml b/test/server/transforms/fixtures/mark-at-range/across-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/mark-at-range/across-blocks/output.yaml b/test/server/transforms/fixtures/mark-at-range/across-blocks/output.yaml new file mode 100644 index 0000000000..e28155de5b --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-blocks/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + - text: rd + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: an + marks: + - type: bold + - text: other diff --git a/test/server/transforms/fixtures/mark-at-range/across-inlines/index.js b/test/server/transforms/fixtures/mark-at-range/across-inlines/index.js new file mode 100644 index 0000000000..f0a762f29c --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/mark-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..c31f583e37 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-inlines/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/mark-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/mark-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..7bf3a7a16f --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/across-inlines/output.yaml @@ -0,0 +1,26 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: wo + - text: rd + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: an + marks: + - type: bold + - text: other diff --git a/test/server/transforms/fixtures/mark-at-range/existing-marks/index.js b/test/server/transforms/fixtures/mark-at-range/existing-marks/index.js new file mode 100644 index 0000000000..0bccf13fce --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/existing-marks/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/existing-marks/input.yaml b/test/server/transforms/fixtures/mark-at-range/existing-marks/input.yaml new file mode 100644 index 0000000000..057734ba1e --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/existing-marks/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: italic diff --git a/test/server/transforms/fixtures/mark-at-range/existing-marks/output.yaml b/test/server/transforms/fixtures/mark-at-range/existing-marks/output.yaml new file mode 100644 index 0000000000..178a19f03e --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/existing-marks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + marks: + - type: italic + - type: bold + - text: rd + marks: + - type: italic diff --git a/test/server/transforms/fixtures/mark-at-range/first-character/index.js b/test/server/transforms/fixtures/mark-at-range/first-character/index.js new file mode 100644 index 0000000000..ab40cb1a7b --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/first-character/input.yaml b/test/server/transforms/fixtures/mark-at-range/first-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/first-character/output.yaml b/test/server/transforms/fixtures/mark-at-range/first-character/output.yaml new file mode 100644 index 0000000000..01cb91f1a7 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/first-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord diff --git a/test/server/transforms/fixtures/mark-at-range/last-character/index.js b/test/server/transforms/fixtures/mark-at-range/last-character/index.js new file mode 100644 index 0000000000..1f3ccccf3e --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length - 1, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/last-character/input.yaml b/test/server/transforms/fixtures/mark-at-range/last-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/last-character/output.yaml b/test/server/transforms/fixtures/mark-at-range/last-character/output.yaml new file mode 100644 index 0000000000..d9b72a0a7b --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/last-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor + - text: d + marks: + - type: bold diff --git a/test/server/transforms/fixtures/mark-at-range/middle-character/index.js b/test/server/transforms/fixtures/mark-at-range/middle-character/index.js new file mode 100644 index 0000000000..aa750c2875 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/mark-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/mark-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..0a062933a8 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/middle-character/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: o + marks: + - type: bold + - text: rd diff --git a/test/server/transforms/fixtures/mark-at-range/whole-word/index.js b/test/server/transforms/fixtures/mark-at-range/whole-word/index.js new file mode 100644 index 0000000000..761dfcede9 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/whole-word/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .markAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/whole-word/input.yaml b/test/server/transforms/fixtures/mark-at-range/whole-word/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/whole-word/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/whole-word/output.yaml b/test/server/transforms/fixtures/mark-at-range/whole-word/output.yaml new file mode 100644 index 0000000000..fce007c77f --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/whole-word/output.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/server/transforms/fixtures/mark-at-range/with-data-object/index.js b/test/server/transforms/fixtures/mark-at-range/with-data-object/index.js new file mode 100644 index 0000000000..c6fdca25a6 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data-object/index.js @@ -0,0 +1,19 @@ + +import { Data } from '../../../../../..' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .markAtRange(range, 'bold', { key: 'value' }) + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/with-data-object/input.yaml b/test/server/transforms/fixtures/mark-at-range/with-data-object/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data-object/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/with-data-object/output.yaml b/test/server/transforms/fixtures/mark-at-range/with-data-object/output.yaml new file mode 100644 index 0000000000..01cb91f1a7 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data-object/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord diff --git a/test/server/transforms/fixtures/mark-at-range/with-data/index.js b/test/server/transforms/fixtures/mark-at-range/with-data/index.js new file mode 100644 index 0000000000..be74be71a3 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data/index.js @@ -0,0 +1,19 @@ + +import { Data } from '../../../../../..' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .markAtRange(range, 'bold', Data.create({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/mark-at-range/with-data/input.yaml b/test/server/transforms/fixtures/mark-at-range/with-data/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/mark-at-range/with-data/output.yaml b/test/server/transforms/fixtures/mark-at-range/with-data/output.yaml new file mode 100644 index 0000000000..01cb91f1a7 --- /dev/null +++ b/test/server/transforms/fixtures/mark-at-range/with-data/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord diff --git a/test/server/transforms/fixtures/set-block-at-range/across-blocks/index.js b/test/server/transforms/fixtures/set-block-at-range/across-blocks/index.js new file mode 100644 index 0000000000..6b4a6286de --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .setBlockAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/across-blocks/input.yaml b/test/server/transforms/fixtures/set-block-at-range/across-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-block-at-range/across-blocks/output.yaml b/test/server/transforms/fixtures/set-block-at-range/across-blocks/output.yaml new file mode 100644 index 0000000000..885a287297 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-blocks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: code + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: code + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-block-at-range/across-inlines/index.js b/test/server/transforms/fixtures/set-block-at-range/across-inlines/index.js new file mode 100644 index 0000000000..6b4a6286de --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .setBlockAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/set-block-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..c31f583e37 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-inlines/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-block-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/set-block-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..33daef92ba --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/across-inlines/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: code + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: code + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-block-at-range/data-only/index.js b/test/server/transforms/fixtures/set-block-at-range/data-only/index.js new file mode 100644 index 0000000000..2017ebf4e2 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/data-only/index.js @@ -0,0 +1,19 @@ + +import { Map } from 'immutable' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setBlockAtRange(range, new Map({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/data-only/input.yaml b/test/server/transforms/fixtures/set-block-at-range/data-only/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/data-only/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/data-only/output.yaml b/test/server/transforms/fixtures/set-block-at-range/data-only/output.yaml new file mode 100644 index 0000000000..591f9d1e11 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/data-only/output.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + data: + key: value + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/nested-block/index.js b/test/server/transforms/fixtures/set-block-at-range/nested-block/index.js new file mode 100644 index 0000000000..5298b3d5cf --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/nested-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setBlockAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/nested-block/input.yaml b/test/server/transforms/fixtures/set-block-at-range/nested-block/input.yaml new file mode 100644 index 0000000000..ae0ad40e5e --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/nested-block/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/nested-block/output.yaml b/test/server/transforms/fixtures/set-block-at-range/nested-block/output.yaml new file mode 100644 index 0000000000..9b66aa6c31 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/nested-block/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: block + type: code + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/single-block/index.js b/test/server/transforms/fixtures/set-block-at-range/single-block/index.js new file mode 100644 index 0000000000..5298b3d5cf --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/single-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setBlockAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/single-block/input.yaml b/test/server/transforms/fixtures/set-block-at-range/single-block/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/single-block/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/single-block/output.yaml b/test/server/transforms/fixtures/set-block-at-range/single-block/output.yaml new file mode 100644 index 0000000000..e20ab29e72 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/single-block/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: code + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/with-data/index.js b/test/server/transforms/fixtures/set-block-at-range/with-data/index.js new file mode 100644 index 0000000000..3206cfeed7 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/with-data/index.js @@ -0,0 +1,19 @@ + +import { Map } from 'immutable' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setBlockAtRange(range, 'code', new Map({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/set-block-at-range/with-data/input.yaml b/test/server/transforms/fixtures/set-block-at-range/with-data/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/with-data/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-block-at-range/with-data/output.yaml b/test/server/transforms/fixtures/set-block-at-range/with-data/output.yaml new file mode 100644 index 0000000000..f56d4e2807 --- /dev/null +++ b/test/server/transforms/fixtures/set-block-at-range/with-data/output.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: code + data: + key: value + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/across-inlines/index.js b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/index.js new file mode 100644 index 0000000000..496973d767 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .setInlineAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-inline-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..c31f583e37 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-inline-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..4263a23c51 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/across-inlines/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: code + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: code + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/set-inline-at-range/data-only/index.js b/test/server/transforms/fixtures/set-inline-at-range/data-only/index.js new file mode 100644 index 0000000000..bb24100ae2 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/data-only/index.js @@ -0,0 +1,19 @@ + +import { Map } from 'immutable' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setInlineAtRange(range, new Map({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/set-inline-at-range/data-only/input.yaml b/test/server/transforms/fixtures/set-inline-at-range/data-only/input.yaml new file mode 100644 index 0000000000..395351b4f1 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/data-only/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/data-only/output.yaml b/test/server/transforms/fixtures/set-inline-at-range/data-only/output.yaml new file mode 100644 index 0000000000..8dc03cf88d --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/data-only/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + data: + key: value + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/nested-inline/index.js b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/index.js new file mode 100644 index 0000000000..db565c5e01 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setInlineAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-inline-at-range/nested-inline/input.yaml b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/input.yaml new file mode 100644 index 0000000000..6ed138519b --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: hashtag + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/nested-inline/output.yaml b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/output.yaml new file mode 100644 index 0000000000..b1b7e276ec --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/nested-inline/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: hashtag + nodes: + - kind: inline + type: code + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/single-inline/index.js b/test/server/transforms/fixtures/set-inline-at-range/single-inline/index.js new file mode 100644 index 0000000000..db565c5e01 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/single-inline/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setInlineAtRange(range, 'code') + .apply() +} diff --git a/test/server/transforms/fixtures/set-inline-at-range/single-inline/input.yaml b/test/server/transforms/fixtures/set-inline-at-range/single-inline/input.yaml new file mode 100644 index 0000000000..395351b4f1 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/single-inline/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/single-inline/output.yaml b/test/server/transforms/fixtures/set-inline-at-range/single-inline/output.yaml new file mode 100644 index 0000000000..5db5042f6a --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/single-inline/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: code + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/with-data/index.js b/test/server/transforms/fixtures/set-inline-at-range/with-data/index.js new file mode 100644 index 0000000000..ff7431c8ed --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/with-data/index.js @@ -0,0 +1,19 @@ + +import { Map } from 'immutable' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .setInlineAtRange(range, 'code', new Map({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/set-inline-at-range/with-data/input.yaml b/test/server/transforms/fixtures/set-inline-at-range/with-data/input.yaml new file mode 100644 index 0000000000..395351b4f1 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/with-data/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/set-inline-at-range/with-data/output.yaml b/test/server/transforms/fixtures/set-inline-at-range/with-data/output.yaml new file mode 100644 index 0000000000..0e2088cd86 --- /dev/null +++ b/test/server/transforms/fixtures/set-inline-at-range/with-data/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: code + data: + key: value + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/block-end/index.js b/test/server/transforms/fixtures/split-block-at-range/block-end/index.js new file mode 100644 index 0000000000..c47f7c8d49 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-end/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .splitBlockAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/split-block-at-range/block-end/input.yaml b/test/server/transforms/fixtures/split-block-at-range/block-end/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-end/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/block-end/output.yaml b/test/server/transforms/fixtures/split-block-at-range/block-end/output.yaml new file mode 100644 index 0000000000..f86b0e9de0 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-end/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: "" diff --git a/test/server/transforms/fixtures/split-block-at-range/block-middle/index.js b/test/server/transforms/fixtures/split-block-at-range/block-middle/index.js new file mode 100644 index 0000000000..d3bb315642 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-middle/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .splitBlockAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/split-block-at-range/block-middle/input.yaml b/test/server/transforms/fixtures/split-block-at-range/block-middle/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-middle/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/block-middle/output.yaml b/test/server/transforms/fixtures/split-block-at-range/block-middle/output.yaml new file mode 100644 index 0000000000..0537306d51 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-middle/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: rd diff --git a/test/server/transforms/fixtures/split-block-at-range/block-start/index.js b/test/server/transforms/fixtures/split-block-at-range/block-start/index.js new file mode 100644 index 0000000000..d4a4e11a43 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-start/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .splitBlockAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/split-block-at-range/block-start/input.yaml b/test/server/transforms/fixtures/split-block-at-range/block-start/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-start/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/block-start/output.yaml b/test/server/transforms/fixtures/split-block-at-range/block-start/output.yaml new file mode 100644 index 0000000000..3e64b4a974 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/block-start/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: "" + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/index.js b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/index.js new file mode 100644 index 0000000000..40f13856e6 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .splitBlockAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/input.yaml b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/output.yaml b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/output.yaml new file mode 100644 index 0000000000..69c1496d93 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete-across-blocks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: other diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete/index.js b/test/server/transforms/fixtures/split-block-at-range/with-delete/index.js new file mode 100644 index 0000000000..f3e80a2fc4 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 3 + }) + + return state + .transform() + .splitBlockAtRange(range) + .apply() +} diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete/input.yaml b/test/server/transforms/fixtures/split-block-at-range/with-delete/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/split-block-at-range/with-delete/output.yaml b/test/server/transforms/fixtures/split-block-at-range/with-delete/output.yaml new file mode 100644 index 0000000000..58febc8d53 --- /dev/null +++ b/test/server/transforms/fixtures/split-block-at-range/with-delete/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: d diff --git a/test/server/transforms/fixtures/unmark-at-range/across-blocks/index.js b/test/server/transforms/fixtures/unmark-at-range/across-blocks/index.js new file mode 100644 index 0000000000..2f4164301a --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/across-blocks/input.yaml b/test/server/transforms/fixtures/unmark-at-range/across-blocks/input.yaml new file mode 100644 index 0000000000..14db1f6dba --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-blocks/input.yaml @@ -0,0 +1,18 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/across-blocks/output.yaml b/test/server/transforms/fixtures/unmark-at-range/across-blocks/output.yaml new file mode 100644 index 0000000000..837e4e330d --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-blocks/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + marks: + - type: bold + - text: rd + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: an + - text: other + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/across-inlines/index.js b/test/server/transforms/fixtures/unmark-at-range/across-inlines/index.js new file mode 100644 index 0000000000..2f4164301a --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/unmark-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..68d1f01fce --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-inlines/input.yaml @@ -0,0 +1,24 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/unmark-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..68568ba657 --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/across-inlines/output.yaml @@ -0,0 +1,26 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: wo + marks: + - type: bold + - text: rd + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: an + - text: other + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/existing-marks/index.js b/test/server/transforms/fixtures/unmark-at-range/existing-marks/index.js new file mode 100644 index 0000000000..67686382fd --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/existing-marks/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/existing-marks/input.yaml b/test/server/transforms/fixtures/unmark-at-range/existing-marks/input.yaml new file mode 100644 index 0000000000..e92972a11d --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/existing-marks/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: italic + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/existing-marks/output.yaml b/test/server/transforms/fixtures/unmark-at-range/existing-marks/output.yaml new file mode 100644 index 0000000000..217fbdebf2 --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/existing-marks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + marks: + - type: italic + - text: rd + marks: + - type: italic + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/first-character/index.js b/test/server/transforms/fixtures/unmark-at-range/first-character/index.js new file mode 100644 index 0000000000..f8b39860f8 --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/first-character/input.yaml b/test/server/transforms/fixtures/unmark-at-range/first-character/input.yaml new file mode 100644 index 0000000000..fce007c77f --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/first-character/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/first-character/output.yaml b/test/server/transforms/fixtures/unmark-at-range/first-character/output.yaml new file mode 100644 index 0000000000..7ee8affffe --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/first-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: ord + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/last-character/index.js b/test/server/transforms/fixtures/unmark-at-range/last-character/index.js new file mode 100644 index 0000000000..7c0949b674 --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: first.length - 1, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/last-character/input.yaml b/test/server/transforms/fixtures/unmark-at-range/last-character/input.yaml new file mode 100644 index 0000000000..fce007c77f --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/last-character/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/last-character/output.yaml b/test/server/transforms/fixtures/unmark-at-range/last-character/output.yaml new file mode 100644 index 0000000000..253cf57d9c --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/last-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor + marks: + - type: bold + - text: d diff --git a/test/server/transforms/fixtures/unmark-at-range/middle-character/index.js b/test/server/transforms/fixtures/unmark-at-range/middle-character/index.js new file mode 100644 index 0000000000..0c6226f55e --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 2 + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/middle-character/input.yaml b/test/server/transforms/fixtures/unmark-at-range/middle-character/input.yaml new file mode 100644 index 0000000000..fce007c77f --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/middle-character/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/middle-character/output.yaml b/test/server/transforms/fixtures/unmark-at-range/middle-character/output.yaml new file mode 100644 index 0000000000..b061698b5d --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/middle-character/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: o + - text: rd + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/whole-word/index.js b/test/server/transforms/fixtures/unmark-at-range/whole-word/index.js new file mode 100644 index 0000000000..e14846e4b4 --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/whole-word/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: first.length + }) + + return state + .transform() + .unmarkAtRange(range, 'bold') + .apply() +} diff --git a/test/server/transforms/fixtures/unmark-at-range/whole-word/input.yaml b/test/server/transforms/fixtures/unmark-at-range/whole-word/input.yaml new file mode 100644 index 0000000000..fce007c77f --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/whole-word/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/server/transforms/fixtures/unmark-at-range/whole-word/output.yaml b/test/server/transforms/fixtures/unmark-at-range/whole-word/output.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/unmark-at-range/whole-word/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/index.js new file mode 100644 index 0000000000..c38e668295 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/input.yaml new file mode 100644 index 0000000000..87b381f139 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/input.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/output.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-blocks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/index.js new file mode 100644 index 0000000000..69e1049807 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..c31f583e37 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..f4bfc73036 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/across-inlines/output.yaml @@ -0,0 +1,23 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/index.js new file mode 100644 index 0000000000..baf30d8b9f --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/input.yaml new file mode 100644 index 0000000000..c4ac2e6c55 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/output.yaml new file mode 100644 index 0000000000..5dd1c2b592 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/nested-block/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/single-block/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/index.js new file mode 100644 index 0000000000..baf30d8b9f --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/single-block/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/single-block/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/output.yaml new file mode 100644 index 0000000000..c4ac2e6c55 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/single-block/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/index.js new file mode 100644 index 0000000000..724fcd3af5 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote', { key: 'value' }) + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/output.yaml new file mode 100644 index 0000000000..ad222fcde6 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data-object/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: quote + data: + key: value + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data/index.js b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/index.js new file mode 100644 index 0000000000..20d8e292f9 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/index.js @@ -0,0 +1,19 @@ + +import { Data } from '../../../../../..' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote', Data.create({ key: 'value' })) + .apply() +} diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data/input.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/input.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/unwrap-block-at-range/with-data/output.yaml b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/output.yaml new file mode 100644 index 0000000000..ad222fcde6 --- /dev/null +++ b/test/server/transforms/fixtures/unwrap-block-at-range/with-data/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: quote + data: + key: value + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/index.js b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/index.js new file mode 100644 index 0000000000..69e1049807 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .wrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/input.yaml new file mode 100644 index 0000000000..4960c06f94 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/output.yaml new file mode 100644 index 0000000000..87b381f139 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-blocks/output.yaml @@ -0,0 +1,17 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/index.js b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/index.js new file mode 100644 index 0000000000..c38e668295 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/input.yaml new file mode 100644 index 0000000000..f4bfc73036 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/input.yaml @@ -0,0 +1,23 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/output.yaml new file mode 100644 index 0000000000..c31f583e37 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/across-inlines/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/server/transforms/fixtures/wrap-block-at-range/nested-block/index.js b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/index.js new file mode 100644 index 0000000000..7a6bc5f614 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/nested-block/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/input.yaml new file mode 100644 index 0000000000..5dd1c2b592 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/nested-block/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/output.yaml new file mode 100644 index 0000000000..c4ac2e6c55 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/nested-block/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/single-block/index.js b/test/server/transforms/fixtures/wrap-block-at-range/single-block/index.js new file mode 100644 index 0000000000..7a6bc5f614 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/single-block/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote') + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/single-block/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/single-block/input.yaml new file mode 100644 index 0000000000..c4ac2e6c55 --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/single-block/input.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: quote + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/single-block/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/single-block/output.yaml new file mode 100644 index 0000000000..b1be31e90d --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/single-block/output.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/index.js b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/index.js new file mode 100644 index 0000000000..e248eeaf8d --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote', { thing: 'one' }) + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/input.yaml new file mode 100644 index 0000000000..3836e4e21c --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/input.yaml @@ -0,0 +1,18 @@ + +nodes: + - kind: block + type: quote + data: + thing: one + nodes: + - kind: block + type: quote + data: + thing: two + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/output.yaml new file mode 100644 index 0000000000..de4353710e --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data-object/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: quote + data: + thing: two + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data/index.js b/test/server/transforms/fixtures/wrap-block-at-range/with-data/index.js new file mode 100644 index 0000000000..a63753a78f --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data/index.js @@ -0,0 +1,19 @@ + +import { Data } from '../../../../../..' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const range = selection.merge({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + + return state + .transform() + .unwrapBlockAtRange(range, 'quote', Data.create({ thing: 'one' })) + .apply() +} diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data/input.yaml b/test/server/transforms/fixtures/wrap-block-at-range/with-data/input.yaml new file mode 100644 index 0000000000..3836e4e21c --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data/input.yaml @@ -0,0 +1,18 @@ + +nodes: + - kind: block + type: quote + data: + thing: one + nodes: + - kind: block + type: quote + data: + thing: two + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/fixtures/wrap-block-at-range/with-data/output.yaml b/test/server/transforms/fixtures/wrap-block-at-range/with-data/output.yaml new file mode 100644 index 0000000000..de4353710e --- /dev/null +++ b/test/server/transforms/fixtures/wrap-block-at-range/with-data/output.yaml @@ -0,0 +1,13 @@ + +nodes: + - kind: block + type: quote + data: + thing: two + nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/server/transforms/index.js b/test/server/transforms/index.js new file mode 100644 index 0000000000..115f622c70 --- /dev/null +++ b/test/server/transforms/index.js @@ -0,0 +1,38 @@ + +import assert from 'assert' +import fs from 'fs' +import readMetadata from 'read-metadata' +import toCamel from 'to-camel-case' +import { Raw, State } from '../../..' +import { equal } from '../../helpers/assert-json' +import { resolve } from 'path' + +/** + * Tests. + */ + +describe('transforms', () => { + const dir = resolve(__dirname, './fixtures') + const transforms = fs.readdirSync(dir) + + for (const transform of transforms) { + describe(toCamel(transform), () => { + const dir = resolve(__dirname, './fixtures', transform) + const tests = fs.readdirSync(dir) + + for (const test of tests) { + it(test, () => { + const innerDir = resolve(dir, test) + const fn = require(innerDir).default + const input = readMetadata.sync(resolve(innerDir, 'input.yaml')) + const expected = readMetadata.sync(resolve(innerDir, 'output.yaml')) + + let state = Raw.deserialize(input) + state = fn(state) + const output = Raw.serialize(state) + equal(output, expected) + }) + } + }) + } +})