<?xml version="1.0"?>

<bindings id="socialChatBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xbl="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="chatbox">
    <content orient="vertical" mousethrough="never">
      <xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected,activity"
                onclick="document.getBindingParent(this).toggle();" align="baseline">
        <xul:image class="chat-status-icon" xbl:inherits="src=image"/>
        <xul:label class="chat-title" flex="1" xbl:inherits="value=label" crop="center"/>
        <xul:toolbarbutton class="chat-close-button chat-toolbarbutton"
                           oncommand="document.getBindingParent(this).close();"/>
      </xul:hbox>
      <xul:iframe anonid="iframe" class="chat-frame" flex="1"
                  xbl:inherits="src,origin,collapsed=minimized" type="content"/>
    </content>

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

      <property name="minimized">
        <getter>
          return this.getAttribute("minimized") == "true";
        </getter>
        <setter>
          this.isActive = !val;
          if (val)
            this.setAttribute("minimized", "true");
          else
            this.removeAttribute("minimized");
        </setter>
      </property>

      <property name="isActive">
        <getter>
          return this.iframe.docShell.isActive;
        </getter>
        <setter>
          this.iframe.docShell.isActive = !!val;

          // let the chat frame know if it is being shown or hidden
          let evt = this.iframe.contentDocument.createEvent("CustomEvent");
          evt.initCustomEvent(val ? "socialFrameShow" : "socialFrameHide", true, true, {});
          this.iframe.contentDocument.documentElement.dispatchEvent(evt);
        </setter>
      </property>

      <method name="init">
        <parameter name="aProvider"/>
        <parameter name="aURL"/>
        <parameter name="aCallback"/>
        <body><![CDATA[
          this._callback = aCallback;
          this.setAttribute("origin", aProvider.origin);
          this.setAttribute("src", aURL);
        ]]></body>
      </method>

      <method name="close">
        <body><![CDATA[
          this.parentNode.remove(this);
        ]]></body>
      </method>

      <method name="toggle">
        <body><![CDATA[
          this.minimized = !this.minimized;
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="focus" phase="capturing">
        this.parentNode.selectedChat = this;
      </handler>
      <handler event="DOMContentLoaded"><![CDATA[
        this.isActive = !this.minimized;
        if (this._callback) this._callback(this.iframe.contentWindow);
        let chatbox = this;
        function chatActivity() {
          chatbox.setAttribute("activity", true);
          chatbox.parentNode.updateTitlebar(chatbox);
        };
        let iframeWindow = this.iframe.contentWindow;
        iframeWindow.addEventListener("socialChatActivity", chatActivity);
        iframeWindow.addEventListener("unload", function unload() {
          iframeWindow.removeEventListener("unload", unload);
          iframeWindow.removeEventListener("socialChatActivity", chatActivity);
        });
      ]]></handler>
      <handler event="DOMTitleChanged"><![CDATA[
        this.setAttribute('label', this.iframe.contentDocument.title);
        this.parentNode.updateTitlebar(this);
      ]]></handler>
      <handler event="DOMLinkAdded"><![CDATA[
        // much of this logic is from DOMLinkHandler in browser.js
        // this sets the presence icon for a chat user, we simply use favicon style updating
        let link = event.originalTarget;
        let rel = link.rel && link.rel.toLowerCase();
        if (!link || !link.ownerDocument || !rel || !link.href)
          return;
        if (link.rel.indexOf("icon") < 0)
          return;

        let uri = DOMLinkHandler.getLinkIconURI(link);
        if (!uri)
          return;

        // we made it this far, use it
        this.setAttribute('image', uri.spec);
        this.parentNode.updateTitlebar(this);
      ]]></handler>
    </handlers>
  </binding>

  <binding id="chatbar">
    <content>
      <xul:hbox align="end" pack="end" anonid="innerbox" class="chatbar-innerbox" mousethrough="always" flex="1">
        <xul:toolbarbutton anonid="nub" class="chatbar-button" type="menu" collapsed="true" mousethrough="never">
          <xul:menupopup anonid="nubMenu" oncommand="document.getBindingParent(this).swapChat(event)"/>
        </xul:toolbarbutton>
        <xul:spacer flex="1" anonid="spacer" class="chatbar-overflow-spacer"/>
        <children/>
      </xul:hbox>
    </content>

    <implementation implements="nsIDOMEventListener">

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

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

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

      <property name="emptyWidth">
        <getter>
          return document.getAnonymousElementByAttribute(this, "anonid", "spacer").boxObject.width;
        </getter>
      </property>

      <property name="selectedChat">
        <getter><![CDATA[
          return this._selectedChat;
        ]]></getter>
        <setter><![CDATA[
          if (this._selectedChat)
            this._selectedChat.removeAttribute("selected");
          this._selectedChat = val;
          if (val) {
            this._selectedChat.setAttribute("selected", "true");
            this._selectedChat.removeAttribute("activity");
          }
        ]]></setter>
      </property>

      <field name="menuitemMap">new WeakMap()</field>
      <field name="chatboxForURL">new Map();</field>

      <property name="firstCollapsedChild">
        <getter><![CDATA[
          let child = this.lastChild;
          while (child && !child.collapsed) {
            child = child.previousSibling;
          }
          return child;
        ]]></getter>
      </property>

      <property name="firstVisibleChild">
        <getter><![CDATA[
          let child = this.firstChild;
          while (child && child.collapsed) {
            child = child.nextSibling;
          }
          return child;
        ]]></getter>
      </property>

      <property name="firstRemovableChild">
        <getter><![CDATA[
          let child = this.firstChild;
          // find the first visible non-focused chatbox, always keep one visible if we
          // have enough width to do so.
          while (child &&
                (child.collapsed || child == this.selectedChat)) {
            child = child.nextSibling;
          }
          if (!child && this.selectedChat) {
            child = this.selectedChat;
          }
          return child;
        ]]></getter>
      </property>

      <method name="resize">
        <body><![CDATA[
          let child = this.firstCollapsedChild;
          if (child && this.emptyWidth > child.viewWidth) {
            this.showChat(child);
          }
          if (!this.firstCollapsedChild) {
            window.removeEventListener("resize", this);
            this.menupopup.parentNode.collapsed = true;
          }
        ]]></body>
      </method>

      <method name="updateTitlebar">
        <parameter name="aChatbox"/>
        <body><![CDATA[
          if (aChatbox.collapsed) {
            let menuitem = this.menuitemMap.get(aChatbox);
            if (aChatbox.getAttribute("activity")) {
              menuitem.setAttribute("activity", true);
              this.nub.setAttribute("activity", true);
            }
            menuitem.setAttribute("label", aChatbox.getAttribute("label"));
            menuitem.setAttribute("image", aChatbox.getAttribute("image"));
          }
        ]]></body>
      </method>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (aEvent.type == "resize") {
            this.resize();
          }
        ]]></body>
      </method>

      <method name="swapChat">
        <parameter name="aEvent"/>
        <body><![CDATA[
          let menuitem = aEvent.target;
          let newChat = menuitem.chat;
          let oldChat = this.firstVisibleChild;
          if (oldChat)
            this.collapseChat(oldChat);
          if (newChat) 
            this.showChat(newChat);
        ]]></body>
      </method>

      <method name="collapseChat">
        <parameter name="aChatbox"/>
        <body><![CDATA[
          aChatbox.viewWidth = aChatbox.getBoundingClientRect().width;
          aChatbox.collapsed = true;
          aChatbox.isActive = false;
          let menu = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
          menu.setAttribute("class", "menuitem-iconic");
          menu.setAttribute("label", aChatbox.iframe.contentDocument.title);
          menu.setAttribute("image", aChatbox.getAttribute("image"));
          menu.chat = aChatbox;
          this.menuitemMap.set(aChatbox, menu);
          this.menupopup.appendChild(menu);
          this.menupopup.parentNode.collapsed = false;
        ]]></body>
      </method>

      <method name="showChat">
        <parameter name="aChatbox"/>
        <body><![CDATA[
          let menuitem = this.menuitemMap.get(aChatbox);
          this.menuitemMap.delete(aChatbox);
          this.menupopup.removeChild(menuitem);
          aChatbox.collapsed = false;
          aChatbox.isActive = !aChatbox.minimized;
        ]]></body>
      </method>

      <method name="remove">
        <parameter name="aChatbox"/>
        <body><![CDATA[
          if (this.selectedChat == aChatbox) {
            this.selectedChat = aChatbox.previousSibling ? aChatbox.previousSibling : aChatbox.nextSibling
          }
          this.removeChild(aChatbox);
          this.resize();
          this.chatboxForURL.delete(aChatbox.getAttribute('src'));
        ]]></body>
      </method>

      <method name="removeAll">
        <body><![CDATA[
          while (this.firstChild) {
            this.removeChild(this.firstChild);
          }
          this.chatboxForURL = new Map();
        ]]></body>
      </method>

      <method name="openChat">
        <parameter name="aProvider"/>
        <parameter name="aURL"/>
        <parameter name="aCallback"/>
        <parameter name="aMode"/>
        <body><![CDATA[
          let cb = this.chatboxForURL.get(aURL);
          if (cb) {
            cb = cb.get();
            if (cb.parentNode) {
              // ensure this chatbox is visible
              if (this.selectedChat != cb)
                this.selectedChat = cb;
              if (cb.collapsed)
                this.showChat(cb);
              if (aCallback)
                aCallback(cb.iframe.contentWindow);
              return;
            }
            this.chatboxForURL.delete(aURL);
          }
          cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
          if (aMode == "minimized")
            cb.setAttribute("minimized", "true");
          this.selectedChat = cb;
          this.insertBefore(cb, this.firstChild);
          cb.init(aProvider, aURL, aCallback);
          this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
        ]]></body>
      </method>

    </implementation>
    <handlers>
      <handler event="popupshown"><![CDATA[
        this.nub.removeAttribute("activity");
      ]]></handler>
      <handler event="overflow"><![CDATA[
        // make sure we're not getting an overflow from content
        if (event.originalTarget != this.innerbox)
          return;

        let hasHidden = this.firstCollapsedChild;
        let child = this.firstRemovableChild;
        if (child)
          this.collapseChat(child);
        if (!hasHidden) {
          window.addEventListener("resize", this);
        }
      ]]></handler>
    </handlers>
  </binding>

</bindings>
