Example #1
0
  addHandlers(): void {
    this.domlistener.addHandler(
      "included-element",
      util.classFromOriginalName("*", {}),
      (root: Node, _tree: Node, _parent: Node, _prev: Node | null,
       _next: Node | null, el: Element) => {
         // Skip elements which would already have been removed from the
         // tree. Unlikely but...
         if (!root.contains(el)) {
           return;
         }

         this.elementDecorator(root as Element, el);

         const klass = this.getAdditionalClasses(el);
         if (klass.length > 0) {
           el.className += ` ${klass}`;
         }
       });

    this.domlistener.addHandler(
      "children-changed",
      util.classFromOriginalName("*", {}),
      (root: Node, added: Node[], removed: Node[],
       _previousSibling: Node | null, _nextSibling: Node | null,
       el: Element) => {
         for (const child of added.concat(removed)) {
           if (isText(child) || (isElement(child) &&
                                 (child.classList.contains("_real") ||
                                  child.classList.contains("_phantom_wrap")))) {
             this.elementDecorator(root as Element, el);
             break;
           }
         }
       });

    this.domlistener.addHandler("text-changed",
                                util.classFromOriginalName("*", {}),
                                (root: Node, node: Text) => {
                                  this.elementDecorator(
                                    root as Element,
                                    node.parentNode! as Element);
                                });

    this.domlistener.addHandler("attribute-changed",
                                util.classFromOriginalName("*", {}),
                                (root: Node, el: Element) => {
                                  this.elementDecorator(root as Element, el);
                                });
  }
Example #2
0
    mode.getBibliographicalInfo().then((info: BibliographicalInfo) => {
      const allValues: BibliographicalItem[] = [];
      for (const key of Object.keys(info)) {
        allValues.push(info[key]);
      }

      const citedValues: BibliographicalItem[] = [];
      const refs = editor.guiRoot.querySelectorAll("._real.ref");
      // tslint:disable-next-line:prefer-for-of
      for (let refIx = 0; refIx < refs.length; ++refIx) {
        const ref = refs[refIx];
        const origTarget = ref.getAttribute(util.encodeAttrName("target"))!;
        if (origTarget.lastIndexOf("/bibliography/", 0) !== 0) {
          continue;
        }

        citedValues.push(info[origTarget]);
      }

      zoteroEngine.add(allValues);
      citedEngine.add(citedValues);
      if (range !== undefined) {
        ta.setValue(range.toString());
      }
      ta.hideSpinner();
    });
Example #3
0
export function insertPtr(editor: EditorAPI,
                          data: TargetedTransformationData): void {
  const caret = editor.caretManager.getDataCaret()!;
  let parent = caret.node;
  const index = caret.offset;

  // The data.target value is the wed ID target of the ptr. We must find this
  // element and add a data ID.
  const target = editor.guiRoot.ownerDocument.getElementById(data.target)!;
  const dataId = data.target.slice(4);
  target.setAttribute(util.encodeAttrName("xml:id"), dataId);
  $.data(target, "wed_mirror_node").setAttributeNS(
    // tslint:disable-next-line:no-http-string
    "http://www.w3.org/XML/1998/namespace", "xml:id", dataId);

  const mode = editor.modeTree.getMode(parent);
  const ename = mode.getAbsoluteResolver().resolveName("ptr")!;

  const ptr = makeElement(parent.ownerDocument,
                          ename.ns, "ptr", { target: `#${dataId}` });
  editor.dataUpdater.insertAt(parent, index, ptr);

  // The original parent and index information are no necessarily representative
  // because insertAt can do quite a lot of things to insert the node.
  parent = ptr.parentNode!;
  editor.caretManager.setCaret(parent, _indexOf.call(parent.childNodes, ptr));
}
Example #4
0
  citDecorator(root: Element, el: Element): void {
    this.elementDecorator(root, el);

    let ref;
    let child = el.firstElementChild;
    while (child !== null) {
      const next = child.nextElementSibling;
      if (child.classList.contains("_ref_space") ||
          child.classList.contains("_cit_bullet")) {
        this.guiUpdater.removeNode(child);
      }
      else if (child.classList.contains("ref")) {
        ref = child;
      }
      child = next;
    }

    if (ref) {
      const space = el.ownerDocument.createElement("div");
      space.className = "_text _phantom _ref_space";
      space.textContent = " ";
      el.insertBefore(space, ref.nextSibling);
    }

    if (el.querySelector(`*[${util.encodeAttrName("xml:lang")}='pi-Latn']`) !==
       null) {
      const div = el.ownerDocument.createElement("div");
      div.className = "_phantom _text _cit_bullet";
      div.style.position = "absolute";
      div.style.left = "-1em";
      div.textContent = WHEEL;
      this.guiUpdater.insertNodeAt(el, 0, div);
      (el as HTMLElement).style.position = "relative";
    }
  }
Example #5
0
  idDecorator(_root: Element, el: Element): void {
    const refman = this.refmans.getRefmanForElement(el);
    if (refman !== null) {
      let wedId = el.id;
      if (wedId === "") {
        const id = el.getAttribute(util.encodeAttrName("xml:id"));
        const idMan = this._getIDManagerForRefman(refman);
        wedId = `BTW-${id !== null ? id : idMan.generate()}`;
        el.id = wedId;
      }

      // We have some reference managers that don't derive from ReferenceManager
      // and thus do not have this method.
      if (refman instanceof LabelManager) {
        refman.allocateLabel(wedId);
      }
    }
  }
Example #6
0
  execute(data: TransformationData): void {
    const editor = this.editor;

    const dataCaret = editor.caretManager.getDataCaret(true)!;
    const mode = editor.modeTree.getMode(dataCaret.node);
    if (!(mode instanceof Mode)) {
      throw new Error("expected BTW mode");
    }

    const decorator = editor.modeTree.getDecorator(dataCaret.node);
    if (!(decorator instanceof BTWDecorator)) {
      throw new Error("our decorator must be a BTWDecorator");
    }

    const doc = editor.guiRoot.ownerDocument;
    const mappings = mode.getAbsoluteNamespaceMappings();
    const senses = editor.guiRoot.querySelectorAll(
      util.classFromOriginalName("btw:sense", mappings));
    const labels: Element[] = [];
    const radios: Element[] = [];
    // tslint:disable-next-line:prefer-for-of
    for (let i = 0; i < senses.length; ++i) {
      const sense = senses[i];
      let dataNode = $.data(sense, "wed_mirror_node");
      const termNodes = btwUtil.termsForSense(sense, mappings);
      const terms: string[] = [];
      // tslint:disable-next-line:prefer-for-of
      for (let tix = 0; tix < termNodes.length; ++tix) {
        terms.push($.data(termNodes[tix], "wed_mirror_node").textContent);
      }
      const senseLabel = decorator.refmans.getSenseLabel(sense);

      let span = doc.createElement("span");
      span.textContent = ` [${senseLabel}] ${terms.join(", ")}`;
      span.setAttribute("data-wed-id", sense.id);

      let radio = doc.createElement("input");
      radio.type = "radio";
      radio.name = "sense";

      let div = doc.createElement("div");
      div.appendChild(radio);
      div.appendChild(span);

      labels.push(div);
      radios.push(radio);

      const subsenses = domutil.childrenByClass(sense, "btw:subsense");
      for (const subsense of subsenses) {
        dataNode = $.data(subsense, "wed_mirror_node");
        const subsenseLabel = decorator.refmans.getSubsenseLabel(subsense);
        let child = dataNode.firstElementChild;
        let explanation;
        while (child) {
          if (child.tagName === "btw:explanation") {
            explanation = child;
            break;
          }
          child = child.nextElementSibling;
        }

        span = doc.createElement("span");
        span.textContent = ` [${subsenseLabel}] ${explanation.textContent}`;
        span.setAttribute("data-wed-id", subsense.id);

        radio = doc.createElement("input");
        radio.type = "radio";
        radio.name = "sense";

        div = doc.createElement("div");
        div.appendChild(radio);
        div.appendChild(span);

        labels.push(div);
        radios.push(radio);
      }
    }

    const hyperlinkModal = mode.hyperlinkModal;
    const primary = hyperlinkModal.getPrimary()[0] as HTMLButtonElement;
    const body = doc.createElement("div");
    for (const label of labels) {
      body.appendChild(label);
    }
    $(radios).on("click.wed", () => {
      primary.disabled = false;
      primary.classList.remove("disabled");
    });
    primary.disabled = true;
    primary.classList.add("disabled");
    hyperlinkModal.setBody(body);
    hyperlinkModal.modal(() => {
      const clicked = hyperlinkModal.getClickedAsText();
      if (clicked === "Insert") {
        const id = body.querySelector("input[type='radio']:checked")!
              .nextElementSibling!.getAttribute("data-wed-id")!;
        mode.insertPtrTr.execute({ ...data, target: id });
      }
    });
  }
Example #7
0
  execute(data: TransformationData): void {
    const editor = this.editor;

    const dataCaret = editor.caretManager.getDataCaret(true)!;
    const mode = editor.modeTree.getMode(dataCaret.node);
    if (!(mode instanceof Mode)) {
      throw new Error("expected BTW mode");
    }

    const doc = editor.guiRoot.ownerDocument;
    const mappings = mode.getAbsoluteNamespaceMappings();
    const examples =
      editor.guiRoot.querySelectorAll(domutil.toGUISelector(
        "btw:example, btw:example-explained", mappings));
    const labels: Element[] = [];
    const radios: Element[] = [];
    // tslint:disable-next-line:prefer-for-of
    for (let i = 0; i < examples.length; ++i) {
      const example = examples[i];
      const dataNode = $.data(example, "wed_mirror_node");
      let child = dataNode.firstElementChild;
      let cit;
      while (child) {
        if (child.tagName === "btw:cit") {
          cit = child;
          break;
        }
        child = child.nextElementSibling;
      }

      const abbr = example.querySelector(util.classFromOriginalName("ref",
                                                                    mappings));
      // We skip those examples that do not have a ref in them yet, as links to
      // them are meaningless.
      if (abbr === null) {
        continue;
      }

      const abbrCopy = abbr.cloneNode(true) as Element;
      child = abbrCopy.firstElementChild;
      while (child) {
        const next = child.nextElementSibling;
        if (child.classList.contains("_gui")) {
          abbrCopy.removeChild(child);
        }
        child = next;
      }

      const span = doc.createElement("span");
      span.setAttribute("data-wed-id", example.id);
      span.textContent = ` ${abbrCopy.textContent} ${cit.textContent}`;

      const radio = doc.createElement("input");
      radio.type = "radio";
      radio.name = "example";

      const div = doc.createElement("div");
      div.appendChild(radio);
      div.appendChild(span);

      labels.push(div);
      radios.push(radio);
    }

    const hyperlinkModal = mode.hyperlinkModal;
    const primary = hyperlinkModal.getPrimary()[0] as HTMLButtonElement;
    const body = doc.createElement("div");
    for (const label of labels) {
      body.appendChild(label);
    }

    $(radios).on("click.wed", () => {
      primary.disabled = false;
      primary.classList.remove("disabled");
    });
    primary.disabled = true;
    primary.classList.add("disabled");
    hyperlinkModal.setBody(body);
    hyperlinkModal.modal(() => {
      const clicked = hyperlinkModal.getClickedAsText();
      if (clicked === "Insert") {
        const id = body.querySelector("input[type='radio']:checked")!
              .nextElementSibling!.getAttribute("data-wed-id")!;
        mode.insertPtrTr.execute({ ...data, target: id });
      }
    });
  }
Example #8
0
 classFromOriginalName(name: string): string {
   return util.classFromOriginalName(name, this.mapping);
 }
Example #9
0
  protected dispatch(root: Element, el: Element): void {
    const klass = this.getAdditionalClasses(el);
    if (klass.length !== 0) {
      el.className += ` ${klass}`;
    }

    const name = util.getOriginalName(el);
    let skipDefault = false;
    switch (name) {
    case "btw:overview":
    case "btw:sense-discrimination":
    case "btw:historico-semantical-data":
    case "btw:credits":
      this.headingDecorator.unitHeadingDecorator(el);
      break;
    case "btw:definition":
    case "btw:english-renditions":
    case "btw:english-rendition":
    case "btw:etymology":
    case "btw:contrastive-section":
    case "btw:antonyms":
    case "btw:cognates":
    case "btw:conceptual-proximates":
    case "btw:other-citations":
    case "btw:citations":
      this.headingDecorator.sectionHeadingDecorator(el);
      break;
    case "btw:semantic-fields":
      this.headingDecorator.sectionHeadingDecorator(el);
      break;
    case "btw:sf":
      this.sfDecorator(root, el);
      skipDefault = true;
      break;
    case "ptr":
      this.ptrDecorator(root, el);
      break;
    case "foreign":
      this.languageDecorator(el);
      break;
    case "ref":
      this.refDecorator(root, el);
      break;
    case "btw:example":
      this.idDecorator(root, el);
      break;
    case "btw:cit":
      this.citDecorator(root, el);
      skipDefault = true; // citDecorator calls elementDecorator
      break;
    case "btw:explanation":
      this.explanationDecorator(root, el);
      skipDefault = true; // explanationDecorator calls elementDecorator
      break;
    case "btw:none":
      this.noneDecorator(el);
      // THIS ELEMENT DOES NOT GET THE REGULAR DECORATION.
      skipDefault = true;
      break;
    default:
      break;
    }

    if (!skipDefault) {
      this.elementDecorator(root, el);
    }
  }
Example #10
0
  // tslint:disable-next-line:cyclomatic-complexity max-func-body-length
  linkingDecorator(root: Element, el: Element, isPtr: boolean): void {
    let origTarget = el.getAttribute(util.encodeAttrName("target"));
    // XXX This should become an error one day. The only reason we need this now
    // is that some of the early test files had <ref> elements without targets.
    if (origTarget === null) {
      origTarget = "";
    }

    origTarget = origTarget.trim();

    const doc = root.ownerDocument;
    if (origTarget.lastIndexOf("#", 0) === 0) {
      // Internal target
      // Add BTW in front because we want the target used by wed.
      const targetId = origTarget.replace(/#(.*)$/, "#BTW-$1");

      const text = doc.createElement("div");
      text.className = "_text _phantom _linking_deco";
      const a = doc.createElement("a");
      a.className = "_phantom";
      a.setAttribute("href", targetId);
      text.appendChild(a);
      if (isPtr) {
        // _linking_deco is used locally to make this function idempotent
        {
          let child = el.firstElementChild;
          while (child !== null) {
            const next = child.nextElementSibling;
            if (child.classList.contains("_linking_deco")) {
              this.guiUpdater.removeNode(child);
              break; // There is only one.
            }
            child = next;
          }
        }

        const refman = this.refmans.getRefmanForElement(el);

        // Find the referred element. Slice to drop the #.
        let target = doc.getElementById(targetId.slice(1));

        // An undefined or null refman can happen when first decorating the
        // document.
        let label;
        if (refman !== null) {
          if (refman instanceof LabelManager) {
            if (refman.name === "sense" || refman.name === "subsense") {
              label = refman.idToLabel(targetId.slice(1));
              label = label !== undefined ? `[${label}]` : undefined;
            }
          }
          else {
            // An empty target can happen when first decorating the document.
            if (target !== null) {
              label = refman.getPositionalLabel(
                this.editor.toDataNode(el) as Element,
                this.editor.toDataNode(target) as Element);
            }
          }
        }

        if (label === undefined) {
          label = targetId;
        }

        a.textContent = label;

        // A ptr contains only attributes, no text, so we can just append.
        const pair = this.mode.nodesAroundEditableContents(el);
        this.guiUpdater.insertBefore(el, text, pair[1]);

        if (target !== null) {
          const targetName = util.getOriginalName(target);

          // Reduce the target to something sensible for tooltip text.
          if (targetName === "btw:sense") {
            const terms = target.querySelectorAll(this.mapped.toGUISelector(
              this.senseTooltipSelector));
            let html = "";
            for (let i = 0; i < terms.length; ++i) {
              const term = terms[i];
              html += term.innerHTML;
              if (i < terms.length - 1) {
                html += ", ";
              }
            }
            target = target.ownerDocument.createElement("div");
            // tslint:disable-next-line:no-inner-html
            target.innerHTML = html;
          }
          else if (targetName === "btw:subsense") {
            let child = target.firstElementChild;
            while (child !== null) {
              if (child.classList.contains("btw:explanation")) {
                target = child.cloneNode(true) as HTMLElement;
                break;
              }
              child = child.nextElementSibling;
            }
          }
          else if (targetName === "btw:example") {
            target = null;
          }

          if (target !== null) {
            const nodes =
              target.querySelectorAll(".head, ._gui, ._explanation_number");
            // tslint:disable-next-line:prefer-for-of
            for (let nodeIx = 0; nodeIx < nodes.length; ++nodeIx) {
              const node = nodes[nodeIx];
              node.parentNode!.removeChild(node);
            }
            tooltip($(text), { title: `<div>${target.innerHTML}</div>`,
                               html: true,
                               container: "body",
                               trigger: "hover" });
          }
        }
      }
      else {
        throw new Error("internal error: ref with unexpected target");
      }
    }
    else {
      // External target
      const biblPrefix = "/bibliography/";
      if (origTarget.lastIndexOf(biblPrefix, 0) === 0) {
        // Bibliographical reference...
        if (isPtr) {
          throw new Error("internal error: bibliographic " +
                          "reference recorded as ptr");
        }

        const targetId = origTarget;

        // It is okay to skip the tree updater for these operations.
        let child = el.firstElementChild;
        while (child !== null) {
          const next = child.nextElementSibling;
          if (child.classList.contains("_ref_abbr") ||
              child.classList.contains("_ref_paren")) {
            this.guiUpdater.removeNode(child);
          }
          child = next;
        }

        const abbr = doc.createElement("div");
        abbr.className = "_text _phantom _ref_abbr";
        this.guiUpdater.insertBefore(el, abbr, el.firstChild);
        const open = doc.createElement("div");
        open.className = "_phantom _decoration_text _ref_paren " +
          "_open_ref_paren _start_wrapper";
        open.textContent = "(";
        this.guiUpdater.insertBefore(el, open, abbr);

        const close = doc.createElement("div");
        close.className = "_phantom _decoration_text " +
          "_ref_paren _close_ref_paren _end_wrapper";
        close.textContent = ")";
        this.guiUpdater.insertBefore(el, close, null);

        // tslint:disable-next-line:no-floating-promises
        this.fetchAndFillBiblData(targetId, el, abbr);
      }
    }
  }