New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

prosemirror

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

prosemirror - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

AUTHORS

34

CHANGELOG.md

@@ -0,1 +1,35 @@

## [0.5.0](http://prosemirror.net/version/0.5.0.html) (2016-03-22)
### Bug fixes
ProseMirror now ignores most evens when not focused, so you can have
focusable fields inside the editor.
The Markdown serializer is now a lot more clever about serializing
mixed inline styles.
Event handlers unregistering themselves is now safe (used to skip next
event handler).
### New features
The default command parameter prompt UI now shows the command label
and a submit button.
When joining an empty textblock with a non-empty one, the resulting
block now gets the type of the non-empty one.
Node types can now handle double clicks with a `handleDoubleClick`
method.
Undo and redo now restore the selection that was current when the
history event was created.
The collab module now fires a `"collabTransform"` event when receiving
changes.
The `"filterTransform"` event can now be used to cancel transforms.
Node kinds can now specify both their super- and sub-kinds.
## [0.4.0](http://prosemirror.net/version/0.4.0.html) (2016-02-24)

@@ -2,0 +36,0 @@

15

CONTRIBUTING.md

@@ -45,5 +45,9 @@ # How to contribute

([how to fork a repo](https://help.github.com/articles/fork-a-repo))
- Make your changes, and commit them
- Follow the code style of the rest of the project (see below). Run
`npm run lint` to verify that the linter is happy (you'll need to
run `npm install` first).
- If your changes are easy to test or likely to regress, add tests in

@@ -53,8 +57,7 @@ the `test/` directory. Either put them in an existing `test-*.js`

- Follow the code style of the rest of the project (see below). Run
`npm run lint` to verify that the linter is happy (you'll need to
run `npm install` first).
- Make sure all tests pass. Run `npm run test` verify tests pass.
Also run browser tests by starting the demo (`npm run demo`) and
visiting [http://localhost:8080/test.html](http://localhost:8080/test.html)
in a browser.
- Make sure all tests pass.
- Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)).

@@ -61,0 +64,0 @@ Don't put more than one feature/fix in a single pull request.

23

dist/collab/index.js

@@ -16,2 +16,4 @@ "use strict";

var _transform = require("../transform");
var _rebase = require("./rebase");

@@ -151,18 +153,21 @@

value: function receive(steps) {
var doc = this.versionDoc;
var maps = steps.map(function (step) {
var result = step.apply(doc);
doc = result.doc;
return result.map;
var transform = new _transform.Transform(this.versionDoc);
steps.forEach(function (step) {
return transform.step(step);
});
this.version += steps.length;
this.versionDoc = doc;
this.versionDoc = transform.doc;
var rebased = (0, _rebase.rebaseSteps)(doc, maps, this.unconfirmedSteps, this.unconfirmedMaps);
var rebased = (0, _rebase.rebaseSteps)(transform.doc, transform.maps, this.unconfirmedSteps, this.unconfirmedMaps);
this.unconfirmedSteps = rebased.transform.steps.slice();
this.unconfirmedMaps = rebased.transform.maps.slice();
var selectionBefore = this.pm.selection;
this.pm.updateDoc(rebased.doc, rebased.mapping);
this.pm.history.rebased(maps, rebased.transform, rebased.positions);
return maps;
this.pm.history.rebased(transform.maps, rebased.transform, rebased.positions);
// :: (transform: Transform, selectionBeforeTransform: Selection) #path=Collab#events#collabTransform
// Signals that a transformation has been aplied to the editor. Passes the `Transform` and the selection
// before the transform as arguments to the handler.
this.signal("collabTransform", transform, selectionBefore);
return transform.maps;
}

@@ -169,0 +174,0 @@ }]);

@@ -84,3 +84,7 @@ "use strict";

after = around.child(cut.offset);
if (before.type.canContainContent(after.type) && pm.tr.join(cut).apply(pm.apply.scroll) !== false) return;
if (before.type.canContainContent(after.type)) {
var tr = pm.tr.join(cut);
if (tr.steps.length && before.size == 0 && !before.sameMarkup(after)) tr.setNodeType(cut.move(-1), after.type, after.attrs);
if (tr.apply(pm.apply.scroll) !== false) return;
}

@@ -132,2 +136,10 @@ var conn = undefined;

// If the node below has no content and the node above is
// selectable, delete the node below and select the one above.
if (before.type.contains == null && before.type.selectable && pm.doc.path(head.path).size == 0) {
var tr = pm.tr.delete(cut, cut.move(1)).apply(pm.apply.scroll);
pm.setNodeSelection(cut.move(-1));
return tr;
}
// If the node doesn't allow children, delete it

@@ -377,3 +389,3 @@ if (before.type.contains == null) return pm.tr.delete(cut.move(-1), cut).apply(pm.apply.scroll);

//
// **Keybindings:** Alt-Left
// **Keybindings:** Ctrl-[
baseCommands.lift = {

@@ -404,3 +416,3 @@ label: "Lift out of enclosing block",

},
keys: ["Alt-Left"]
keys: ["Mod-["]
};

@@ -588,3 +600,6 @@

keys: ["Left", "Mod-Left"]
keys: {
all: ["Left", "Mod-Left"],
mac: ["Alt-Left"]
}
};

@@ -604,5 +619,12 @@

keys: ["Right", "Mod-Right"]
keys: {
all: ["Right", "Mod-Right"],
mac: ["Alt-Left"]
}
};
// : (ProseMirror, number)
// Check whether vertical selection motion would involve node
// selections. If so, apply it (if not, the result is left to the
// browser)
function selectNodeVertically(pm, dir) {

@@ -609,0 +631,0 @@ var _pm$selection17 = pm.selection;

@@ -6,3 +6,6 @@ "use strict";

});
exports.applyDOMChange = applyDOMChange;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
exports.readDOMChange = readDOMChange;
exports.textContext = textContext;

@@ -17,2 +20,4 @@ exports.textInContext = textInContext;

var _selection = require("./selection");
var _dompos = require("./dompos");

@@ -67,12 +72,24 @@

function applyDOMChange(pm) {
function readDOMChange(pm) {
var updated = parseNearSelection(pm);
var changeStart = (0, _model.findDiffStart)(pm.doc.content, updated.content);
if (changeStart) {
var changeEnd = findDiffEndConstrained(pm.doc.content, updated.content, changeStart);
// Mark nodes touched by this change as 'to be redrawn'
markDirtyFor(pm, changeStart, changeEnd);
var _ret = function () {
var changeEnd = findDiffEndConstrained(pm.doc.content, updated.content, changeStart);
// Mark nodes touched by this change as 'to be redrawn'
markDirtyFor(pm, changeStart, changeEnd);
pm.tr.replace(changeStart, changeEnd.a, updated, changeStart, changeEnd.b).apply();
return true;
var near = undefined;
// FIXME when we have a Slice type, just return replace info, & let caller inspect it
if (pm.doc.path(changeStart.path).isTextblock && _model.Pos.samePath(changeStart.path, changeEnd.a.path) && !_model.Pos.samePath(changeStart.path, changeEnd.b.path) && (near = (0, _selection.findSelectionFrom)(updated, after(updated, changeStart), 1, true)) && !near.head.cmp(changeEnd.b)) return {
v: { type: "enter" }
};else return {
v: { type: "replace",
run: function run() {
return pm.tr.replace(changeStart, changeEnd.a, updated, changeStart, changeEnd.b).apply();
} }
};
}();
if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;
} else {

@@ -83,2 +100,6 @@ return false;

function after(doc, pos) {
if (pos.offset < doc.path(pos.path).size) return pos.move(1);else return pos.shorten(null, 1);
}
function offsetBy(first, second, pos) {

@@ -85,0 +106,0 @@ var same = (0, _tree.samePathDepth)(first, second);

@@ -353,2 +353,7 @@ "use strict";

// :: (pm: ProseMirror, event: MouseEvent, path: [number], node: Node) → bool
// #path=NodeType.prototype.handleDoubleClick
// This works like [`handleClick`](#NodeType.handleClick), but is
// called for double clicks instead.
// :: (pm: ProseMirror, event: MouseEvent, path: [number], node: Node) → bool
// #path=NodeType.prototype.handleContextMenu

@@ -355,0 +360,0 @@ //

@@ -24,8 +24,8 @@ "use strict";

onRender: function onRender(node, dom, offset) {
if (!node.isText && node.type.contains == null) {
dom.contentEditable = false;
if (node.isBlock) dom.setAttribute("pm-leaf", "true");
if (node.isBlock) {
if (node.type.contains == null) dom.setAttribute("pm-leaf", "true");
if (offset != null) dom.setAttribute("pm-offset", offset);
if (node.isTextblock) adjustTrailingHacks(dom, node);
if (dom.contentEditable == "false") dom = (0, _dom.elt)("div", dom);
}
if (node.isBlock && offset != null) dom.setAttribute("pm-offset", offset);
if (node.isTextblock) adjustTrailingHacks(dom, node);

@@ -32,0 +32,0 @@ return dom;

@@ -27,2 +27,13 @@ "use strict";

var HistoryEvent = function HistoryEvent(steps, selection) {
_classCallCheck(this, HistoryEvent);
this.steps = steps;
this.selection = selection;
};
// Assists with remapping a step with other changes that have been
// made since the step was first applied.
var BranchRemapping = function () {

@@ -34,2 +45,4 @@ function BranchRemapping(branch) {

this.remap = new _transform.Remapping();
// Track the internal version of what step the current remapping collection
// would put the content at.
this.version = branch.version;

@@ -39,2 +52,6 @@ this.mirrorBuffer = Object.create(null);

// Add all position maps between the current version
// and the desired version to the remapping collection.
_createClass(BranchRemapping, [{

@@ -47,2 +64,6 @@ key: "moveToVersion",

}
// Add the next map at the current version to the
// remapping collection.
}, {

@@ -69,5 +90,13 @@ key: "addNextMap",

var workTime = 100,
pauseTime = 150;
// The number of milliseconds the compression worker has to compress.
var workTime = 100;
// The number of milliseconds to pause compression worker if it uses
// all its work time.
var pauseTime = 150;
// Help compress steps in events for a branch.
var CompressionWorker = function () {

@@ -91,2 +120,5 @@ function CompressionWorker(doc, branch, callback) {

// Compress steps in all events in the branch.
_createClass(CompressionWorker, [{

@@ -103,9 +135,10 @@ key: "work",

if (this.i == 0) return this.finish();
var event = this.branch.events[--this.i],
outEvent = [];
for (var j = event.length - 1; j >= 0; j--) {
var _event$j = event[j];
var step = _event$j.step;
var stepVersion = _event$j.version;
var stepID = _event$j.id;
var event = this.branch.events[--this.i];
var mappedSelection = event.selection && event.selection.map(this.doc, this.remap.remap);
var outEvent = new HistoryEvent([], mappedSelection);
for (var j = event.steps.length - 1; j >= 0; j--) {
var _event$steps$j = event.steps[j];
var step = _event$steps$j.step;
var stepVersion = _event$steps$j.version;
var stepID = _event$steps$j.id;

@@ -115,2 +148,4 @@ this.remap.moveToVersion(stepVersion);

var mappedStep = step.map(this.remap.remap);
// Combine contiguous delete steps.
if (mappedStep && isDelStep(step)) {

@@ -120,3 +155,3 @@ var extra = 0,

while (j > 0) {
var next = event[j - 1];
var next = event.steps[j - 1];
if (next.version != stepVersion - 1 || !isDelStep(next.step) || start.cmp(next.step.to)) break;

@@ -138,3 +173,3 @@ extra += next.step.to.offset - next.step.from.offset;

this.maps.push(result.map.invert());
outEvent.push(new InvertedStep(mappedStep, this.version, stepID));
outEvent.steps.push(new InvertedStep(mappedStep, this.version, stepID));
this.version--;

@@ -144,4 +179,4 @@ }

}
if (outEvent.length) {
outEvent.reverse();
if (outEvent.steps.length) {
outEvent.steps.reverse();
this.events.push(outEvent);

@@ -181,2 +216,3 @@ }

// The minimum number of new steps before a compression is started.
var compressStepCount = 150;

@@ -213,7 +249,12 @@

}
// : (Selection)
// Create a new history event at tip of the branch.
}, {
key: "newEvent",
value: function newEvent() {
value: function newEvent(currentSelection) {
this.abortCompression();
this.events.push([]);
this.events.push(new HistoryEvent([], currentSelection));
while (this.events.length > this.maxDepth) {

@@ -223,2 +264,8 @@ this.events.shift();

}
// : (PosMap)
// Add a position map to the branch, either representing one of the
// changes recorded in the branch, or representing a non-history
// change that the branch's changes must be mapped through.
}, {

@@ -234,2 +281,6 @@ key: "addMap",

}
// : () → bool
// Whether the branch is empty (has no history events).
}, {

@@ -245,3 +296,3 @@ key: "empty",

if (id == null) id = this.nextStepID++;
this.events[this.events.length - 1].push(new InvertedStep(step, this.version, id));
this.events[this.events.length - 1].steps.push(new InvertedStep(step, this.version, id));
}

@@ -262,3 +313,3 @@

// : (Node, bool) → ?{transform: Transform, ids: [number]}
// : (Node, bool) → ?{transform: Transform, ids: [number], selection: Selection}
// Pop the latest event off the branch's history and apply it

@@ -279,10 +330,12 @@ // to a document transform, returning the transform and the step ID.

for (var i = event.length - 1; i >= 0; i--) {
var invertedStep = event[i],
for (var i = event.steps.length - 1; i >= 0; i--) {
var invertedStep = event.steps[i],
step = invertedStep.step;
if (!collapsing || invertedStep.version != remap.version) {
collapsing = false;
// Remap the step through any position mappings unrelated to
// history (e.g. collaborative edits).
remap.moveToVersion(invertedStep.version);
step = step.map(remap.remap);
step = step.map(remap.remap);
var result = step && tr.step(step);

@@ -294,3 +347,3 @@ if (result) {

if (i > 0) remap.movePastStep(result);
remap.movePastStep(result);
} else {

@@ -305,4 +358,6 @@ this.version--;

}
var selection = event.selection && event.selection.map(tr.doc, remap.remap);
if (this.empty()) this.clear(true);
return { transform: tr, ids: ids };
return { transform: tr, ids: ids, selection: selection };
}

@@ -314,3 +369,3 @@ }, {

var event = this.events[i];
if (event.length) return event[event.length - 1];
if (event.steps.length) return event.steps[event.steps.length - 1];
}

@@ -333,6 +388,10 @@ }

value: function findVersion(version) {
// FIXME this is not accurate when the actual revision has fallen
// off the end of the history. Current representation of versions
// does not allow us to recognize that case.
if (version.lastID == null) return { event: 0, step: 0 };
for (var i = this.events.length - 1; i >= 0; i--) {
var event = this.events[i];
for (var j = event.length - 1; j >= 0; j--) {
if (event[j].id <= version.lastID) return { event: i, step: j + 1 };
for (var j = event.steps.length - 1; j >= 0; j--) {
if (event.steps[j].id <= version.lastID) return { event: i, step: j + 1 };
}

@@ -352,11 +411,11 @@ }

var event = this.events[i];
for (var j = event.length - 1; j >= 0; j--) {
var step = event[j];
for (var j = event.steps.length - 1; j >= 0; j--) {
var step = event.steps[j];
if (step.version <= startVersion) break out;
var off = positions[step.version - startVersion - 1];
if (off == -1) {
event.splice(j--, 1);
event.steps.splice(j--, 1);
} else {
var inv = rebasedTransform.steps[off].invert(rebasedTransform.docs[off], rebasedTransform.maps[off]);
event[j] = new InvertedStep(inv, startVersion + newMaps.length + off + 1, step.id);
event.steps[j] = new InvertedStep(inv, startVersion + newMaps.length + off + 1, step.id);
}

@@ -405,2 +464,5 @@ }

// Delay between transforms required to compress steps.
var compressDelay = 750;

@@ -426,4 +488,4 @@

pm.on("transform", function (transform, options) {
return _this3.recordTransform(transform, options);
pm.on("transform", function (transform, selection, options) {
return _this3.recordTransform(transform, selection, options);
});

@@ -438,3 +500,3 @@ }

key: "recordTransform",
value: function recordTransform(transform, options) {
value: function recordTransform(transform, selection, options) {
if (this.ignoreTransform) return;

@@ -452,3 +514,3 @@

// Group transforms that occur in quick succession into one event.
if (now > this.lastAddedAt + this.pm.options.historyEventDelay) this.done.newEvent();
if (now > this.lastAddedAt + this.pm.options.historyEventDelay) this.done.newEvent(selection);

@@ -502,6 +564,8 @@ this.done.addTransform(transform);

var ids = event.ids;
var selection = event.selection;
var selectionBeforeTransform = this.pm.selection;
this.ignoreTransform = true;
this.pm.apply(transform);
this.pm.apply(transform, { selection: selection });
this.ignoreTransform = false;

@@ -512,3 +576,7 @@

if (to) {
to.newEvent();
// Store the selection before transform on the event so that
// it can be reapplied if the event is undone or redone (e.g.
// redoing a character addition should place the cursor after
// the character).
to.newEvent(selectionBeforeTransform);
to.addTransform(transform, ids);

@@ -556,8 +624,10 @@ }

var event = this.done.events[found.event];
if (found.event == this.done.events.length - 1 && found.step == event.length) return true;
var combined = this.done.events.slice(found.event + 1).reduce(function (comb, arr) {
return comb.concat(arr);
}, event.slice(found.step));
this.done.events.length = found.event + ((event.length = found.step) ? 1 : 0);
this.done.events.push(combined);
if (found.event == this.done.events.length - 1 && found.step == event.steps.length) return true;
// Combine all steps past the verion to rollback to into
// one event, and then "undo" that event.
var combinedSteps = this.done.events.slice(found.event + 1).reduce(function (comb, arr) {
return comb.concat(arr.steps);
}, event.steps.slice(found.step));
this.done.events.length = found.event + ((event.steps.length = found.step) ? 1 : 0);
this.done.events.push(new HistoryEvent(combinedSteps, null));

@@ -580,2 +650,6 @@ this.shift(this.done);

}
// : (Branch)
// Schedule compression for a branch if it needs compressing.
}, {

@@ -582,0 +656,0 @@ key: "maybeScheduleCompressionForBranch",

@@ -165,2 +165,3 @@ "use strict";

// menus.
if (!(0, _selection.hasFocus)(pm)) return;
pm.signal("interaction");

@@ -190,3 +191,3 @@ if (e.keyCode == 16) pm.input.shiftKey = true;

handlers.keypress = function (pm, e) {
if (pm.input.composing || !e.charCode || e.ctrlKey && !e.altKey || _dom.browser.mac && e.metaKey) return;
if (!(0, _selection.hasFocus)(pm) || pm.input.composing || !e.charCode || e.ctrlKey && !e.altKey || _dom.browser.mac && e.metaKey) return;
if (dispatchKey(pm, _browserkeymap2.default.keyName(e), e)) return;

@@ -247,3 +248,3 @@ var sel = pm.selection;

if (tripleClick) handleTripleClick(pm, e);else pm.input.mouseDown = new MouseDown(pm, e, doubleClick);
if (tripleClick) handleTripleClick(pm, e);else if (doubleClick && (0, _dompos.handleNodeClick)(pm, "handleDoubleClick", e, true)) {} else pm.input.mouseDown = new MouseDown(pm, e, doubleClick);
};

@@ -286,11 +287,11 @@

key: "up",
value: function up() {
value: function up(event) {
this.done();
if (this.leaveToBrowser) {
if (this.leaveToBrowser || !(0, _dom.contains)(this.pm.content, event.target)) {
this.pm.sel.fastPoll();
} else if (this.event.ctrlKey) {
selectClickedNode(this.pm, this.event);
} else if (!(0, _dompos.handleNodeClick)(this.pm, "handleClick", this.event, true)) {
var pos = (0, _dompos.selectableNodeAbove)(this.pm, this.event.target, { left: this.x, top: this.y });
selectClickedNode(this.pm, event);
} else if (!(0, _dompos.handleNodeClick)(this.pm, "handleClick", event, true)) {
var pos = (0, _dompos.selectableNodeAbove)(this.pm, event.target, { left: this.x, top: this.y });
if (pos) {

@@ -343,3 +344,3 @@ this.pm.setNodeSelection(pos);

handlers.compositionstart = function (pm, e) {
if (pm.input.maybeAbortComposition()) return;
if (!(0, _selection.hasFocus)(pm) || pm.input.maybeAbortComposition()) return;

@@ -353,2 +354,3 @@ pm.flush();

handlers.compositionupdate = function (pm, e) {
if (!(0, _selection.hasFocus)(pm)) return;
var info = pm.input.composing;

@@ -365,2 +367,3 @@ if (info && info.data != e.data) {

handlers.compositionend = function (pm, e) {
if (!(0, _selection.hasFocus)(pm)) return;
var info = pm.input.composing;

@@ -386,3 +389,4 @@ if (info) {

handlers.input = function (pm) {
handlers.input = function (pm, e) {
if (!(0, _selection.hasFocus)(pm)) return;
if (pm.input.skipInput) return --pm.input.skipInput;

@@ -396,3 +400,6 @@

pm.startOperation({ readSelection: false });
(0, _domchange.applyDOMChange)(pm);
var change = (0, _domchange.readDOMChange)(pm);
if (change) {
if (change.type == "enter") dispatchKey(pm, "Enter", e);else change.run();
}
pm.scrollIntoView();

@@ -508,2 +515,3 @@ };

handlers.paste = function (pm, e) {
if (!(0, _selection.hasFocus)(pm)) return;
if (!e.clipboardData) return;

@@ -510,0 +518,0 @@ var sel = pm.selection;

@@ -276,2 +276,7 @@ "use strict";

//
// **`filter`**: ?bool
// : When set to false, suppresses the ability of the
// [`"filterTransform"` event](#ProseMirror.event_beforeTransform)
// to cancel this transform.
//
// Returns the transform, or `false` if there were no steps in it.

@@ -286,2 +291,10 @@ //

// :: (transform: Transform) #path=ProseMirror#events#filterTransform
// Fired before a transform (applied without `filter: false`) is
// applied. The handler can return a truthy value to cancel the
// transform.
if (options.filter !== false && this.signalHandleable("filterTransform", transform)) return false;
var selectionBeforeTransform = this.selection;
// :: (transform: Transform, options: Object) #path=ProseMirror#events#beforeTransform

@@ -294,7 +307,8 @@ // Indicates that the given transform is about to be

this.updateDoc(transform.doc, transform, options.selection);
// :: (transfom: Transform, options: Object) #path=ProseMirror#events#transform
// :: (transform: Transform, selectionBeforeTransform: Selection, options: Object) #path=ProseMirror#events#transform
// Signals that a (non-empty) transformation has been aplied to
// the editor. Passes the `Transform` and the options given to
// [`apply`](#ProseMirror.apply) as arguments to the handler.
this.signal("transform", transform, options);
// the editor. Passes the `Transform`, the selection before the
// transform, and the options given to [`apply`](#ProseMirror.apply)
// as arguments to the handler.
this.signal("transform", transform, selectionBeforeTransform, options);
if (options.scrollIntoView) this.scrollIntoView();

@@ -635,3 +649,14 @@ return transform;

}
// :: (string) → string
// Return a translated string, if a translate function has been supplied,
// or the original string.
}, {
key: "translate",
value: function translate(string) {
var trans = this.options.translate;
return trans ? trans(string) : string;
}
}, {
key: "selection",

@@ -638,0 +663,0 @@ get: function get() {

@@ -21,2 +21,6 @@ "use strict";

// An option encapsulates functionality for an editor instance,
// e.g. the amount of history events that the editor should hold
// onto or the document's schema.
var Option = function Option(defaultValue, update, updateOnInit) {

@@ -26,2 +30,5 @@ _classCallCheck(this, Option);

this.defaultValue = defaultValue;
// A function that will be invoked with the option's old and new
// value every time the option is [set](#ProseMirror.setOption).
// This function should bootstrap option functionality.
this.update = update;

@@ -94,2 +101,8 @@ this.updateOnInit = updateOnInit !== false;

// :: ?(string) → string #path=translate #kind=option
// Optional function to translate strings such as menu labels and prompts.
// When set, should be a function that takes a string as argument and returns
// a string, i.e. :: (string) → string
defineOption("translate", null); // FIXME create a way to explicitly force a menu redraw
function parseOptions(obj) {

@@ -96,0 +109,0 @@ var result = Object.create(null);

@@ -177,3 +177,3 @@ "use strict";

//
// **Keybindings:** Alt-Right '*', Alt-Right '-'
// **Keybindings:** Shift-Mod-8
_model.BulletList.register("command", "wrap", {

@@ -190,3 +190,3 @@ derive: { list: true },

},
keys: ["Alt-Right '*'", "Alt-Right '-'"]
keys: ["Shift-Mod-8"]
});

@@ -197,3 +197,3 @@

//
// **Keybindings:** Alt-Right '1'
// **Keybindings:** Shift-Mod-8
_model.OrderedList.register("command", "wrap", {

@@ -210,3 +210,3 @@ derive: { list: true },

},
keys: ["Alt-Right '1'"]
keys: ["Shift-Mod-9"]
});

@@ -217,3 +217,3 @@

//
// **Keybindings:** Alt-Right '>', Alt-Right '"'
// **Keybindings:** Shift-Mod-.
_model.BlockQuote.register("command", "wrap", {

@@ -230,3 +230,3 @@ derive: true,

},
keys: ["Alt-Right '>'", "Alt-Right '\"'"]
keys: ["Shift-Mod-."]
});

@@ -283,3 +283,3 @@

//
// **Keybindings:** Mod-1 through Mod-6
// **Keybindings:** Shift-Mod-1 through Shift-Mod-6
_model.Heading.registerComputed("command", "make" + i, function (type) {

@@ -289,3 +289,3 @@ if (i <= type.maxLevel) return {

label: "Change to heading " + i,
keys: i < 10 && ["Mod-" + i],
keys: i <= 6 && ["Shift-Mod-" + i],
menu: {

@@ -305,7 +305,7 @@ group: "textblockHeading", rank: 30 + i,

//
// **Keybindings:** Mod-0
// **Keybindings:** Shift-Mod-0
_model.Paragraph.register("command", "make", {
derive: true,
label: "Change to paragraph",
keys: ["Mod-0"],
keys: ["Shift-Mod-0"],
menu: {

@@ -321,7 +321,7 @@ group: "textblock", rank: 10,

//
// **Keybindings:** Mod-\
// **Keybindings:** Shift-Mod-\
_model.CodeBlock.register("command", "make", {
derive: true,
label: "Change to code block",
keys: ["Mod-\\"],
keys: ["Shift-Mod-\\"],
menu: {

@@ -328,0 +328,0 @@ group: "textblock", rank: 20,

@@ -164,3 +164,10 @@ "use strict";

var newRange = findSelectionNear(doc, head, this.range.head && this.range.head.cmp(head) < 0 ? -1 : 1);
if (newRange instanceof TextSelection && doc.path(anchor.path).isTextblock) newRange = new TextSelection(anchor, newRange.head);
if (newRange instanceof TextSelection && doc.path(anchor.path).isTextblock) {
newRange = new TextSelection(anchor, newRange.head);
} else if (newRange instanceof NodeSelection && (anchor.cmp(newRange.from) < 0 || anchor.cmp(newRange.to) > 0)) {
// If head falls on a node, but anchor falls outside of it,
// create a text selection between them
var inv = anchor.cmp(newRange.to) > 0;
newRange = new TextSelection(findSelectionNear(doc, anchor, inv ? -1 : 1, true).anchor, findSelectionNear(doc, inv ? newRange.from : newRange.to, inv ? 1 : -1, true).head);
}
this.setAndSignal(newRange);

@@ -269,6 +276,6 @@

// :: Pos #path=Selection.prototype.from
// The start of the selection.
// The left-bound of the selection.
// :: Pos #path=Selection.prototype.to
// The end of the selection.
// The right-bound of the selection.

@@ -413,2 +420,3 @@ // :: bool #path=Selection.empty

function hasFocus(pm) {
if (document.activeElement != pm.content) return false;
var sel = window.getSelection();

@@ -418,2 +426,4 @@ return sel.rangeCount && (0, _dom.contains)(pm.content, sel.anchorNode);

// Try to find a selection inside the node at the given path coming
// from a given direction.
function findSelectionIn(doc, path, offset, dir, text) {

@@ -423,2 +433,4 @@ var node = doc.path(path);

// Iterate over child nodes recursively coming from the given
// direction and return the first viable selection.
for (var i = offset + (dir > 0 ? 0 : -1); dir > 0 ? i < node.size : i >= 0; i += dir) {

@@ -436,2 +448,6 @@ var child = node.child(i);

// Create a selection which is moved relative to a position in a
// given direction. When a selection isn't found at the given position,
// walks up the document tree one level and one step in the
// desired direction.
function findSelectionFrom(doc, pos, dir, text) {

@@ -455,2 +471,3 @@ for (var path = pos.path.slice(), offset = pos.offset;;) {

// Find the selection closes to the start of the given node.
function findSelectionAtStart(node) {

@@ -463,2 +480,3 @@ var path = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];

// Find the selection closes to the end of the given node.
function findSelectionAtEnd(node) {

@@ -471,2 +489,5 @@ var path = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];

// : (ProseMirror, Pos, number)
// Whether vertical position motion in a given direction
// from a position would leave a text block.
function verticalMotionLeavesTextblock(pm, pos, dir) {

@@ -473,0 +494,0 @@ var dom = (0, _dompos.pathToDOM)(pm.content, pos.path);

@@ -235,3 +235,3 @@ "use strict";

def(_model.HorizontalRule, function (_, s) {
return s.elt("hr");
return s.elt("div", null, s.elt("hr"));
});

@@ -238,0 +238,0 @@

@@ -168,3 +168,3 @@ "use strict";

for (var i = parent.iter(0, pos.offset), child; child = i.next().value;) {
if (child.isText) textBefore += child.text;else textBefore = "";
if (child.isText) textBefore += child.text;else textBefore += "";
if (i.atEnd() && child.marks.some(function (st) {

@@ -171,0 +171,0 @@ return st.type.isCode;

@@ -171,30 +171,44 @@ "use strict";

var stack = [];
var active = [];
var progress = function progress(node) {
var marks = node ? node.marks.slice() : [];
if (stack.length && stack[stack.length - 1].type == "code" && (!marks.length || marks[marks.length - 1].type != "code")) {
_this2.text("`", false);
stack.pop();
}
for (var j = 0; j < stack.length; j++) {
var cur = stack[j],
found = false;
for (var k = 0; k < marks.length; k++) {
if (marks[k].eq(stack[j])) {
marks.splice(k, 1);
found = true;
break;
var marks = node ? node.marks : [];
var code = marks.length && marks[marks.length - 1].type.isCode && marks[marks.length - 1];
var len = marks.length - (code ? 1 : 0);
// Try to reorder 'mixable' marks, such as em and strong, which
// in Markdown may be opened and closed in different order, so
// that order of the marks for the token matches the order in
// active.
outer: for (var i = 0; i < len; i++) {
var mark = marks[i];
if (!mark.type.markdownMixable) break;
for (var j = 0; j < active.length; j++) {
var other = active[j];
if (!other.type.markdownMixable) break;
if (mark.eq(other)) {
if (i > j) marks = marks.slice(0, j).concat(mark).concat(marks.slice(j, i)).concat(marks.slice(i + 1), len);else if (j > i) marks = marks.slice(0, i).concat(marks.slice(i + 1, j)).concat(mark).concat(marks.slice(j, len));
continue outer;
}
}
if (!found) {
_this2.text(_this2.markString(cur, false), false);
stack.splice(j--, 1);
}
}
for (var j = 0; j < marks.length; j++) {
var cur = marks[j];
stack.push(cur);
_this2.text(_this2.markString(cur, true), false);
// Find the prefix of the mark set that didn't change
var keep = 0;
while (keep < Math.min(active.length, len) && marks[keep].eq(active[keep])) {
++keep;
} // Close the marks that need to be closed
while (keep < active.length) {
_this2.text(_this2.markString(active.pop(), false), false);
} // Open the marks that need to be opened
while (active.length < len) {
var add = marks[active.length];
active.push(add);
_this2.text(_this2.markString(add, true), false);
}
if (node) _this2.render(node);
// Render the node. Special case code marks, since their content
// may not be escaped.
if (node) {
if (code && node.isText) _this2.text(_this2.markString(code, false) + node.text + _this2.markString(code, true), false);else _this2.render(node);
}
};

@@ -257,2 +271,6 @@ parent.forEach(progress);

}
// : (Mark, bool) → string
// Get the markdown string for a given opening or closing mark.
}, {

@@ -345,15 +363,13 @@ key: "markString",

function defMark(mark, open, close) {
mark.prototype.openMarkdown = open;
mark.prototype.closeMarkdown = close;
}
_model.EmMark.prototype.openMarkdown = _model.EmMark.prototype.closeMarkdown = "*";
_model.EmMark.prototype.markdownMixable = true;
defMark(_model.EmMark, "*", "*");
_model.StrongMark.prototype.openMarkdown = _model.StrongMark.prototype.closeMarkdown = "**";
_model.StrongMark.prototype.markdownMixable = true;
defMark(_model.StrongMark, "**", "**");
defMark(_model.LinkMark, "[", function (state, mark) {
_model.LinkMark.prototype.openMarkdown = "[";
_model.LinkMark.prototype.closeMarkdown = function (state, mark) {
return "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")";
});
};
defMark(_model.CodeMark, "`", "`");
_model.CodeMark.prototype.openMarkdown = _model.CodeMark.prototype.closeMarkdown = "`";

@@ -53,4 +53,5 @@ "use strict";

if (!command.label) return null;
var label = pm.translate(command.label);
var key = command.name && pm.keyForCommand(command.name);
return key ? command.label + " (" + key + ")" : command.label;
return key ? label + " (" + key + ")" : label;
}

@@ -80,3 +81,6 @@

}
}, {
key: "render",
// :: (ProseMirror) → DOMNode

@@ -86,5 +90,2 @@ // Renders the command according to its [display

// executes the command when the representation is clicked.
}, {
key: "render",
value: function render(pm) {

@@ -108,3 +109,4 @@ var cmd = this.command(pm),

} else if (disp.type == "label") {
dom = (0, _dom.elt)("div", null, disp.label || cmd.spec.label);
var label = pm.translate(disp.label || cmd.spec.label);
dom = (0, _dom.elt)("div", null, label);
} else {

@@ -122,4 +124,10 @@ throw new _error.AssertionError("Unsupported command display style: " + disp.type);

});
dom.setAttribute("data-command", this.commandName);
return dom;
}
}, {
key: "commandName",
get: function get() {
return typeof this.command_ === "string" ? this.command_.command : this.command_.name;
}
}]);

@@ -239,2 +247,3 @@

var label = this.options.activeLabel && this.findActiveIn(this, pm) || this.options.label;
label = pm.translate(label);
var dom = (0, _dom.elt)("div", { class: prefix + "-dropdown " + (this.options.class || ""),

@@ -335,3 +344,3 @@ style: this.options.css,

var label = (0, _dom.elt)("div", { class: prefix + "-submenu-label" }, this.options.label);
var label = (0, _dom.elt)("div", { class: prefix + "-submenu-label" }, pm.translate(this.options.label));
var wrap = (0, _dom.elt)("div", { class: prefix + "-submenu-wrap" }, label, (0, _dom.elt)("div", { class: prefix + "-submenu" }, items));

@@ -338,0 +347,0 @@ label.addEventListener("mousedown", function (e) {

@@ -76,3 +76,3 @@ "use strict";

this.selectedBlockMenu = this.config.selectedBlockMenu;
this.updater = new _update.UpdateScheduler(pm, "change selectionChange blur commandsChanged", function () {
this.updater = new _update.UpdateScheduler(pm, "change selectionChange blur focus commandsChanged", function () {
return _this.update();

@@ -79,0 +79,0 @@ });

@@ -319,3 +319,3 @@ "use strict";

get: function get() {
return 51;
return 31;
}

@@ -342,3 +342,3 @@ }]);

get: function get() {
return 52;
return 32;
}

@@ -376,3 +376,3 @@ }]);

get: function get() {
return 25;
return 60;
}

@@ -379,0 +379,0 @@ }]);

@@ -507,3 +507,4 @@ "use strict";

if (end > this.offset) {
var sliceEnd = node.width;
var sliceEnd = node.width,
sliceStart = this.offset - offset;
if (end > this.endOffset) {

@@ -513,3 +514,3 @@ sliceEnd = this.endOffset - offset;

}
node = node.copy(node.text.slice(this.offset - offset, sliceEnd));
node = sliceEnd > sliceStart ? node.copy(node.text.slice(this.offset - offset, sliceEnd)) : null;
this.offset = end;

@@ -516,0 +517,0 @@ return node;

@@ -18,2 +18,4 @@ "use strict";

var _schema = require("./schema");
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

@@ -560,7 +562,8 @@

var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TextNode).call(this, type, attrs, null, marks));
if (!content) throw new _schema.SchemaError("Empty text nodes are not allowed");
// :: ?string
// For text nodes, this contains the node's text content.
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TextNode).call(this, type, attrs, null, marks));
_this.text = content;

@@ -567,0 +570,0 @@ return _this;

@@ -453,9 +453,10 @@ "use strict";

var NodeKind = exports.NodeKind = function () {
// :: (string, [NodeKind])
// :: (string, ?[NodeKind], ?[NodeKind])
// Create a new node kind with the given set of superkinds (the new
// kind counts as a member of each of the superkinds). The `name`
// field is only for debugging purposes—kind equivalens is defined
// by identity.
// kind counts as a member of each of the superkinds) and subkinds
// (which will count as a member of this new kind). The `name` field
// is only for debugging purposes—kind equivalens is defined by
// identity.
function NodeKind(name) {
function NodeKind(name, supers, subs) {
var _this4 = this;

@@ -466,23 +467,45 @@

this.name = name;
// FIXME temporary backwards-compatibility kludge
if (supers && supers instanceof NodeKind) {
supers = Array.prototype.slice.call(arguments, 1);
subs = null;
}
this.id = ++NodeKind.nextID;
this.supers = Object.create(null);
this.id = ++NodeKind.nextID;
this.supers[this.id] = true;
this.subs = subs || [];
for (var _len = arguments.length, supers = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
supers[_key - 1] = arguments[_key];
}
if (supers) supers.forEach(function (sup) {
return _this4.addSuper(sup);
});
if (subs) subs.forEach(function (sub) {
return _this4.addSub(sub);
});
}
supers.forEach(function (sup) {
_createClass(NodeKind, [{
key: "addSuper",
value: function addSuper(sup) {
for (var id in sup.supers) {
_this4.supers[id] = true;
this.supers[id] = true;
sup.subs.push(this);
}
});
}
}
}, {
key: "addSub",
value: function addSub(sub) {
var _this5 = this;
// :: (NodeKind) → bool
// Test whether `other` is a subkind of this kind (or the same
// kind).
if (this.supers[sub.id]) throw new SchemaError("Circular subkind relation");
sub.supers[this.id] = true;
sub.subs.forEach(function (next) {
return _this5.addSub(next);
});
}
// :: (NodeKind) → bool
// Test whether `other` is a subkind of this kind (or the same
// kind).
_createClass(NodeKind, [{
}, {
key: "isSubKind",

@@ -507,3 +530,3 @@ value: function isSubKind(other) {

// `NodeKind.inline`.
NodeKind.text = new NodeKind("text", NodeKind.inline);
NodeKind.text = new NodeKind("text", [NodeKind.inline]);

@@ -710,13 +733,13 @@ // ;; Base type for block nodetypes.

var _this9 = _possibleConstructorReturn(this, Object.getPrototypeOf(MarkType).call(this));
var _this10 = _possibleConstructorReturn(this, Object.getPrototypeOf(MarkType).call(this));
_this9.name = name;
_this9.freezeAttrs();
_this9.rank = rank;
_this10.name = name;
_this10.freezeAttrs();
_this10.rank = rank;
// :: Schema
// The schema that this mark type instance is part of.
_this9.schema = schema;
var defaults = _this9.getDefaultAttrs();
_this9.instance = defaults && new _mark.Mark(_this9, defaults);
return _this9;
_this10.schema = schema;
var defaults = _this10.getDefaultAttrs();
_this10.instance = defaults && new _mark.Mark(_this10, defaults);
return _this10;
}

@@ -913,3 +936,4 @@

// :: (string, ?[Mark]) → Node
// Create a text node in the schema. This method is bound to the Schema.
// Create a text node in the schema. This method is bound to the
// Schema. Empty text nodes are not allowed.

@@ -916,0 +940,0 @@ }, {

@@ -152,3 +152,3 @@ "use strict";

type(pm1, "E");
conv(pm1, pm2, "ACDE");
conv(pm1, pm2, "ADCE");
});

@@ -155,0 +155,0 @@

@@ -62,2 +62,3 @@ "use strict";

test("joinBackward", (0, _build.doc)(_build.hr, (0, _build.p)("<a>there")), (0, _build.doc)((0, _build.p)("there")));
test("joinBackward", (0, _build.doc)(_build.hr, (0, _build.p)("<a>"), _build.hr), (0, _build.doc)(_build.hr, _build.hr));
test("joinBackward", (0, _build.doc)(_build.hr, (0, _build.blockquote)((0, _build.p)("<a>there"))), (0, _build.doc)((0, _build.blockquote)((0, _build.p)("there"))));

@@ -64,0 +65,0 @@ test("joinBackward", (0, _build.doc)((0, _build.p)("<a>foo")), (0, _build.doc)((0, _build.p)("foo")));

@@ -15,5 +15,9 @@ "use strict";

function apply(pm) {
(0, _domchange.readDOMChange)(pm).run();
}
test("add_text", function (pm) {
(0, _testSelection.findTextNode)(pm.content, "hello").nodeValue = "heLllo";
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("heLllo")));

@@ -24,3 +28,3 @@ });

(0, _testSelection.findTextNode)(pm.content, "hello").nodeValue = "heo";
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("heo")));

@@ -32,3 +36,3 @@ });

txt.parentNode.appendChild(document.createTextNode("!"));
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("hello!")));

@@ -40,3 +44,3 @@ });

txt.parentNode.appendChild(document.createElement("em")).appendChild(document.createTextNode("!"));
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("hello", (0, _build.em)("!"))));

@@ -48,3 +52,3 @@ });

txt.parentNode.removeChild(txt);
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)()));

@@ -55,3 +59,3 @@ });

pm.content.insertBefore(document.createElement("p"), pm.content.firstChild).appendChild(document.createTextNode("hey"));
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("hey"), (0, _build.p)("hello")));

@@ -62,3 +66,3 @@ });

pm.content.insertBefore(document.createElement("p"), pm.content.firstChild).appendChild(document.createTextNode("hello"));
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("hello"), (0, _build.p)("hello")));

@@ -69,4 +73,11 @@ });

(0, _testSelection.findTextNode)(pm.content, "hello").nodeValue = "helhello";
(0, _domchange.applyDOMChange)(pm);
apply(pm);
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("helhello")));
});
test("detect_enter", function (pm) {
(0, _testSelection.findTextNode)(pm.content, "hello").nodeValue = "hel";
pm.content.appendChild(document.createElement("p")).innerHTML = "lo";
var change = (0, _domchange.readDOMChange)(pm);
(0, _cmp.cmp)(change && change.type, "enter");
});

@@ -15,3 +15,3 @@ "use strict";

function cut(pm) {
function cutHistory(pm) {
pm.history.lastAddedAt = 0;

@@ -39,3 +39,3 @@ }

type(pm, "a");
cut(pm);
cutHistory(pm);
type(pm, "b");

@@ -65,3 +65,3 @@ (0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("ab")));

type(pm, "hello");
cut(pm);
cutHistory(pm);
type(pm, "!");

@@ -80,3 +80,3 @@ pm.tr.insertText((0, _cmp.P)(0, 0), "....").apply();

type(pm, "hello");
cut(pm);
cutHistory(pm);
pm.tr.delete((0, _cmp.P)(0, 0), (0, _cmp.P)(0, 5)).apply();

@@ -93,3 +93,3 @@ (0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)()));

type(pm, "hello");
cut(pm);
cutHistory(pm);
pm.tr.delete((0, _cmp.P)(0, 0), (0, _cmp.P)(0, 5)).apply();

@@ -105,3 +105,3 @@ (0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)()));

type(pm, "hi");
cut(pm);
cutHistory(pm);
type(pm, "hello");

@@ -117,6 +117,6 @@ pm.tr.delete((0, _cmp.P)(0, 0), (0, _cmp.P)(0, 7)).apply({ addToHistory: false });

type(pm, " two");
cut(pm);
cutHistory(pm);
type(pm, " three");
pm.tr.insertText((0, _cmp.P)(0, 0), "zero ").apply();
cut(pm);
cutHistory(pm);
pm.tr.split((0, _cmp.P)(0, 0)).apply();

@@ -136,7 +136,7 @@ pm.setTextSelection((0, _cmp.P)(0, 0));

type(pm, " two");
cut(pm);
cutHistory(pm);
pm.tr.insertText(pm.selection.head, "xxx").apply({ addToHistory: false });
type(pm, " three");
pm.tr.insertText((0, _cmp.P)(0, 0), "zero ").apply();
cut(pm);
cutHistory(pm);
pm.tr.split((0, _cmp.P)(0, 0)).apply();

@@ -158,3 +158,3 @@ pm.setTextSelection((0, _cmp.P)(0, 0));

pm.setTextSelection((0, _cmp.P)(0, 1));
cut(pm);
cutHistory(pm);
type(pm, "one");

@@ -168,4 +168,6 @@ type(pm, "two");

(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("XY!")));
(0, _cmp.cmpStr)(pm.selection.anchor, (0, _cmp.P)(0, 1));
pm.execCommand("redo");
(0, _cmp.cmpNode)(pm.doc, (0, _build.doc)((0, _build.p)("XonetwothreeY!")));
(0, _cmp.cmpStr)(pm.selection.anchor, (0, _cmp.P)(0, 12));
});

@@ -182,3 +184,3 @@

type(pm, "hello");
cut(pm);
cutHistory(pm);
var version = pm.history.getVersion();

@@ -199,3 +201,3 @@ type(pm, "ok");

type(pm, "ok");
cut(pm);
cutHistory(pm);
type(pm, "more");

@@ -208,2 +210,25 @@ (0, _cmp.is)(pm.history.backToVersion(version), "rollback");

(0, _cmp.is)(!pm.history.backToVersion(version), "failed rollback");
});
test("setSelectionOnUndo", function (pm) {
type(pm, "hi");
cutHistory(pm);
pm.setTextSelection((0, _cmp.P)(0, 0), (0, _cmp.P)(0, 2));
var selection = pm.selection;
pm.tr.replaceWith(selection.from, selection.to, pm.schema.text("hello")).apply();
var selection2 = pm.selection;
pm.execCommand("undo");
(0, _cmp.is)(pm.selection.eq(selection), "failed restoring selection after undo");
pm.execCommand("redo");
(0, _cmp.is)(pm.selection.eq(selection2), "failed restoring selection after redo");
});
test("rebaseSelectionOnUndo", function (pm) {
type(pm, "hi");
cutHistory(pm);
pm.setTextSelection((0, _cmp.P)(0, 0), (0, _cmp.P)(0, 2));
pm.tr.insert((0, _cmp.P)(0, 0), pm.schema.text("hello")).apply();
pm.tr.insert((0, _cmp.P)(0, 0), pm.schema.text("---")).apply({ addToHistory: false });
pm.execCommand("undo");
(0, _cmp.cmpStr)(pm.selection.head, (0, _cmp.P)(0, 5));
});

@@ -11,2 +11,5 @@ "use strict";

// : (string) → Function
// Create a function for creating inline nodes with brevity.
// For use with `doc()`.
function buildInline(style) {

@@ -18,2 +21,5 @@ return function () {

// : (string, Object) → Function
// Create a function which creating block nodes with brevity.
// For use with `doc()`.
function build(type, attrs) {

@@ -68,2 +74,5 @@ return function () {

// : (...nodes: Node) → Doc
// Create a document node. Child nodes can be added with
// abbreviated node notation, see `build()`.
function doc() {

@@ -70,0 +79,0 @@ var content = [];

@@ -40,4 +40,8 @@ "use strict";

t("inline_overlap", "This is **strong *emphasized text with `code` in* it**", (0, _build.doc)((0, _build.p)("This is ", (0, _build.strong)("strong ", (0, _build.em)("emphasized text with ", (0, _build.code)("code"), " in"), " it"))));
t("inline_overlap_mix", "This is **strong *emphasized text with `code` in* it**", (0, _build.doc)((0, _build.p)("This is ", (0, _build.strong)("strong ", (0, _build.em)("emphasized text with ", (0, _build.code)("code"), " in"), " it"))));
t("inline_overlap_link", "**[link](http://foo) is bold**", (0, _build.doc)((0, _build.p)((0, _build.strong)((0, _build.a)("link"), " is bold"))));
t("inline_overlap_code", "**`code` is bold**", (0, _build.doc)((0, _build.p)((0, _build.strong)((0, _build.code)("code"), " is bold"))));
t("link", "My [link](http://foo) goes to foo", (0, _build.doc)((0, _build.p)("My ", (0, _build.a)("link"), " goes to foo")));

@@ -44,0 +48,0 @@

@@ -10,2 +10,6 @@ "use strict";

// :(string, Function)
// Define a test. A test should include a descriptive name and
// a function which runs the test. If a test fails, it should
// throw a Failure.
function defTest(name, f) {

@@ -12,0 +16,0 @@ if (name in tests) throw new Error("Duplicate definition of test " + name);

@@ -23,3 +23,3 @@ "use strict";

return new _step.StepResult((0, _tree.copyStructure)(doc, step.from, step.to, function (node, from, to) {
if (!node.type.canContainMark(step.param)) return node;
if (!node.type.canContainMark(step.param.type)) return node;
return (0, _tree.copyInline)(node, from, to, function (node) {

@@ -26,0 +26,0 @@ return node.mark(step.param.addToSet(node.marks));

@@ -53,7 +53,13 @@ "use strict";

});
var promptTitle = (0, _dom.elt)("h5", {}, command.spec && command.spec.label ? pm.translate(command.spec.label) : "");
var submitButton = (0, _dom.elt)("button", { type: "submit", class: "ProseMirror-prompt-submit" }, "Ok");
var cancelButton = (0, _dom.elt)("button", { type: "button", class: "ProseMirror-prompt-cancel" }, "Cancel");
cancelButton.addEventListener("click", function () {
return _this.close();
});
// :: DOMNode
// An HTML form wrapping the fields.
this.form = (0, _dom.elt)("form", null, this.fields.map(function (f) {
this.form = (0, _dom.elt)("form", null, promptTitle, this.fields.map(function (f) {
return (0, _dom.elt)("div", null, f);
}));
}), (0, _dom.elt)("div", { class: "ProseMirror-prompt-buttons" }, submitButton, " ", cancelButton));
}

@@ -234,3 +240,3 @@

return (0, _dom.elt)("input", { type: "text",
placeholder: param.label,
placeholder: this.translate(param.label),
value: value,

@@ -246,5 +252,7 @@ autocomplete: "off" });

render: function render(param, value) {
var _this4 = this;
var options = param.options.call ? param.options(this) : param.options;
return (0, _dom.elt)("select", null, options.map(function (o) {
return (0, _dom.elt)("option", { value: o.value, selected: o.value == value ? "true" : null }, o.label);
return (0, _dom.elt)("option", { value: o.value, selected: o.value == value ? "true" : null }, _this4.translate(o.label));
}));

@@ -277,3 +285,3 @@ },

wrapper.style.left = options.pos.left - outerBox.left + "px";
wrapper.style.pos = options.pos.top - outerBox.top + "px";
wrapper.style.top = options.pos.top - outerBox.top + "px";
} else {

@@ -299,2 +307,2 @@ var blockBox = wrapper.getBoundingClientRect();

(0, _dom.insertCSS)("\n.ProseMirror-prompt {\n background: white;\n padding: 2px 6px 2px 15px;\n border: 1px solid silver;\n position: absolute;\n border-radius: 3px;\n z-index: 11;\n}\n\n.ProseMirror-prompt input[type=\"text\"],\n.ProseMirror-prompt textarea {\n background: #eee;\n border: none;\n outline: none;\n}\n\n.ProseMirror-prompt input[type=\"text\"] {\n padding: 0 4px;\n}\n\n.ProseMirror-prompt-close {\n position: absolute;\n left: 2px; top: 1px;\n color: #666;\n border: none; background: transparent; padding: 0;\n}\n\n.ProseMirror-prompt-close:after {\n content: \"✕\";\n font-size: 12px;\n}\n\n.ProseMirror-invalid {\n background: #ffc;\n border: 1px solid #cc7;\n border-radius: 4px;\n padding: 5px 10px;\n position: absolute;\n min-width: 10em;\n}\n");
(0, _dom.insertCSS)("\n.ProseMirror-prompt {\n background: white;\n padding: 2px 6px 2px 15px;\n border: 1px solid silver;\n position: absolute;\n border-radius: 3px;\n z-index: 11;\n}\n\n.ProseMirror-prompt h5 {\n margin: 0;\n font-weight: normal;\n font-size: 100%;\n color: #444;\n}\n\n.ProseMirror-prompt input[type=\"text\"],\n.ProseMirror-prompt textarea {\n background: #eee;\n border: none;\n outline: none;\n}\n\n.ProseMirror-prompt input[type=\"text\"] {\n padding: 0 4px;\n}\n\n.ProseMirror-prompt-close {\n position: absolute;\n left: 2px; top: 1px;\n color: #666;\n border: none; background: transparent; padding: 0;\n}\n\n.ProseMirror-prompt-close:after {\n content: \"✕\";\n font-size: 12px;\n}\n\n.ProseMirror-invalid {\n background: #ffc;\n border: 1px solid #cc7;\n border-radius: 4px;\n padding: 5px 10px;\n position: absolute;\n min-width: 10em;\n}\n\n.ProseMirror-prompt-buttons {\n margin-top: 5px;\n display: none;\n}\n\n");

@@ -23,12 +23,15 @@ "use strict";

var Tooltip = exports.Tooltip = function () {
// :: (DOMNode, string)
// :: (DOMNode, ?union<string, Object)
// Create a new tooltip that lives in the wrapper node, which should
// be its offset anchor, i.e. it should have a `relative` or
// `absolute` CSS position. You'll often want to pass an editor's
// [`wrapper` node](#ProseMirror.wrapper). `dir` may be `"above"`,
// `"below"`, `"right"`, `"left"`, or `"center"`. In the latter
// case, the tooltip has no arrow and is positioned centered in its
// wrapper node.
// [`wrapper` node](#ProseMirror.wrapper). `options` may be an object
// containg a `direction` string and a `getBoundingRect` function which
// should return a rectangle determining the space in which the tooltip
// may appear. Alternatively, `options` may be a string specifying the
// direction. The direction can be `"above"`, `"below"`, `"right"`,
// `"left"`, or `"center"`. In the latter case, the tooltip has no arrow
// and is positioned centered in its wrapper node.
function Tooltip(wrapper, dir) {
function Tooltip(wrapper, options) {
var _this = this;

@@ -39,3 +42,4 @@

this.wrapper = wrapper;
this.dir = dir || "above";
this.options = typeof options == "string" ? { direction: options } : options;
this.dir = this.options.direction || "above";
this.pointer = wrapper.appendChild((0, _dom.elt)("div", { class: prefix + "-pointer-" + this.dir + " " + prefix + "-pointer" }));

@@ -91,2 +95,6 @@ this.pointerWidth = this.pointerHeight = null;

// Use the window as the bounding rectangle if no getBoundingRect
// function is defined
var boundingRect = (this.options.getBoundingRect || windowRect)();
for (var child = this.dom.firstChild, next; child; child = next) {

@@ -110,3 +118,3 @@ next = child.nextSibling;

if (this.dir == "above" || this.dir == "below") {
var tipLeft = Math.max(0, Math.min(left - size.width / 2, window.innerWidth - size.width));
var tipLeft = Math.max(boundingRect.left, Math.min(left - size.width / 2, boundingRect.right - size.width));
this.dom.style.left = tipLeft - around.left + "px";

@@ -138,4 +146,4 @@ this.pointer.style.left = left - around.left - this.pointerWidth / 2 + "px";

} else if (this.dir == "center") {
var _top = Math.max(around.top, 0),
bottom = Math.min(around.bottom, window.innerHeight);
var _top = Math.max(around.top, boundingRect.top),
bottom = Math.min(around.bottom, boundingRect.bottom);
var fromTop = (bottom - _top - size.height) / 2;

@@ -168,2 +176,9 @@ this.dom.style.left = (around.width - size.width) / 2 + "px";

function windowRect() {
return {
left: 0, right: window.innerWidth,
top: 0, bottom: window.innerHeight
};
}
(0, _dom.insertCSS)("\n\n." + prefix + " {\n position: absolute;\n display: none;\n box-sizing: border-box;\n -moz-box-sizing: border- box;\n overflow: hidden;\n\n -webkit-transition: width 0.4s ease-out, height 0.4s ease-out, left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n -moz-transition: width 0.4s ease-out, height 0.4s ease-out, left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n transition: width 0.4s ease-out, height 0.4s ease-out, left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n opacity: 0;\n\n border-radius: 5px;\n padding: 3px 7px;\n margin: 0;\n background: white;\n border: 1px solid #777;\n color: #555;\n\n z-index: 11;\n}\n\n." + prefix + "-pointer {\n position: absolute;\n display: none;\n width: 0; height: 0;\n\n -webkit-transition: left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n -moz-transition: left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n transition: left 0.4s ease-out, top 0.4s ease-out, opacity 0.2s;\n opacity: 0;\n\n z-index: 12;\n}\n\n." + prefix + "-pointer:after {\n content: \"\";\n position: absolute;\n display: block;\n}\n\n." + prefix + "-pointer-above {\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 6px solid #777;\n}\n\n." + prefix + "-pointer-above:after {\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 6px solid white;\n left: -6px; top: -7px;\n}\n\n." + prefix + "-pointer-below {\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #777;\n}\n\n." + prefix + "-pointer-below:after {\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid white;\n left: -6px; top: 1px;\n}\n\n." + prefix + "-pointer-right {\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-right: 6px solid #777;\n}\n\n." + prefix + "-pointer-right:after {\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-right: 6px solid white;\n left: 1px; top: -6px;\n}\n\n." + prefix + "-pointer-left {\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-left: 6px solid #777;\n}\n\n." + prefix + "-pointer-left:after {\n border-top: 6px solid transparent;\n border-bottom: 6px solid transparent;\n border-left: 6px solid white;\n left: -7px; top: -6px;\n}\n\n." + prefix + " input[type=\"text\"],\n." + prefix + " textarea {\n background: #eee;\n border: none;\n outline: none;\n}\n\n." + prefix + " input[type=\"text\"] {\n padding: 0 4px;\n}\n\n");

@@ -11,2 +11,10 @@ "use strict";

var noHandlers = [];
function getHandlers(obj, type, copy) {
var arr = obj._handlers && obj._handlers[type];
if (!arr) return noHandlers;
return !copy || arr.length < 2 ? arr : arr.slice();
}
var methods = {

@@ -26,4 +34,4 @@ // :: (type: string, handler: (...args: [any])) #path=EventMixin.on

off: function off(type, handler) {
var arr = this._handlers && this._handlers[type];
if (arr) for (var i = 0; i < arr.length; ++i) {
var arr = getHandlers(this, type, false);
for (var i = 0; i < arr.length; ++i) {
if (arr[i] == handler) {

@@ -41,3 +49,3 @@ arr.splice(i, 1);break;

signal: function signal(type) {
var arr = this._handlers && this._handlers[type];
var arr = getHandlers(this, type, true);

@@ -48,3 +56,3 @@ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {

if (arr) for (var i = 0; i < arr.length; ++i) {
for (var i = 0; i < arr.length; ++i) {
arr[i].apply(arr, args);

@@ -55,20 +63,20 @@ }

// :: (type: string, ...args: [any]) → any #path=EventMixin.signalHandleable
// Signal a handleable event of the given type. All handlers for the
// event will be called with the given arguments, until one of them
// returns something that is not the value `false`. When that
// happens, the return value of that handler is returned. If that
// does not happen, `false` is returned.
// :: (type: string, ...args: [any]) → any
// #path=EventMixin.signalHandleable Signal a handleable event of
// the given type. All handlers for the event will be called with
// the given arguments, until one of them returns something that is
// not the value `null` or `undefined`. When that happens, the
// return value of that handler is returned. If that does not
// happen, `undefined` is returned.
signalHandleable: function signalHandleable(type) {
var arr = this._handlers && this._handlers[type];
if (arr) {
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
var arr = getHandlers(this, type, true);
for (var i = 0; i < arr.length; ++i) {
var result = arr[i].apply(arr, args);
if (result !== false) return result;
}
}return false;
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
for (var i = 0; i < arr.length; ++i) {
var result = arr[i].apply(arr, args);
if (result != null) return result;
}
},

@@ -83,4 +91,4 @@

signalPipelined: function signalPipelined(type, value) {
var arr = this._handlers && this._handlers[type];
if (arr) for (var i = 0; i < arr.length; ++i) {
var arr = getHandlers(this, type, true);
for (var i = 0; i < arr.length; ++i) {
value = arr[i](value);

@@ -98,7 +106,6 @@ }return value;

signalDOM: function signalDOM(event, type) {
var arr = this._handlers && this._handlers[type || event.type];
if (arr) for (var i = 0; i < arr.length; ++i) {
var arr = getHandlers(this, type || event.type, true);
for (var i = 0; i < arr.length; ++i) {
if (arr[i](event) || event.defaultPrevented) return true;
}
return false;
}return false;
},

@@ -110,4 +117,3 @@

hasHandler: function hasHandler(type) {
var arr = this._handlers && this._handlers[type];
return arr && arr.length > 0;
return getHandlers(this, type, false).length > 0;
}

@@ -114,0 +120,0 @@ };

{
"name": "prosemirror",
"version": "0.4.0",
"version": "0.5.0",
"description": "Well-defined WYSIWYG editor",

@@ -28,3 +28,3 @@ "main": "dist/edit/index.js",

"jsdom": "^8.0.4",
"moduleserve": "^0.6.0",
"moduleserve": "^0.7.0",
"distfs": "^0.1.1",

@@ -31,0 +31,0 @@ "punycode": "^1.4.0",

# ProseMirror
[![Join the chat at https://gitter.im/ProseMirror/prosemirror](https://badges.gitter.im/ProseMirror/prosemirror.svg)](https://gitter.im/ProseMirror/prosemirror?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a well-behaved rich semantic content editor based on

@@ -4,0 +6,0 @@ contentEditable, with support for collaborative editing and

import {defineOption} from "../edit"
import {eventMixin} from "../util/event"
import {AssertionError} from "../util/error"
import {Transform} from "../transform"

@@ -117,18 +118,19 @@ import {rebaseSteps} from "./rebase"

receive(steps) {
let doc = this.versionDoc
let maps = steps.map(step => {
let result = step.apply(doc)
doc = result.doc
return result.map
})
let transform = new Transform(this.versionDoc)
steps.forEach(step => transform.step(step))
this.version += steps.length
this.versionDoc = doc
this.versionDoc = transform.doc
let rebased = rebaseSteps(doc, maps, this.unconfirmedSteps, this.unconfirmedMaps)
let rebased = rebaseSteps(transform.doc, transform.maps, this.unconfirmedSteps, this.unconfirmedMaps)
this.unconfirmedSteps = rebased.transform.steps.slice()
this.unconfirmedMaps = rebased.transform.maps.slice()
let selectionBefore = this.pm.selection
this.pm.updateDoc(rebased.doc, rebased.mapping)
this.pm.history.rebased(maps, rebased.transform, rebased.positions)
return maps
this.pm.history.rebased(transform.maps, rebased.transform, rebased.positions)
// :: (transform: Transform, selectionBeforeTransform: Selection) #path=Collab#events#collabTransform
// Signals that a transformation has been aplied to the editor. Passes the `Transform` and the selection
// before the transform as arguments to the handler.
this.signal("collabTransform", transform, selectionBefore)
return transform.maps
}

@@ -135,0 +137,0 @@ }

@@ -66,4 +66,9 @@ import {Pos} from "../model"

let before = around.child(cut.offset - 1), after = around.child(cut.offset)
if (before.type.canContainContent(after.type) && pm.tr.join(cut).apply(pm.apply.scroll) !== false)
return
if (before.type.canContainContent(after.type)) {
let tr = pm.tr.join(cut)
if (tr.steps.length && before.size == 0 && !before.sameMarkup(after))
tr.setNodeType(cut.move(-1), after.type, after.attrs)
if (tr.apply(pm.apply.scroll) !== false)
return
}

@@ -109,2 +114,10 @@ let conn

// If the node below has no content and the node above is
// selectable, delete the node below and select the one above.
if (before.type.contains == null && before.type.selectable && pm.doc.path(head.path).size == 0) {
let tr = pm.tr.delete(cut, cut.move(1)).apply(pm.apply.scroll)
pm.setNodeSelection(cut.move(-1))
return tr
}
// If the node doesn't allow children, delete it

@@ -321,3 +334,3 @@ if (before.type.contains == null)

//
// **Keybindings:** Alt-Left
// **Keybindings:** Ctrl-[
baseCommands.lift = {

@@ -341,3 +354,3 @@ label: "Lift out of enclosing block",

},
keys: ["Alt-Left"]
keys: ["Mod-["]
}

@@ -501,3 +514,6 @@

},
keys: ["Left", "Mod-Left"]
keys: {
all: ["Left", "Mod-Left"],
mac: ["Alt-Left"]
}
}

@@ -516,5 +532,12 @@

},
keys: ["Right", "Mod-Right"]
keys: {
all: ["Right", "Mod-Right"],
mac: ["Alt-Left"]
}
}
// : (ProseMirror, number)
// Check whether vertical selection motion would involve node
// selections. If so, apply it (if not, the result is left to the
// browser)
function selectNodeVertically(pm, dir) {

@@ -521,0 +544,0 @@ let {empty, node, from, to} = pm.selection

@@ -5,2 +5,3 @@ import {Pos, findDiffStart, findDiffEnd} from "../model"

import {findSelectionFrom} from "./selection"
import {findByPath} from "./dompos"

@@ -51,3 +52,3 @@

export function applyDOMChange(pm) {
export function readDOMChange(pm) {
let updated = parseNearSelection(pm)

@@ -60,4 +61,13 @@ let changeStart = findDiffStart(pm.doc.content, updated.content)

pm.tr.replace(changeStart, changeEnd.a, updated, changeStart, changeEnd.b).apply()
return true
let near
// FIXME when we have a Slice type, just return replace info, & let caller inspect it
if (pm.doc.path(changeStart.path).isTextblock &&
Pos.samePath(changeStart.path, changeEnd.a.path) &&
!Pos.samePath(changeStart.path, changeEnd.b.path) &&
(near = findSelectionFrom(updated, after(updated, changeStart), 1, true)) &&
!near.head.cmp(changeEnd.b))
return {type: "enter"}
else
return {type: "replace",
run: () => pm.tr.replace(changeStart, changeEnd.a, updated, changeStart, changeEnd.b).apply()}
} else {

@@ -68,2 +78,9 @@ return false

function after(doc, pos) {
if (pos.offset < doc.path(pos.path).size)
return pos.move(1)
else
return pos.shorten(null, 1)
}
function offsetBy(first, second, pos) {

@@ -70,0 +87,0 @@ let same = samePathDepth(first, second)

@@ -332,2 +332,7 @@ import {Pos} from "../model"

// :: (pm: ProseMirror, event: MouseEvent, path: [number], node: Node) → bool
// #path=NodeType.prototype.handleDoubleClick
// This works like [`handleClick`](#NodeType.handleClick), but is
// called for double clicks instead.
// :: (pm: ProseMirror, event: MouseEvent, path: [number], node: Node) → bool
// #path=NodeType.prototype.handleContextMenu

@@ -334,0 +339,0 @@ //

@@ -13,10 +13,12 @@ import {Pos} from "../model"

onRender(node, dom, offset) {
if (!node.isText && node.type.contains == null) {
dom.contentEditable = false
if (node.isBlock) dom.setAttribute("pm-leaf", "true")
if (node.isBlock) {
if (node.type.contains == null)
dom.setAttribute("pm-leaf", "true")
if (offset != null)
dom.setAttribute("pm-offset", offset)
if (node.isTextblock)
adjustTrailingHacks(dom, node)
if (dom.contentEditable == "false")
dom = elt("div", dom)
}
if (node.isBlock && offset != null)
dom.setAttribute("pm-offset", offset)
if (node.isTextblock)
adjustTrailingHacks(dom, node)

@@ -23,0 +25,0 @@ return dom

@@ -14,2 +14,11 @@ import {Pos} from "../model"

class HistoryEvent {
constructor(steps, selection) {
this.steps = steps
this.selection = selection
}
}
// Assists with remapping a step with other changes that have been
// made since the step was first applied.
class BranchRemapping {

@@ -19,2 +28,4 @@ constructor(branch) {

this.remap = new Remapping
// Track the internal version of what step the current remapping collection
// would put the content at.
this.version = branch.version

@@ -24,2 +35,4 @@ this.mirrorBuffer = Object.create(null)

// Add all position maps between the current version
// and the desired version to the remapping collection.
moveToVersion(version) {

@@ -29,2 +42,4 @@ while (this.version > version) this.addNextMap()

// Add the next map at the current version to the
// remapping collection.
addNextMap() {

@@ -45,4 +60,10 @@ let found = this.branch.mirror[this.version]

const workTime = 100, pauseTime = 150
// The number of milliseconds the compression worker has to compress.
const workTime = 100
// The number of milliseconds to pause compression worker if it uses
// all its work time.
const pauseTime = 150
// Help compress steps in events for a branch.
class CompressionWorker {

@@ -64,2 +85,3 @@ constructor(doc, branch, callback) {

// Compress steps in all events in the branch.
work() {

@@ -72,12 +94,16 @@ if (this.aborted) return

if (this.i == 0) return this.finish()
let event = this.branch.events[--this.i], outEvent = []
for (let j = event.length - 1; j >= 0; j--) {
let {step, version: stepVersion, id: stepID} = event[j]
let event = this.branch.events[--this.i]
let mappedSelection = event.selection && event.selection.map(this.doc, this.remap.remap)
let outEvent = new HistoryEvent([], mappedSelection)
for (let j = event.steps.length - 1; j >= 0; j--) {
let {step, version: stepVersion, id: stepID} = event.steps[j]
this.remap.moveToVersion(stepVersion)
let mappedStep = step.map(this.remap.remap)
// Combine contiguous delete steps.
if (mappedStep && isDelStep(step)) {
let extra = 0, start = step.from
while (j > 0) {
let next = event[j - 1]
let next = event.steps[j - 1]
if (next.version != stepVersion - 1 || !isDelStep(next.step) ||

@@ -101,3 +127,3 @@ start.cmp(next.step.to))

this.maps.push(result.map.invert())
outEvent.push(new InvertedStep(mappedStep, this.version, stepID))
outEvent.steps.push(new InvertedStep(mappedStep, this.version, stepID))
this.version--

@@ -107,4 +133,4 @@ }

}
if (outEvent.length) {
outEvent.reverse()
if (outEvent.steps.length) {
outEvent.steps.reverse()
this.events.push(outEvent)

@@ -139,2 +165,3 @@ }

// The minimum number of new steps before a compression is started.
const compressStepCount = 150

@@ -167,5 +194,8 @@

newEvent() {
// : (Selection)
// Create a new history event at tip of the branch.
newEvent(currentSelection) {
this.abortCompression()
this.events.push([])
this.events.push(new HistoryEvent([], currentSelection))
while (this.events.length > this.maxDepth)

@@ -175,2 +205,6 @@ this.events.shift()

// : (PosMap)
// Add a position map to the branch, either representing one of the
// changes recorded in the branch, or representing a non-history
// change that the branch's changes must be mapped through.
addMap(map) {

@@ -185,2 +219,4 @@ if (!this.empty()) {

// : () → bool
// Whether the branch is empty (has no history events).
empty() {

@@ -193,3 +229,3 @@ return this.events.length == 0

if (id == null) id = this.nextStepID++
this.events[this.events.length - 1].push(new InvertedStep(step, this.version, id))
this.events[this.events.length - 1].steps.push(new InvertedStep(step, this.version, id))
}

@@ -207,3 +243,3 @@

// : (Node, bool) → ?{transform: Transform, ids: [number]}
// : (Node, bool) → ?{transform: Transform, ids: [number], selection: Selection}
// Pop the latest event off the branch's history and apply it

@@ -220,9 +256,11 @@ // to a document transform, returning the transform and the step ID.

for (let i = event.length - 1; i >= 0; i--) {
let invertedStep = event[i], step = invertedStep.step
for (let i = event.steps.length - 1; i >= 0; i--) {
let invertedStep = event.steps[i], step = invertedStep.step
if (!collapsing || invertedStep.version != remap.version) {
collapsing = false
// Remap the step through any position mappings unrelated to
// history (e.g. collaborative edits).
remap.moveToVersion(invertedStep.version)
step = step.map(remap.remap)
step = step.map(remap.remap)
let result = step && tr.step(step)

@@ -235,3 +273,3 @@ if (result) {

if (i > 0) remap.movePastStep(result)
remap.movePastStep(result)
} else {

@@ -246,4 +284,6 @@ this.version--

}
let selection = event.selection && event.selection.map(tr.doc, remap.remap)
if (this.empty()) this.clear(true)
return {transform: tr, ids}
return {transform: tr, ids, selection}
}

@@ -254,3 +294,3 @@

let event = this.events[i]
if (event.length) return event[event.length - 1]
if (event.steps.length) return event.steps[event.steps.length - 1]
}

@@ -270,6 +310,10 @@ }

findVersion(version) {
// FIXME this is not accurate when the actual revision has fallen
// off the end of the history. Current representation of versions
// does not allow us to recognize that case.
if (version.lastID == null) return {event: 0, step: 0}
for (let i = this.events.length - 1; i >= 0; i--) {
let event = this.events[i]
for (let j = event.length - 1; j >= 0; j--)
if (event[j].id <= version.lastID)
for (let j = event.steps.length - 1; j >= 0; j--)
if (event.steps[j].id <= version.lastID)
return {event: i, step: j + 1}

@@ -288,12 +332,12 @@ }

let event = this.events[i]
for (let j = event.length - 1; j >= 0; j--) {
let step = event[j]
for (let j = event.steps.length - 1; j >= 0; j--) {
let step = event.steps[j]
if (step.version <= startVersion) break out
let off = positions[step.version - startVersion - 1]
if (off == -1) {
event.splice(j--, 1)
event.steps.splice(j--, 1)
} else {
let inv = rebasedTransform.steps[off].invert(rebasedTransform.docs[off],
rebasedTransform.maps[off])
event[j] = new InvertedStep(inv, startVersion + newMaps.length + off + 1, step.id)
event.steps[j] = new InvertedStep(inv, startVersion + newMaps.length + off + 1, step.id)
}

@@ -337,2 +381,3 @@ }

// Delay between transforms required to compress steps.
const compressDelay = 750

@@ -353,3 +398,3 @@

pm.on("transform", (transform, options) => this.recordTransform(transform, options))
pm.on("transform", (transform, selection, options) => this.recordTransform(transform, selection, options))
}

@@ -359,3 +404,3 @@

// Record a transformation in undo history.
recordTransform(transform, options) {
recordTransform(transform, selection, options) {
if (this.ignoreTransform) return

@@ -374,3 +419,3 @@

if (now > this.lastAddedAt + this.pm.options.historyEventDelay)
this.done.newEvent()
this.done.newEvent(selection)

@@ -413,6 +458,7 @@ this.done.addTransform(transform)

if (!event) return false
let {transform, ids} = event
let {transform, ids, selection} = event
let selectionBeforeTransform = this.pm.selection
this.ignoreTransform = true
this.pm.apply(transform)
this.pm.apply(transform, {selection})
this.ignoreTransform = false

@@ -423,3 +469,7 @@

if (to) {
to.newEvent()
// Store the selection before transform on the event so that
// it can be reapplied if the event is undone or redone (e.g.
// redoing a character addition should place the cursor after
// the character).
to.newEvent(selectionBeforeTransform)
to.addTransform(transform, ids)

@@ -454,7 +504,9 @@ }

let event = this.done.events[found.event]
if (found.event == this.done.events.length - 1 && found.step == event.length) return true
let combined = this.done.events.slice(found.event + 1)
.reduce((comb, arr) => comb.concat(arr), event.slice(found.step))
this.done.events.length = found.event + ((event.length = found.step) ? 1 : 0)
this.done.events.push(combined)
if (found.event == this.done.events.length - 1 && found.step == event.steps.length) return true
// Combine all steps past the verion to rollback to into
// one event, and then "undo" that event.
let combinedSteps = this.done.events.slice(found.event + 1)
.reduce((comb, arr) => comb.concat(arr.steps), event.steps.slice(found.step))
this.done.events.length = found.event + ((event.steps.length = found.step) ? 1 : 0)
this.done.events.push(new HistoryEvent(combinedSteps, null))

@@ -476,2 +528,4 @@ this.shift(this.done)

// : (Branch)
// Schedule compression for a branch if it needs compressing.
maybeScheduleCompressionForBranch(branch) {

@@ -478,0 +532,0 @@ window.clearTimeout(branch.compressTimeout)

@@ -6,6 +6,6 @@ import Keymap from "browserkeymap"

import {captureKeys} from "./capturekeys"
import {elt, browser} from "../dom"
import {elt, browser, contains} from "../dom"
import {applyDOMChange, textContext, textInContext} from "./domchange"
import {TextSelection, rangeFromDOMLoose, findSelectionAtStart, findSelectionAtEnd} from "./selection"
import {readDOMChange, textContext, textInContext} from "./domchange"
import {TextSelection, rangeFromDOMLoose, findSelectionAtStart, findSelectionAtEnd, hasFocus} from "./selection"
import {coordsAtPos, pathFromDOM, handleNodeClick, selectableNodeAbove} from "./dompos"

@@ -127,2 +127,3 @@

// menus.
if (!hasFocus(pm)) return
pm.signal("interaction")

@@ -152,3 +153,4 @@ if (e.keyCode == 16) pm.input.shiftKey = true

handlers.keypress = (pm, e) => {
if (pm.input.composing || !e.charCode || e.ctrlKey && !e.altKey || browser.mac && e.metaKey) return
if (!hasFocus(pm) || pm.input.composing || !e.charCode ||
e.ctrlKey && !e.altKey || browser.mac && e.metaKey) return
if (dispatchKey(pm, Keymap.keyName(e), e)) return

@@ -204,2 +206,3 @@ let sel = pm.selection

if (tripleClick) handleTripleClick(pm, e)
else if (doubleClick && handleNodeClick(pm, "handleDoubleClick", e, true)) {}
else pm.input.mouseDown = new MouseDown(pm, e, doubleClick)

@@ -239,11 +242,11 @@ }

up() {
up(event) {
this.done()
if (this.leaveToBrowser) {
if (this.leaveToBrowser || !contains(this.pm.content, event.target)) {
this.pm.sel.fastPoll()
} else if (this.event.ctrlKey) {
selectClickedNode(this.pm, this.event)
} else if (!handleNodeClick(this.pm, "handleClick", this.event, true)) {
let pos = selectableNodeAbove(this.pm, this.event.target, {left: this.x, top: this.y})
selectClickedNode(this.pm, event)
} else if (!handleNodeClick(this.pm, "handleClick", event, true)) {
let pos = selectableNodeAbove(this.pm, event.target, {left: this.x, top: this.y})
if (pos) {

@@ -293,3 +296,3 @@ this.pm.setNodeSelection(pos)

handlers.compositionstart = (pm, e) => {
if (pm.input.maybeAbortComposition()) return
if (!hasFocus(pm) || pm.input.maybeAbortComposition()) return

@@ -303,2 +306,3 @@ pm.flush()

handlers.compositionupdate = (pm, e) => {
if (!hasFocus(pm)) return
let info = pm.input.composing

@@ -315,2 +319,3 @@ if (info && info.data != e.data) {

handlers.compositionend = (pm, e) => {
if (!hasFocus(pm)) return
let info = pm.input.composing

@@ -334,3 +339,4 @@ if (info) {

handlers.input = (pm) => {
handlers.input = (pm, e) => {
if (!hasFocus(pm)) return
if (pm.input.skipInput) return --pm.input.skipInput

@@ -344,3 +350,9 @@

pm.startOperation({readSelection: false})
applyDOMChange(pm)
let change = readDOMChange(pm)
if (change) {
if (change.type == "enter")
dispatchKey(pm, "Enter", e)
else
change.run()
}
pm.scrollIntoView()

@@ -444,2 +456,3 @@ }

handlers.paste = (pm, e) => {
if (!hasFocus(pm)) return
if (!e.clipboardData) return

@@ -446,0 +459,0 @@ let sel = pm.selection

@@ -217,2 +217,7 @@ import "./css"

//
// **`filter`**: ?bool
// : When set to false, suppresses the ability of the
// [`"filterTransform"` event](#ProseMirror.event_beforeTransform)
// to cancel this transform.
//
// Returns the transform, or `false` if there were no steps in it.

@@ -226,2 +231,11 @@ //

// :: (transform: Transform) #path=ProseMirror#events#filterTransform
// Fired before a transform (applied without `filter: false`) is
// applied. The handler can return a truthy value to cancel the
// transform.
if (options.filter !== false && this.signalHandleable("filterTransform", transform))
return false
let selectionBeforeTransform = this.selection
// :: (transform: Transform, options: Object) #path=ProseMirror#events#beforeTransform

@@ -234,7 +248,8 @@ // Indicates that the given transform is about to be

this.updateDoc(transform.doc, transform, options.selection)
// :: (transfom: Transform, options: Object) #path=ProseMirror#events#transform
// :: (transform: Transform, selectionBeforeTransform: Selection, options: Object) #path=ProseMirror#events#transform
// Signals that a (non-empty) transformation has been aplied to
// the editor. Passes the `Transform` and the options given to
// [`apply`](#ProseMirror.apply) as arguments to the handler.
this.signal("transform", transform, options)
// the editor. Passes the `Transform`, the selection before the
// transform, and the options given to [`apply`](#ProseMirror.apply)
// as arguments to the handler.
this.signal("transform", transform, selectionBeforeTransform, options)
if (options.scrollIntoView) this.scrollIntoView()

@@ -518,2 +533,10 @@ return transform

}
// :: (string) → string
// Return a translated string, if a translate function has been supplied,
// or the original string.
translate(string) {
let trans = this.options.translate
return trans ? trans(string) : string
}
}

@@ -520,0 +543,0 @@

@@ -7,5 +7,11 @@ import {defaultSchema} from "../model"

// An option encapsulates functionality for an editor instance,
// e.g. the amount of history events that the editor should hold
// onto or the document's schema.
class Option {
constructor(defaultValue, update, updateOnInit) {
this.defaultValue = defaultValue
// A function that will be invoked with the option's old and new
// value every time the option is [set](#ProseMirror.setOption).
// This function should bootstrap option functionality.
this.update = update

@@ -77,2 +83,8 @@ this.updateOnInit = updateOnInit !== false

// :: ?(string) → string #path=translate #kind=option
// Optional function to translate strings such as menu labels and prompts.
// When set, should be a function that takes a string as argument and returns
// a string, i.e. :: (string) → string
defineOption("translate", null) // FIXME create a way to explicitly force a menu redraw
export function parseOptions(obj) {

@@ -79,0 +91,0 @@ let result = Object.create(null)

@@ -180,3 +180,3 @@ import {HardBreak, BulletList, OrderedList, ListItem, BlockQuote, Heading, Paragraph, CodeBlock, HorizontalRule,

//
// **Keybindings:** Alt-Right '*', Alt-Right '-'
// **Keybindings:** Shift-Mod-8
BulletList.register("command", "wrap", {

@@ -193,3 +193,3 @@ derive: {list: true},

},
keys: ["Alt-Right '*'", "Alt-Right '-'"]
keys: ["Shift-Mod-8"]
})

@@ -200,3 +200,3 @@

//
// **Keybindings:** Alt-Right '1'
// **Keybindings:** Shift-Mod-8
OrderedList.register("command", "wrap", {

@@ -213,3 +213,3 @@ derive: {list: true},

},
keys: ["Alt-Right '1'"]
keys: ["Shift-Mod-9"]
})

@@ -220,3 +220,3 @@

//
// **Keybindings:** Alt-Right '>', Alt-Right '"'
// **Keybindings:** Shift-Mod-.
BlockQuote.register("command", "wrap", {

@@ -233,3 +233,3 @@ derive: true,

},
keys: ["Alt-Right '>'", "Alt-Right '\"'"]
keys: ["Shift-Mod-."]
})

@@ -282,3 +282,3 @@

//
// **Keybindings:** Mod-1 through Mod-6
// **Keybindings:** Shift-Mod-1 through Shift-Mod-6
Heading.registerComputed("command", "make" + i, type => {

@@ -288,3 +288,3 @@ if (i <= type.maxLevel) return {

label: "Change to heading " + i,
keys: i < 10 && [`Mod-${i}`],
keys: i <= 6 && [`Shift-Mod-${i}`],
menu: {

@@ -301,7 +301,7 @@ group: "textblockHeading", rank: 30 + i,

//
// **Keybindings:** Mod-0
// **Keybindings:** Shift-Mod-0
Paragraph.register("command", "make", {
derive: true,
label: "Change to paragraph",
keys: ["Mod-0"],
keys: ["Shift-Mod-0"],
menu: {

@@ -317,7 +317,7 @@ group: "textblock", rank: 10,

//
// **Keybindings:** Mod-\
// **Keybindings:** Shift-Mod-\
CodeBlock.register("command", "make", {
derive: true,
label: "Change to code block",
keys: ["Mod-\\"],
keys: ["Shift-Mod-\\"],
menu: {

@@ -324,0 +324,0 @@ group: "textblock", rank: 20,

@@ -100,4 +100,11 @@ import {Pos} from "../model"

let newRange = findSelectionNear(doc, head, this.range.head && this.range.head.cmp(head) < 0 ? -1 : 1)
if (newRange instanceof TextSelection && doc.path(anchor.path).isTextblock)
if (newRange instanceof TextSelection && doc.path(anchor.path).isTextblock) {
newRange = new TextSelection(anchor, newRange.head)
} else if (newRange instanceof NodeSelection && (anchor.cmp(newRange.from) < 0 || anchor.cmp(newRange.to) > 0)) {
// If head falls on a node, but anchor falls outside of it,
// create a text selection between them
let inv = anchor.cmp(newRange.to) > 0
newRange = new TextSelection(findSelectionNear(doc, anchor, inv ? -1 : 1, true).anchor,
findSelectionNear(doc, inv ? newRange.from : newRange.to, inv ? 1 : -1, true).head)
}
this.setAndSignal(newRange)

@@ -187,6 +194,6 @@

// :: Pos #path=Selection.prototype.from
// The start of the selection.
// The left-bound of the selection.
// :: Pos #path=Selection.prototype.to
// The end of the selection.
// The right-bound of the selection.

@@ -285,2 +292,3 @@ // :: bool #path=Selection.empty

export function hasFocus(pm) {
if (document.activeElement != pm.content) return false
let sel = window.getSelection()

@@ -290,2 +298,4 @@ return sel.rangeCount && contains(pm.content, sel.anchorNode)

// Try to find a selection inside the node at the given path coming
// from a given direction.
function findSelectionIn(doc, path, offset, dir, text) {

@@ -295,2 +305,4 @@ let node = doc.path(path)

// Iterate over child nodes recursively coming from the given
// direction and return the first viable selection.
for (let i = offset + (dir > 0 ? 0 : -1); dir > 0 ? i < node.size : i >= 0; i += dir) {

@@ -309,2 +321,6 @@ let child = node.child(i)

// Create a selection which is moved relative to a position in a
// given direction. When a selection isn't found at the given position,
// walks up the document tree one level and one step in the
// desired direction.
export function findSelectionFrom(doc, pos, dir, text) {

@@ -325,2 +341,3 @@ for (let path = pos.path.slice(), offset = pos.offset;;) {

// Find the selection closes to the start of the given node.
export function findSelectionAtStart(node, path = [], text) {

@@ -330,2 +347,3 @@ return findSelectionIn(node, path.slice(), 0, 1, text)

// Find the selection closes to the end of the given node.
export function findSelectionAtEnd(node, path = [], text) {

@@ -335,2 +353,5 @@ return findSelectionIn(node, path.slice(), node.size, -1, text)

// : (ProseMirror, Pos, number)
// Whether vertical position motion in a given direction
// from a position would leave a text block.
export function verticalMotionLeavesTextblock(pm, pos, dir) {

@@ -337,0 +358,0 @@ let dom = pathToDOM(pm.content, pos.path)

@@ -192,3 +192,3 @@ import {Text, BlockQuote, OrderedList, BulletList, ListItem,

def(HorizontalRule, (_, s) => s.elt("hr"))
def(HorizontalRule, (_, s) => s.elt("div", null, s.elt("hr")))

@@ -195,0 +195,0 @@ def(Paragraph, (node, s) => s.renderAs(node, "p"))

@@ -131,3 +131,3 @@ import {Pos} from "../model"

if (child.isText) textBefore += child.text
else textBefore = ""
else textBefore += "\ufffc"
if (i.atEnd() && child.marks.some(st => st.type.isCode)) isCode = true

@@ -134,0 +134,0 @@ }

@@ -123,30 +123,51 @@ import {Text, BlockQuote, OrderedList, BulletList, ListItem,

renderInline(parent) {
let stack = []
let active = []
let progress = node => {
let marks = node ? node.marks.slice() : []
if (stack.length && stack[stack.length - 1].type == "code" &&
(!marks.length || marks[marks.length - 1].type != "code")) {
this.text("`", false)
stack.pop()
}
for (let j = 0; j < stack.length; j++) {
let cur = stack[j], found = false
for (let k = 0; k < marks.length; k++) {
if (marks[k].eq(stack[j])) {
marks.splice(k, 1)
found = true
break
let marks = node ? node.marks : []
let code = marks.length && marks[marks.length - 1].type.isCode && marks[marks.length - 1]
let len = marks.length - (code ? 1 : 0)
// Try to reorder 'mixable' marks, such as em and strong, which
// in Markdown may be opened and closed in different order, so
// that order of the marks for the token matches the order in
// active.
outer: for (let i = 0; i < len; i++) {
let mark = marks[i]
if (!mark.type.markdownMixable) break
for (let j = 0; j < active.length; j++) {
let other = active[j]
if (!other.type.markdownMixable) break
if (mark.eq(other)) {
if (i > j)
marks = marks.slice(0, j).concat(mark).concat(marks.slice(j, i)).concat(marks.slice(i + 1), len)
else if (j > i)
marks = marks.slice(0, i).concat(marks.slice(i + 1, j)).concat(mark).concat(marks.slice(j, len))
continue outer
}
}
if (!found) {
this.text(this.markString(cur, false), false)
stack.splice(j--, 1)
}
}
for (let j = 0; j < marks.length; j++) {
let cur = marks[j]
stack.push(cur)
this.text(this.markString(cur, true), false)
// Find the prefix of the mark set that didn't change
let keep = 0
while (keep < Math.min(active.length, len) && marks[keep].eq(active[keep])) ++keep
// Close the marks that need to be closed
while (keep < active.length)
this.text(this.markString(active.pop(), false), false)
// Open the marks that need to be opened
while (active.length < len) {
let add = marks[active.length]
active.push(add)
this.text(this.markString(add, true), false)
}
if (node) this.render(node)
// Render the node. Special case code marks, since their content
// may not be escaped.
if (node) {
if (code && node.isText)
this.text(this.markString(code, false) + node.text + this.markString(code, true), false)
else
this.render(node)
}
}

@@ -195,2 +216,4 @@ parent.forEach(progress)

// : (Mark, bool) → string
// Get the markdown string for a given opening or closing mark.
markString(mark, open) {

@@ -265,14 +288,12 @@ let value = open ? mark.type.openMarkdown : mark.type.closeMarkdown

function defMark(mark, open, close) {
mark.prototype.openMarkdown = open
mark.prototype.closeMarkdown = close
}
EmMark.prototype.openMarkdown = EmMark.prototype.closeMarkdown = "*"
EmMark.prototype.markdownMixable = true
defMark(EmMark, "*", "*")
StrongMark.prototype.openMarkdown = StrongMark.prototype.closeMarkdown = "**"
StrongMark.prototype.markdownMixable = true
defMark(StrongMark, "**", "**")
LinkMark.prototype.openMarkdown = "["
LinkMark.prototype.closeMarkdown = (state, mark) =>
"](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
defMark(LinkMark, "[",
(state, mark) => "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")")
defMark(CodeMark, "`", "`")
CodeMark.prototype.openMarkdown = CodeMark.prototype.closeMarkdown = "`"

@@ -32,4 +32,5 @@ import {elt, insertCSS} from "../dom"

if (!command.label) return null
let label = pm.translate(command.label)
let key = command.name && pm.keyForCommand(command.name)
return key ? command.label + " (" + key + ")" : command.label
return key ? label + " (" + key + ")" : label
}

@@ -52,2 +53,6 @@

get commandName() {
return typeof this.command_ === "string" ? this.command_.command : this.command_.name
}
// :: (ProseMirror) → DOMNode

@@ -75,3 +80,4 @@ // Renders the command according to its [display

} else if (disp.type == "label") {
dom = elt("div", null, disp.label || cmd.spec.label)
let label = pm.translate(disp.label || cmd.spec.label)
dom = elt("div", null, label)
} else {

@@ -89,2 +95,3 @@ throw new AssertionError("Unsupported command display style: " + disp.type)

})
dom.setAttribute("data-command", this.commandName)
return dom

@@ -175,2 +182,3 @@ }

let label = (this.options.activeLabel && this.findActiveIn(this, pm)) || this.options.label
label = pm.translate(label)
let dom = elt("div", {class: prefix + "-dropdown " + (this.options.class || ""),

@@ -256,3 +264,3 @@ style: this.options.css,

let label = elt("div", {class: prefix + "-submenu-label"}, this.options.label)
let label = elt("div", {class: prefix + "-submenu-label"}, pm.translate(this.options.label))
let wrap = elt("div", {class: prefix + "-submenu-wrap"}, label,

@@ -259,0 +267,0 @@ elt("div", {class: prefix + "-submenu"}, items))

@@ -62,3 +62,3 @@ import {Pos} from "../model"

this.selectedBlockMenu = this.config.selectedBlockMenu
this.updater = new UpdateScheduler(pm, "change selectionChange blur commandsChanged", () => this.update())
this.updater = new UpdateScheduler(pm, "change selectionChange blur focus commandsChanged", () => this.update())
this.onContextMenu = this.onContextMenu.bind(this)

@@ -65,0 +65,0 @@ pm.content.addEventListener("contextmenu", this.onContextMenu)

@@ -86,3 +86,3 @@ import {SchemaSpec, Schema, Block, Textblock, Inline, Text, Attribute, MarkType, NodeKind} from "./schema"

export class EmMark extends MarkType {
static get rank() { return 51 }
static get rank() { return 31 }
}

@@ -92,3 +92,3 @@

export class StrongMark extends MarkType {
static get rank() { return 52 }
static get rank() { return 32 }
}

@@ -101,3 +101,3 @@

export class LinkMark extends MarkType {
static get rank() { return 25 }
static get rank() { return 60 }
get attrs() {

@@ -104,0 +104,0 @@ return {

@@ -326,3 +326,3 @@ import {ModelError} from "./error"

if (end > this.offset) {
let sliceEnd = node.width
let sliceEnd = node.width, sliceStart = this.offset - offset
if (end > this.endOffset) {

@@ -332,3 +332,3 @@ sliceEnd = this.endOffset - offset

}
node = node.copy(node.text.slice(this.offset - offset, sliceEnd))
node = sliceEnd > sliceStart ? node.copy(node.text.slice(this.offset - offset, sliceEnd)) : null
this.offset = end

@@ -335,0 +335,0 @@ return node

import {Fragment, emptyFragment} from "./fragment"
import {Mark} from "./mark"
import {Pos} from "./pos"
import {SchemaError} from "./schema"

@@ -365,2 +366,5 @@ const emptyArray = [], emptyAttrs = Object.create(null)

super(type, attrs, null, marks)
if (!content) throw new SchemaError("Empty text nodes are not allowed")
// :: ?string

@@ -367,0 +371,0 @@ // For text nodes, this contains the node's text content.

@@ -299,15 +299,38 @@ import {Node, TextNode} from "./node"

export class NodeKind {
// :: (string, [NodeKind])
// :: (string, ?[NodeKind], ?[NodeKind])
// Create a new node kind with the given set of superkinds (the new
// kind counts as a member of each of the superkinds). The `name`
// field is only for debugging purposes—kind equivalens is defined
// by identity.
constructor(name, ...supers) {
// kind counts as a member of each of the superkinds) and subkinds
// (which will count as a member of this new kind). The `name` field
// is only for debugging purposes—kind equivalens is defined by
// identity.
constructor(name, supers, subs) {
this.name = name
// FIXME temporary backwards-compatibility kludge
if (supers && supers instanceof NodeKind) {
supers = Array.prototype.slice.call(arguments, 1)
subs = null
}
this.id = ++NodeKind.nextID
this.supers = Object.create(null)
this.id = ++NodeKind.nextID
this.supers[this.id] = true
supers.forEach(sup => { for (let id in sup.supers) this.supers[id] = true })
this.subs = subs || []
if (supers) supers.forEach(sup => this.addSuper(sup))
if (subs) subs.forEach(sub => this.addSub(sub))
}
addSuper(sup) {
for (let id in sup.supers) {
this.supers[id] = true
sup.subs.push(this)
}
}
addSub(sub) {
if (this.supers[sub.id])
throw new SchemaError("Circular subkind relation")
sub.supers[this.id] = true
sub.subs.forEach(next => this.addSub(next))
}
// :: (NodeKind) → bool

@@ -330,3 +353,3 @@ // Test whether `other` is a subkind of this kind (or the same

// `NodeKind.inline`.
NodeKind.text = new NodeKind("text", NodeKind.inline)
NodeKind.text = new NodeKind("text", [NodeKind.inline])

@@ -580,3 +603,4 @@ // ;; Base type for block nodetypes.

// :: (string, ?[Mark]) → Node
// Create a text node in the schema. This method is bound to the Schema.
// Create a text node in the schema. This method is bound to the
// Schema. Empty text nodes are not allowed.
text(text, marks) {

@@ -583,0 +607,0 @@ return this.nodes.text.create(null, text, Mark.setFrom(marks))

@@ -117,3 +117,3 @@ import "../../collab"

type(pm1, "E")
conv(pm1, pm2, "ACDE")
conv(pm1, pm2, "ADCE")
})

@@ -120,0 +120,0 @@

@@ -120,2 +120,5 @@ import {defTest} from "../tests"

test("joinBackward",
doc(hr, p("<a>"), hr),
doc(hr, hr))
test("joinBackward",
doc(hr, blockquote(p("<a>there"))),

@@ -122,0 +125,0 @@ doc(blockquote(p("there"))))

@@ -1,6 +0,6 @@

import {applyDOMChange} from "../../edit/domchange"
import {readDOMChange} from "../../edit/domchange"
import {namespace} from "./def"
import {doc, p, em} from "../build"
import {cmpNode} from "../cmp"
import {cmpNode, cmp} from "../cmp"
import {findTextNode} from "./test-selection"

@@ -10,5 +10,9 @@

function apply(pm) {
readDOMChange(pm).run()
}
test("add_text", pm => {
findTextNode(pm.content, "hello").nodeValue = "heLllo"
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("heLllo")))

@@ -19,3 +23,3 @@ })

findTextNode(pm.content, "hello").nodeValue = "heo"
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("heo")))

@@ -27,3 +31,3 @@ })

txt.parentNode.appendChild(document.createTextNode("!"))
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("hello!")))

@@ -35,3 +39,3 @@ })

txt.parentNode.appendChild(document.createElement("em")).appendChild(document.createTextNode("!"))
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("hello", em("!"))))

@@ -43,3 +47,3 @@ })

txt.parentNode.removeChild(txt)
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p()))

@@ -51,3 +55,3 @@ })

.appendChild(document.createTextNode("hey"))
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("hey"), p("hello")))

@@ -59,3 +63,3 @@ })

.appendChild(document.createTextNode("hello"))
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("hello"), p("hello")))

@@ -66,4 +70,11 @@ })

findTextNode(pm.content, "hello").nodeValue = "helhello"
applyDOMChange(pm)
apply(pm)
cmpNode(pm.doc, doc(p("helhello")))
})
test("detect_enter", pm => {
findTextNode(pm.content, "hello").nodeValue = "hel"
pm.content.appendChild(document.createElement("p")).innerHTML = "lo"
let change = readDOMChange(pm)
cmp(change && change.type, "enter")
})
import {namespace} from "./def"
import {doc, p} from "../build"
import {is, cmp, cmpNode, P} from "../cmp"
import {is, cmp, cmpStr, cmpNode, P} from "../cmp"

@@ -9,3 +9,3 @@ const test = namespace("history")

function cut(pm) { pm.history.lastAddedAt = 0 }
function cutHistory(pm) { pm.history.lastAddedAt = 0 }

@@ -31,3 +31,3 @@ test("undo", pm => {

type(pm, "a")
cut(pm)
cutHistory(pm)
type(pm, "b")

@@ -57,3 +57,3 @@ cmpNode(pm.doc, doc(p("ab")))

type(pm, "hello")
cut(pm)
cutHistory(pm)
type(pm, "!")

@@ -72,3 +72,3 @@ pm.tr.insertText(P(0, 0), "....").apply()

type(pm, "hello")
cut(pm)
cutHistory(pm)
pm.tr.delete(P(0, 0), P(0, 5)).apply()

@@ -85,3 +85,3 @@ cmpNode(pm.doc, doc(p()))

type(pm, "hello")
cut(pm)
cutHistory(pm)
pm.tr.delete(P(0, 0), P(0, 5)).apply()

@@ -97,3 +97,3 @@ cmpNode(pm.doc, doc(p()))

type(pm, "hi")
cut(pm)
cutHistory(pm)
type(pm, "hello")

@@ -109,6 +109,6 @@ pm.tr.delete(P(0, 0), P(0, 7)).apply({addToHistory: false})

type(pm, " two")
cut(pm)
cutHistory(pm)
type(pm, " three")
pm.tr.insertText(P(0, 0), "zero ").apply()
cut(pm)
cutHistory(pm)
pm.tr.split(P(0, 0)).apply()

@@ -128,7 +128,7 @@ pm.setTextSelection(P(0, 0))

type(pm, " two")
cut(pm)
cutHistory(pm)
pm.tr.insertText(pm.selection.head, "xxx").apply({addToHistory: false})
type(pm, " three")
pm.tr.insertText(P(0, 0), "zero ").apply()
cut(pm)
cutHistory(pm)
pm.tr.split(P(0, 0)).apply()

@@ -150,3 +150,3 @@ pm.setTextSelection(P(0, 0))

pm.setTextSelection(P(0, 1))
cut(pm)
cutHistory(pm)
type(pm, "one")

@@ -160,4 +160,6 @@ type(pm, "two")

cmpNode(pm.doc, doc(p("XY!")))
cmpStr(pm.selection.anchor, P(0, 1))
pm.execCommand("redo")
cmpNode(pm.doc, doc(p("XonetwothreeY!")))
cmpStr(pm.selection.anchor, P(0, 12))
})

@@ -174,3 +176,3 @@

type(pm, "hello")
cut(pm)
cutHistory(pm)
let version = pm.history.getVersion()

@@ -191,3 +193,3 @@ type(pm, "ok")

type(pm, "ok")
cut(pm)
cutHistory(pm)
type(pm, "more")

@@ -201,1 +203,25 @@ is(pm.history.backToVersion(version), "rollback")

})
test("setSelectionOnUndo", pm => {
type(pm, "hi")
cutHistory(pm)
pm.setTextSelection(P(0, 0), P(0, 2))
let selection = pm.selection
pm.tr.replaceWith(selection.from, selection.to, pm.schema.text("hello")).apply()
let selection2 = pm.selection
pm.execCommand("undo")
is(pm.selection.eq(selection), "failed restoring selection after undo")
pm.execCommand("redo")
is(pm.selection.eq(selection2), "failed restoring selection after redo")
})
test("rebaseSelectionOnUndo", pm => {
type(pm, "hi")
cutHistory(pm)
pm.setTextSelection(P(0, 0), P(0, 2))
pm.tr.insert(P(0, 0), pm.schema.text("hello")).apply()
pm.tr.insert(P(0, 0), pm.schema.text("---")).apply({addToHistory: false})
pm.execCommand("undo")
cmpStr(pm.selection.head, P(0, 5))
})
import {defaultSchema as schema, Pos} from "../model"
// : (string) → Function
// Create a function for creating inline nodes with brevity.
// For use with `doc()`.
function buildInline(style) {

@@ -9,2 +12,5 @@ return function() {

// : (string, Object) → Function
// Create a function which creating block nodes with brevity.
// For use with `doc()`.
function build(type, attrs) {

@@ -54,2 +60,5 @@ return function() {

// : (...nodes: Node) → Doc
// Create a document node. Child nodes can be added with
// abbreviated node notation, see `build()`.
export function doc() {

@@ -56,0 +65,0 @@ let content = []

@@ -47,6 +47,14 @@ import {doc, blockquote, h1, h2, p, hr, li, ol, ul, em, strong, code, a, br, img, dataImage} from "./build"

t("inline_overlap",
t("inline_overlap_mix",
"This is **strong *emphasized text with `code` in* it**",
doc(p("This is ", strong("strong ", em("emphasized text with ", code("code"), " in"), " it"))))
t("inline_overlap_link",
"**[link](http://foo) is bold**",
doc(p(strong(a("link"), " is bold"))))
t("inline_overlap_code",
"**`code` is bold**",
doc(p(strong(code("code"), " is bold"))))
t("link",

@@ -53,0 +61,0 @@ "My [link](http://foo) goes to foo",

export const tests = Object.create(null)
// :(string, Function)
// Define a test. A test should include a descriptive name and
// a function which runs the test. If a test fails, it should
// throw a Failure.
export function defTest(name, f) {

@@ -4,0 +8,0 @@ if (name in tests) throw new Error("Duplicate definition of test " + name)

@@ -19,3 +19,3 @@ import {Pos, MarkType} from "../model"

return new StepResult(copyStructure(doc, step.from, step.to, (node, from, to) => {
if (!node.type.canContainMark(step.param)) return node
if (!node.type.canContainMark(step.param.type)) return node
return copyInline(node, from, to, node => {

@@ -22,0 +22,0 @@ return node.mark(step.param.addToSet(node.marks))

@@ -34,5 +34,10 @@ import {AssertionError} from "../util/error"

})
let promptTitle = elt("h5", {}, (command.spec && command.spec.label) ? pm.translate(command.spec.label) : "")
let submitButton = elt("button", {type: "submit", class: "ProseMirror-prompt-submit"}, "Ok")
let cancelButton = elt("button", {type: "button", class: "ProseMirror-prompt-cancel"}, "Cancel")
cancelButton.addEventListener("click", () => this.close())
// :: DOMNode
// An HTML form wrapping the fields.
this.form = elt("form", null, this.fields.map(f => elt("div", null, f)))
this.form = elt("form", null, promptTitle, this.fields.map(f => elt("div", null, f)),
elt("div", {class: "ProseMirror-prompt-buttons"}, submitButton, " ", cancelButton))
}

@@ -183,3 +188,3 @@

return elt("input", {type: "text",
placeholder: param.label,
placeholder: this.translate(param.label),
value,

@@ -196,3 +201,4 @@ autocomplete: "off"})

let options = param.options.call ? param.options(this) : param.options
return elt("select", null, options.map(o => elt("option", {value: o.value, selected: o.value == value ? "true" : null}, o.label)))
return elt("select", null, options.map(o => elt("option", {value: o.value, selected: o.value == value ? "true" : null},
this.translate(o.label))))
},

@@ -224,3 +230,3 @@ read(dom) {

wrapper.style.left = (options.pos.left - outerBox.left) + "px"
wrapper.style.pos = (options.pos.top - outerBox.top) + "px"
wrapper.style.top = (options.pos.top - outerBox.top) + "px"
} else {

@@ -256,2 +262,9 @@ let blockBox = wrapper.getBoundingClientRect()

.ProseMirror-prompt h5 {
margin: 0;
font-weight: normal;
font-size: 100%;
color: #444;
}
.ProseMirror-prompt input[type="text"],

@@ -288,2 +301,8 @@ .ProseMirror-prompt textarea {

}
.ProseMirror-prompt-buttons {
margin-top: 5px;
display: none;
}
`)

@@ -11,13 +11,17 @@ import {elt, insertCSS} from "../dom"

export class Tooltip {
// :: (DOMNode, string)
// :: (DOMNode, ?union<string, Object)
// Create a new tooltip that lives in the wrapper node, which should
// be its offset anchor, i.e. it should have a `relative` or
// `absolute` CSS position. You'll often want to pass an editor's
// [`wrapper` node](#ProseMirror.wrapper). `dir` may be `"above"`,
// `"below"`, `"right"`, `"left"`, or `"center"`. In the latter
// case, the tooltip has no arrow and is positioned centered in its
// wrapper node.
constructor(wrapper, dir) {
// [`wrapper` node](#ProseMirror.wrapper). `options` may be an object
// containg a `direction` string and a `getBoundingRect` function which
// should return a rectangle determining the space in which the tooltip
// may appear. Alternatively, `options` may be a string specifying the
// direction. The direction can be `"above"`, `"below"`, `"right"`,
// `"left"`, or `"center"`. In the latter case, the tooltip has no arrow
// and is positioned centered in its wrapper node.
constructor(wrapper, options) {
this.wrapper = wrapper
this.dir = dir || "above"
this.options = typeof options == "string" ? {direction: options} : options
this.dir = this.options.direction || "above"
this.pointer = wrapper.appendChild(elt("div", {class: prefix + "-pointer-" + this.dir + " " + prefix + "-pointer"}))

@@ -66,2 +70,6 @@ this.pointerWidth = this.pointerHeight = null

// Use the window as the bounding rectangle if no getBoundingRect
// function is defined
let boundingRect = (this.options.getBoundingRect || windowRect)()
for (let child = this.dom.firstChild, next; child; child = next) {

@@ -85,3 +93,3 @@ next = child.nextSibling

if (this.dir == "above" || this.dir == "below") {
let tipLeft = Math.max(0, Math.min(left - size.width / 2, window.innerWidth - size.width))
let tipLeft = Math.max(boundingRect.left, Math.min(left - size.width / 2, boundingRect.right - size.width))
this.dom.style.left = (tipLeft - around.left) + "px"

@@ -111,3 +119,3 @@ this.pointer.style.left = (left - around.left - this.pointerWidth / 2) + "px"

} else if (this.dir == "center") {
let top = Math.max(around.top, 0), bottom = Math.min(around.bottom, window.innerHeight)
let top = Math.max(around.top, boundingRect.top), bottom = Math.min(around.bottom, boundingRect.bottom)
let fromTop = (bottom - top - size.height) / 2

@@ -134,2 +142,9 @@ this.dom.style.left = (around.width - size.width) / 2 + "px"

function windowRect() {
return {
left: 0, right: window.innerWidth,
top: 0, bottom: window.innerHeight
}
}
insertCSS(`

@@ -136,0 +151,0 @@

@@ -5,2 +5,10 @@ // ;; #path=EventMixin #kind=interface

const noHandlers = []
function getHandlers(obj, type, copy) {
let arr = obj._handlers && obj._handlers[type]
if (!arr) return noHandlers
return !copy || arr.length < 2 ? arr : arr.slice()
}
const methods = {

@@ -18,4 +26,4 @@ // :: (type: string, handler: (...args: [any])) #path=EventMixin.on

off(type, handler) {
let arr = this._handlers && this._handlers[type]
if (arr) for (let i = 0; i < arr.length; ++i)
let arr = getHandlers(this, type, false)
for (let i = 0; i < arr.length; ++i)
if (arr[i] == handler) { arr.splice(i, 1); break }

@@ -29,20 +37,19 @@ },

signal(type, ...args) {
let arr = this._handlers && this._handlers[type]
if (arr) for (let i = 0; i < arr.length; ++i)
arr[i](...args)
let arr = getHandlers(this, type, true)
for (let i = 0; i < arr.length; ++i) arr[i](...args)
},
// :: (type: string, ...args: [any]) → any #path=EventMixin.signalHandleable
// Signal a handleable event of the given type. All handlers for the
// event will be called with the given arguments, until one of them
// returns something that is not the value `false`. When that
// happens, the return value of that handler is returned. If that
// does not happen, `false` is returned.
// :: (type: string, ...args: [any]) → any
// #path=EventMixin.signalHandleable Signal a handleable event of
// the given type. All handlers for the event will be called with
// the given arguments, until one of them returns something that is
// not the value `null` or `undefined`. When that happens, the
// return value of that handler is returned. If that does not
// happen, `undefined` is returned.
signalHandleable(type, ...args) {
let arr = this._handlers && this._handlers[type]
if (arr) for (let i = 0; i < arr.length; ++i) {
let arr = getHandlers(this, type, true)
for (let i = 0; i < arr.length; ++i) {
let result = arr[i](...args)
if (result !== false) return result
if (result != null) return result
}
return false
},

@@ -56,5 +63,4 @@

signalPipelined(type, value) {
let arr = this._handlers && this._handlers[type]
if (arr) for (let i = 0; i < arr.length; ++i)
value = arr[i](value)
let arr = getHandlers(this, type, true)
for (let i = 0; i < arr.length; ++i) value = arr[i](value)
return value

@@ -70,6 +76,5 @@ },

signalDOM(event, type) {
let arr = this._handlers && this._handlers[type || event.type]
if (arr) for (let i = 0; i < arr.length; ++i) {
let arr = getHandlers(this, type || event.type, true)
for (let i = 0; i < arr.length; ++i)
if (arr[i](event) || event.defaultPrevented) return true
}
return false

@@ -81,4 +86,3 @@ },

hasHandler(type) {
let arr = this._handlers && this._handlers[type]
return arr && arr.length > 0
return getHandlers(this, type, false).length > 0
}

@@ -85,0 +89,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc