<!DOCTYPE bindings [
  <!ENTITY % verbosioLocale SYSTEM "chrome://verbosio/locale/verbosio.dtd">
  %verbosioLocale;
  <!ENTITY % commonDTD SYSTEM "chrome://verbosio/content/verbosio.dtd">
  %commonDTD;
]>
<bindings
  xmlns="&XBL_NS;"
  xmlns:xbl="&XBL_NS;"
  xmlns:html="&XHTML_NS;"
  xmlns:xul="&XUL_NS;">

  <binding id="node-editor">
    <content>
      <xul:deck anonid="deck" selectedIndex="1">
        <xul:box anonid="null"/>
        <xul:cdata-editor   anonid="cdata"   xbl:inherits="label"/>
        <xul:source-editor  anonid="source"  xbl:inherits="label"/>
        <xul:element-editor anonid="element" xbl:inherits="label"/>
      </xul:deck>
    </content>

    <implementation>
      <field name="deck">
        document.getAnonymousElementByAttribute(this, "anonid", "deck");
      </field>
      <field name="cdataPanel">
        document.getAnonymousElementByAttribute(this, "anonid", "cdata");
      </field>
      <field name="sourcePanel">
        document.getAnonymousElementByAttribute(this, "anonid", "source");
      </field>

      <method name="doCommand">
        <parameter name="cmd"/>
        <body><![CDATA[
          switch (cmd) {
            case "sourceMode":
              this.deck.selectedPanel = this.sourcePanel;
              break;
          }
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="contextmenu" phase="capturing">
        verbosio.nodeEditor = this;
      </handler>
    </handlers>
  </binding>

  <binding id="cdata-editor">
    <content>
      <xul:groupbox anonid="groupbox" context="_child">
        <xul:caption xbl:inherits="label"/>
        <xul:textbox rows="10" cols="20" type="cdata-text">
          &sourceMode.menuitem;
          <xul:menuseparator/>
        </xul:textbox>
        <xul:menupopup 
          oncommand="
            var nodeEditorCmd = event.originalTarget.getAttribute('nodecmd');
            if (nodeEditorCmd) {
              verbosio.nodeEditor.doCommand(nodeEditorCmd);
            }
          ">
          &sourceMode.menuitem;
        </xul:menupopup>
      </xul:groupbox>
    </content>
    <implementation>
      <field name="groupbox">
        document.getAnonymousElementByAttribute(this, "anonid", "groupbox");
      </field>
      <property name="textbox" readonly="true">
        <getter>
          return this.groupbox.lastChild;
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="cdata-text"
    extends="chrome://verbosio/content/bindings/nodeEditor.xml#base-editor-multiline">
    <!-- XXX ajvincent Right now I have nothing else to extend this binding with. -->
    <implementation/>
  </binding>

  <binding id="source-editor">
    <content>
      <xul:groupbox anonid="groupbox" context="_child"> 
        <xul:textbox type="base-editor" rows="10" cols="20" anonid="input">
          &propertiesMode.menuitem;
          <menuseparator/>
        </xul:textbox>
        <xul:textbox multiline="true" rows="3" cols="20" 
                     readonly="true" anonid="errorOutput"/>
        <xul:menupopup 
          oncommand="
            var nodeEditorCmd = event.originalTarget.getAttribute('nodecmd');
            if (nodeEditorCmd) {
              verbosio.nodeEditor.doCommand(nodeEditorCmd);
            }
          ">
          &propertiesMode.menuitem;
        </xul:menupopup>
      </xul:groupbox>
    </content>

    <implementation>
      <field name="input" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "input");
      </field>

      <field name="errorOutput" readonly="true">
        document.getAnonymousElementByAttribute(this, "anonid", "errorOutput");
      </field>

      <property name="value">
        <getter>return this.input.value;</getter>
        <setter>this.input.value = val; return val;</setter>
      </property>

      <property name="language" readonly="true">
        <getter>return this.getAttribute("language")</getter>
      </property>

      <method name="getSummaryElement">
        <parameter name="aElement"/>
        <body><![CDATA[
          var namespacesMap = verbosio.getNamespacesMap(aElement);
          var response = "<foo ";
          for (var prop in namespacesMap) {
            if (prop == "xmlns") {
              response.value += "xmlns='";
            } else {
              response.value += "xmlns:" + prop + "='";
            }
            response.value += namespacesMap[prop] + "' ";
          }
          response += ">";
          return response;
        ]]></body>
      </method>

      <method name="resetForEditContent">
        <parameter name="aNode"/>
        <parameter name="aParentElement"/>
        <parameter name="syntaxChecker"/>
        <body><![CDATA[
          var source = "";
          if (aParentElement) {
            assert(aNode.parentNode == aParentElement, "What are you doing, Dave?");
            this.shortElementPrefix = this.getSummaryElement(aParentElement);
            this.checkXML = true;
            source = serializer.serializeToString(aNode, "application/xml");
            this.allowChildren = false;
          } else {
            this.checkXML = false;
          }
          this.syntaxChecker = syntaxChecker;
          this.input.resetToValue();

        ]]></body>
      </method>

      <method name="resetForNewContent">
        <parameter name="aParentElement"/>
        <parameter name="syntaxChecker"/>
        <body><![CDATA[
          if (aParentElement) {
            this.shortElementPrefix = this.getSummaryElement(aParentElement);
            this.checkXML = true;
            this.allowChildren = true;
          } else {
            this.checkXML = false;
          }
          this.syntaxChecker = syntaxChecker;
          this.input.resetToValue("");
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="input" phase="bubbling"><![CDATA[
        if (this.checkXML) {
          var source = this.shortElementPrefix + this.value + "</foo>";
          var testDoc = parser.parseFromString(source, "application/xml");
          var isXML = parser.checkRootElement(testDoc, "foo");
          if (!isXML) {
            this.setAttribute("invalid", "xml-well-formed");
            var range = testDoc.createRange();
            range.setStartBefore(testDoc);
            range.setEndAfter(testDoc);
            this.errorOutput.value = range.toString();
            return;
          }
          if (!this.allowChildren && testDoc.documentElement.firstChild.hasChildNodes()) {
            this.setAttribute("invalid", "child-nodes");
            this.errorOutput.value = "Child nodes are not allowed for this node.";
          }
        }

        if (this.syntaxChecker) {
          var consoleMessage = this.syntaxChecker.lastSyntaxError(this.value);
          if (consoleMessage) {
            this.setAttribute("invalid", "syntax-error");
            var output = consoleMessage.message;
            this.errorOutput.value = output;
            return;
          }
        }

        this.removeAttribute("invalid");
        this.errorOutput.value = "";
      ]]></handler>
    </handlers>
  </binding>

  <binding id="base-editor-multiline" extends="chrome://global/content/bindings/textbox.xml#textarea">
    <content>
      <xul:hbox class="base-editor-input-box" flex="1">
        <children includes="menuitem|menuseparator|menu"/>
        <html:textarea class="textbox-textarea" flex="1" anonid="input"
          xbl:inherits="onfocus,onblur,xbl:text=value,disabled,tabindex,rows,cols,readonly,wrap"/>
      </xul:hbox>
    </content>

    <implementation>
      <!-- Set to 1 so we only look it up once. -->      
      <field name="mEditor">1</field>

      <property name="editor" readonly="true">
        <getter><![CDATA[
          if (this.mEditor == 1) {
            var iface = Components.interfaces.nsIDOMNSEditableElement;
            try { 
              this.mEditor = (this.inputField instanceof iface) ?
                             this.inputField.editor :
                             null;
            }
            catch (e) {
              this.mEditor = null;
            }
          }
          return this.mEditor;
        ]]></getter>
      </property>

      <method name="reset">
        <body><![CDATA[
          this.value = this.getAttribute("value");
          if (this.editor) {
            this.editor.transactionManager.clear();
            return true;
          }
          return false;
        ]]></body>
      </method>

      <method name="resetToValue">
        <parameter name="aValue"/>
        <body><![CDATA[
          this.setAttribute("value", aValue);
          return this.reset();
        ]]></body>
      </method>

      <constructor>
        try {
          if (this.editor) {
            this.spellchecker = this.editor.inlineSpellChecker;
            dump("spellchecker: " + this.spellchecker + "\n");
          }
        }
        catch (e) {
          dump("Failed to get spellchecker: " + e + "\n");
        }
      </constructor>
    </implementation>
  </binding>

  <binding id="base-editor-input-box" extends="chrome://global/content/bindings/textbox.xml#input-box">
    <content context="_child">
      <children/>
      <xul:menupopup anonid="input-box-contextmenu"
         onpopupshowing="if (document.commandDispatcher.focusedElement != this.parentNode.firstChild)
                            this.parentNode.firstChild.focus();
                         this.parentNode.doPopupItemEnabling(this);"
         oncommand="var cmd = event.originalTarget.getAttribute('cmd');
                    if (cmd) {
                      this.parentNode.doCommand(cmd);
                      event.preventBubble();
                    }">
        <children includes="menuitem|menuseparator|menu"/>
        &baseEditorContext.content;
      </xul:menupopup>
    </content>
  </binding>
</bindings>
