wed
Version:
Wed is a schema-aware editor for XML documents.
325 lines • 14.9 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
define(["require", "exports", "jquery", "merge-options", "salve", "wed", "wed/modes/generic/generic", "wed/modes/generic/generic-decorator"], function (require, exports, jquery_1, merge_options_1, salve_1, wed_1, generic_1, generic_decorator_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
jquery_1 = __importDefault(jquery_1);
merge_options_1 = __importDefault(merge_options_1);
var ContextMenu = wed_1.gui.contextMenu.ContextMenu;
const { childrenByClass, closestByClass, indexOf } = wed_1.domutil;
// tslint:disable-next-line:completed-docs
class Validator {
constructor(dataRoot) {
this.dataRoot = dataRoot;
}
validateDocument() {
return [{
error: new salve_1.ValidationError("Test"),
node: this.dataRoot,
index: 0,
}];
}
}
// tslint:disable-next-line:completed-docs
class TestDecorator extends generic_decorator_1.GenericDecorator {
constructor() {
super(...arguments);
this.elementLevel = {
term: 2,
ref: 2,
text: 1,
};
}
addHandlers() {
super.addHandlers();
wed_1.inputTriggerFactory.makeSplitMergeInputTrigger(this.editor, this.mode, wed_1.GUISelector.fromDataSelector("hi", this.mode.getAbsoluteNamespaceMappings()), wed_1.key.makeKey(";"), wed_1.keyConstants.BACKSPACE, wed_1.keyConstants.DELETE);
}
// tslint:disable:no-jquery-raw-elements
elementDecorator(root, el) {
if (this.editor.modeTree.getMode(el) !== this.mode) {
// The element is not governed by this mode.
return;
}
const dataNode = this.editor.toDataNode(el);
const rend = dataNode.getAttribute("rend");
const localName = dataNode.localName;
const inTEI = dataNode.namespaceURI === this.namespaces.tei;
let level = inTEI ? this.elementLevel[localName] : undefined;
if (level === undefined) {
level = 1;
}
const isP = inTEI && localName === "p";
const isRef = inTEI && localName === "ref";
// We don't run the default when we wrap p.
if (!(isP && rend === "wrap")) {
// There's no super.super syntax we can use here.
wed_1.Decorator.prototype.elementDecorator.call(this, root, el, level, this.contextMenuHandler.bind(this, true), this.contextMenuHandler.bind(this, false));
}
if (isRef) {
jquery_1.default(el).children("._text._phantom").remove();
this.guiUpdater.insertBefore(el, jquery_1.default("<div class='_text _phantom _end_wrapper'>)</div>")[0], el.lastChild);
const $before = jquery_1.default("<div class='_text _phantom _start_wrapper'>(</div>");
this.guiUpdater.insertBefore(el, $before[0], el.firstChild.nextSibling);
$before.on("wed-context-menu", { node: el }, this._navigationContextMenuHandler.bind(this));
$before[0].setAttribute("data-wed--custom-context-menu", "true");
}
if (isP) {
switch (rend) {
case "foo":
jquery_1.default(el).children("._gui_test").remove();
this.guiUpdater
.insertBefore(el, jquery_1.default("<div class='_gui _phantom _gui_test btn " +
"btn-default'>Foo</div>")[0], el.lastChild);
let found;
let child = dataNode.firstElementChild;
while (found === undefined && child !== null) {
if (child.tagName === "abbr") {
found = child;
}
child = child.nextElementSibling;
}
if (found !== undefined) {
this.guiUpdater
.insertBefore(el, jquery_1.default("<div class='_gui _phantom _gui_test btn " +
"btn-default'>Foo2</div>")[0], el.lastChild);
this.guiUpdater
.insertBefore(el, jquery_1.default("<div class='_gui _phantom _gui_test btn " +
"btn-default'>Foo3</div>")[0], el.lastChild);
}
break;
case "wrap":
if (closestByClass(el, "_gui_test") !== null) {
break;
}
const toRemove = childrenByClass(el, "_gui");
for (const remove of toRemove) {
el.removeChild(remove);
}
const wrapper = jquery_1.default("<div class='_gui _phantom_wrap _gui_test btn " +
"btn-default'></div>")[0];
this.guiUpdater.insertBefore(el.parentNode, wrapper, el);
this.guiUpdater.insertBefore(wrapper, el, null);
break;
default:
}
}
}
_navigationContextMenuHandler(wedEv, ev) {
// node is the node in the GUI tree which corresponds to the navigation item
// for which a context menu handler was required by the user.
const node = wedEv.data.node;
const dataNode = this.editor.toDataNode(node);
const prefixedName = this.mode.unresolveName(new salve_1.EName(dataNode.namespaceURI === null ? "" : dataNode.namespaceURI, dataNode.localName));
// We don't know this element.
if (prefixedName === undefined) {
return true;
}
// container, offset: location of the node in its parent.
const container = node.parentNode;
const offset = indexOf(container.childNodes, node);
// Create "insert" transformations for siblings that could be inserted
// before this node.
const actions = this.mode.getContextualActions("insert", prefixedName, container, offset);
// data to pass to transformations
const data = {
name: prefixedName,
moveCaretTo: this.editor.caretManager.makeCaret(container, offset),
};
const items = [];
for (const act of actions) {
const text = `${act.getLabelFor(data)} before this one`;
const $a = jquery_1.default(`<a tabindex='0' href='#'>${text}</a>`);
$a.click(data, act.boundTerminalHandler);
items.push(jquery_1.default("<li></li>").append($a)[0]);
}
// tslint:disable-next-line:no-unused-expression
new ContextMenu(this.editor.doc, ev.clientX, ev.clientY, items);
return false;
}
}
exports.TestDecorator = TestDecorator;
// tslint:disable-next-line:completed-docs
class TypeaheadAction extends wed_1.Action {
execute() {
const editor = this.editor;
const substringMatcher = (strs) => {
return (q, cb) => {
const re = new RegExp(q, "i");
const matches = [];
for (const str of strs) {
if (re.test(str)) {
matches.push(str);
}
}
cb(matches);
};
};
const testData = [];
for (let i = 0; i < 100; ++i) {
testData.push(`Test ${i}`);
}
const options = {
options: {
autoselect: true,
hint: true,
highlight: true,
minLength: 1,
},
datasets: [{
source: substringMatcher(testData),
limit: testData.length,
}],
};
const typeahead = editor.editingMenuManager.setupTypeaheadPopup(300, "Test", options, (obj) => {
if (obj != null) {
editor.insertText(obj);
}
}, undefined, true);
typeahead.hideSpinner();
const range = editor.caretManager.range;
// This is purposely not as intelligent as what real mode would need.
if (range != null && !range.collapsed) {
typeahead.setValue(range.toString());
}
}
}
// tslint:disable-next-line:completed-docs
class DraggableModalAction extends wed_1.Action {
get modal() {
if (this._modal === undefined) {
this._modal = this.editor.makeModal({ draggable: true });
}
return this._modal;
}
execute() {
this.modal.modal();
}
}
// tslint:disable-next-line:completed-docs
class DraggableResizableModalAction extends wed_1.Action {
get modal() {
if (this._modal === undefined) {
this._modal = this.editor.makeModal({
resizable: true,
draggable: true,
});
}
return this._modal;
}
execute() {
this.modal.modal();
}
}
// tslint:disable-next-line:completed-docs
class ResizableModalAction extends wed_1.Action {
get modal() {
if (this._modal === undefined) {
this._modal = this.editor.makeModal({ resizable: true });
}
return this._modal;
}
execute() {
this.modal.modal();
}
}
/**
* This mode is purely designed to help test wed, and nothing
* else. Don't derive anything from it and don't use it for editing.
*/
class TestMode extends generic_1.Mode {
constructor(editor, options) {
super(editor, options);
this.optionTemplate = {
metadata: true,
autoinsert: false,
ambiguous_fileDesc_insert: false,
fileDesc_insert_needs_input: false,
hide_attributes: false,
// We use nameSuffix to vary the name given to multiple instances.
nameSuffix: false,
stylesheets: false,
};
this.wedOptions = merge_options_1.default({}, this.wedOptions);
const suffix = options.nameSuffix != null ? options.nameSuffix : "";
this.wedOptions.metadata = {
name: `Test${suffix}`,
authors: ["Louis-Dominique Dubeau"],
description: "TEST MODE. DO NOT USE IN PRODUCTION!",
license: "MPL 2.0",
copyright: "Mangalam Research Center for Buddhist Languages",
};
this.wedOptions.label_levels = {
max: 2,
initial: 1,
};
if (options.hide_attributes) {
this.wedOptions.attributes = "hide";
}
else {
this.wedOptions.attributes = {
handling: "edit",
autohide: {
method: "selector",
elements: [{
selector: "div",
attributes: ["*", {
except: ["sample", "type", "subtype"],
}],
}],
},
};
}
this.typeaheadAction = new TypeaheadAction(editor, "Test typeahead", undefined, "<i class='fa fa-plus fa-fw'></i>", true);
this.draggableAction = new DraggableModalAction(editor, "Test draggable", undefined, undefined, true);
this.resizableAction = new ResizableModalAction(editor, "Test resizable", undefined, undefined, true);
this.draggableResizableAction = new DraggableResizableModalAction(editor, "Test draggable resizable", undefined, undefined, true);
}
getStylesheets() {
const stylesheets = this.options.stylesheets;
return stylesheets !== undefined ? stylesheets : [];
}
getToolbarButtons() {
return [this.typeaheadAction.makeButton()];
}
getContextualActions(transformationType, tag, container, offset) {
if (this.options.fileDesc_insert_needs_input &&
tag === "fileDesc" && transformationType === "insert") {
return [new wed_1.transformation.Transformation(this.editor, "insert", "foo",
// We don't need a real handler because it will not be called.
// tslint:disable-next-line:no-empty
() => { }, { needsInput: true })];
}
let ret = super.getContextualActions(transformationType, tag, container, offset);
if (this.options.ambiguous_fileDesc_insert &&
tag === "fileDesc" && transformationType === "insert") {
// We just duplicate the transformation.
ret = ret.concat(ret);
}
if (tag === "ref" &&
(transformationType === "insert" || transformationType === "wrap")) {
// It is a bit peculiar to tie the draggable and resizable actions to
// "ref", because it is not necessary, but meh...
ret.push(this.typeaheadAction, this.draggableAction, this.resizableAction, this.draggableResizableAction);
}
return ret;
}
makeDecorator() {
return new TestDecorator(this, this.editor, this.metadata, this.options);
}
getAttributeCompletions(attr) {
if (attr.name === "n") {
return ["completion1", "completion2"];
}
return [];
}
getValidator() {
return new Validator(this.editor.dataRoot);
}
}
exports.TestMode = TestMode;
exports.Mode = TestMode;
});
// LocalWords: Dubeau MPL Mangalam tei domutil btn getLabelFor tabindex href
// LocalWords: li nameSuffix subtype typeahead fw draggable resizable
//# sourceMappingURL=test-mode.js.map