Search code examples
javascripthyperlinkoxygenxml

functionality is not working for newly created menu


I'm working on creating the Link menu on the Oxygen Web-Author, there is already having two menu's called "ulink" and "uri". When I include the third menu called xref, the functionality of xref is not working fine, It's taking "ulink" functionality in the oxygen framework.js.

var insertWebLinkActionId =
      sync.docbook.DocbookExtension.prototype.version == 5 ? "insert.web.link" : "insert.web.ulink";
    var originalInsertWebLinkAction = actionsManager.getActionById(insertWebLinkActionId);
    if(originalInsertWebLinkAction) {
      var webLinkOperationClass = sync.docbook.DocbookExtension.prototype.version == 5 ?
        "ro.sync.ecss.extensions.docbook.link.InsertExternalLinkOperation" :
        "ro.sync.ecss.extensions.docbook.link.InsertULink";
      var insertWebLinkAction = new sync.actions.InsertWebLink(
        originalInsertWebLinkAction,
        webLinkOperationClass,
        editor,
        'url_value');
      actionsManager.registerAction(insertWebLinkActionId, insertWebLinkAction);
    }

Here they already inserted menu by using the above code, in that insert.web.link and insert.web.ulink represent the corresponding menu from the translation.xml

<key type="action" value="insert.web.ulink">
        <comment>Insert Link action name.</comment>
        <val lang="en_US">Web Link (ulink)...</val>
 </key>
...
<key type="action" value="insert.cross.reference.xref">
        <comment>Insert Link action name.</comment>
        <val lang="en_US">Cross reference (xref)...</val>
</key>

So I want to insert the Xref menu to the javascript, the Xref menu and class path represent insert.cross.reference.xref and ro.sync.ecss.extensions.docbook.link.InsertXrefOperation correspondingly.


Solution

  • You can implement your own version of the "Insert Cross Reference (xref)" action in your DocBook framework extension. To do this you need to:

    1. Create a custom action that prompts the user to choose the destination file and the ID of the target element. The code for this action needs to be added in the "web" folder of you framework in a new JS file. You can find below a minimal working example:
    var DocBookCrossRefXrefAction = function (editor) {
      sync.actions.AbstractAction.call(this);
      this.editor = editor;
      this.xrefTargetsDialog = null;
    };
    DocBookCrossRefXrefAction.prototype = 
      Object.create(sync.actions.AbstractAction.prototype);
    DocBookCrossRefXrefAction.prototype.constructor = DocBookCrossRefXrefAction;
    
    DocBookCrossRefXrefAction.prototype.getDisplayName = function () {
      return 'Insert cross reference (xref)';
    };
    
    DocBookCrossRefXrefAction.prototype.actionPerformed = function (callback) {
      // This callback might have to be called at a later point, so save it until then.
      this.callback = callback || function () {};
    
      var chooser = workspace.getUrlChooser();
      var context = new sync.api.UrlChooser.Context(sync.api.UrlChooser.Type.EXTERNAL_REF);
      chooser.chooseUrl(context, this.showXrefTargetChooser.bind(this), 
        sync.api.UrlChooser.Purpose.CHOOSE);
    };
    
    DocBookCrossRefXrefAction.prototype.showXrefTargetChooser = function (url) {
      if (url) {
        // Create a dialog to display all targets, so the user can select one to insert 
        // cross reference.
        this.xrefTargetsDialog = this.createXrefTargetsDialog();
        this.populateXrefTargetsDialog(url);
    
        this.xrefTargetsDialog.onSelect(function (key, e) {
          if (key === 'ok') {
            e.preventDefault();
            this.xrefTargetChosen();
          } else {
            this.callback();
          }
        }.bind(this));
        this.xrefTargetsDialog.show();
      } else {
        this.callback();
      }
    };
    
    DocBookCrossRefXrefAction.prototype.createXrefTargetsDialog = function () {
      var dialog = workspace.createDialog();
      dialog.setTitle('Choose cross reference');
      dialog.setButtonConfiguration(sync.api.Dialog.ButtonConfiguration.OK_CANCEL);
      dialog.setPreferredSize(700, 500);
      dialog.setResizable(true);
      return dialog;
    };
    
    DocBookCrossRefXrefAction.prototype.populateXrefTargetsDialog = function (url) {
      this.editor.getActionsManager()
        .invokeOperation('ro.sync.servlet.operation.FindXrefTargetsOperation', {url: url})
        .then(function(str) { return JSON.parse(str);})
        .then(this.xrefTargetsReceived.bind(this))
    };
    
    DocBookCrossRefXrefAction.prototype.xrefTargetsReceived = function (targets) {
      var container = this.xrefTargetsDialog.getElement();
      if (!targets || !targets.length) {
        container.textContent = 'No cross reference targets found in the chosen file';
      } else {
        for (var i = 0; i < targets.length; i++) {
          var radio = document.createElement('input');
          radio.name = 'docbook-ref-table-radio';
          radio.type = 'radio';
          radio.dataset.id = targets[i].id;
    
          var label = document.createElement('label');
          label.style = 'display: block';
          label.appendChild(radio);
          label.appendChild(document.createTextNode(targets[i].nodeName + ' (#' + 
            targets[i].id  + ') - ' + targets[i].content.substring(0, 50)));
    
          container.appendChild(label);
        }
      }
    };
    
    DocBookCrossRefXrefAction.prototype.xrefTargetChosen = function() {
      var targetRadio = this.xrefTargetsDialog.getElement().querySelector(
        'input[name="docbook-ref-table-radio"]:checked');
      if (targetRadio) {
        this.editor.getActionsManager().invokeOperation('InsertFragmentOperation',
          {fragment: '<xref linkend="' + targetRadio.dataset.id + '"/>'})
          .then(this.xrefInserted.bind(this));
      }
    };
    
    DocBookCrossRefXrefAction.prototype.xrefInserted = function () {
      this.xrefTargetsDialog && this.xrefTargetsDialog.dispose();
      this.callback();
    };
    
    DocBookCrossRefXrefAction.prototype.dispose = function() {
      this.xrefTargetsDialog && this.xrefTargetsDialog.dispose();
    };
    
    goog.events.listen(workspace, sync.api.Workspace.EventType.EDITOR_LOADED, function(e) {
      var editor = e.editor;
      goog.events.listen(editor, sync.api.Editor.EventTypes.ACTIONS_LOADED, function(e) {
        editor.getActionsManager().registerAction('insert.cross.reference.xref', 
          new DocBookCrossRefXrefAction(editor));
      });
    });
    
    1. This action uses a server-side AuthorOperationWithResult to collect xref targets from the file chosen by the user. You can find below the code of this Java operation:
    package ro.sync.servlet.operation;
    
    import java.net.URL;
    import java.util.ArrayList;
    
    import javax.xml.parsers.DocumentBuilder;
    
    import org.codehaus.jackson.map.ObjectMapper;
    import org.w3c.dom.Attr;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.NamedNodeMap;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.InputSource;
    
    import ro.sync.basic.util.URLUtil;
    import ro.sync.ecss.extensions.api.ArgumentsMap;
    import ro.sync.ecss.extensions.api.AuthorOperationException;
    import ro.sync.ecss.extensions.api.webapp.AuthorDocumentModel;
    import ro.sync.ecss.extensions.api.webapp.AuthorOperationWithResult;
    import ro.sync.ecss.extensions.api.webapp.WebappRestSafe;
    import ro.sync.xml.parser.ParserCreator;
    
    @WebappRestSafe
    public class FindXrefTargetsOperation extends AuthorOperationWithResult {
    
      @Override
      public String doOperation(AuthorDocumentModel model, ArgumentsMap args) 
          throws AuthorOperationException {
        String urlString = (String) args.getArgumentValue("url");
        try {
          URL url = URLUtil.addAuthenticationInfo(
            model.getAuthorAccess().getEditorAccess().getEditorLocation(), 
            new URL(urlString));
          InputSource is = new InputSource(url.toString());
          DocumentBuilder docBuilder = ParserCreator.newSchemaAwareDocumentBuilder();
          Document document = docBuilder.parse(is);
    
          ArrayList<XrefTarget> xrefTargets = new ArrayList<>();
          gatherXrefTargets(document, xrefTargets);
          return new ObjectMapper().writeValueAsString(xrefTargets);
        } catch (Exception e) {
          throw new AuthorOperationException(e.getMessage(), e);
        }
      }
    
      public static class XrefTarget {
        public final String id;
        public final String nodeName;
        public final String content;
    
        public XrefTarget(Element elem) {
          this.id = getIDValue(elem);
          this.nodeName = elem.getTagName();
          this.content = elem.getTextContent();
        }
      }
    
    
      private static void gatherXrefTargets(Node node, ArrayList<XrefTarget> xrefTargets) {
        if (node.getNodeType() == Node.ELEMENT_NODE) {
          Element elem = (Element) node;
          if (getIDValue(elem) != null) {
            xrefTargets.add(new XrefTarget(elem));
          }
        }
        NodeList childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) { 
          gatherXrefTargets(childNodes.item(i), xrefTargets);
        }
      }
    
      public static String getIDValue(Element elem) {
        NamedNodeMap attributes = elem.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
          Attr attribute = (Attr)attributes.item(i);
          // There are some ID attributes defined as such in the schema file.
          if (attribute.isId() || "xml:id".equals(attribute.getName()) || 
            "id".equals(attribute.getName())) {
            return attribute.getValue();
          }
        }
        return null;
      }
    }
    

    You need to compile this Java class and add it as a JAR file in the classpath of your framework. More details here .