/* ********************************************************************
 * Evertz Single Frame Modular Layout Tools - Web Application
 *
 * Build a frame right before your eyes, using this tool
 *
 */

/* ***************************************************************************************************************************
 * Controlling application object
 *
 */
function Application() {
  var self = this;

  this.clipboard = [];

  this.fpp = 1;
  this.pages = 1;
  this.page = 0;
  this.flimit = 1;
  this.working = 0;
  this.view = "edit";
  this.md5 = "";
  this.online = false;
  this.mirror = false;
  this.cursor = Math.floor(page.slots / 2);
  this.wname = "layout";
  this.type = "Single";

  this.keydown = false;
  this.keydelay = false;
  this.keyrepeat = false;

  this.dragging = false;

  this.fopts = [];
  this.selkirk = 0;
  this.seltime = 0;
  this.seltip = 0;

  this.infoips = { address: "0.0.0.0", subnetmask: "255.255.255.0", gateway: "0.0.0.0" };
  this.infofields = ["author", "company", "country", "project", "contact", "agent"];
  this.infomultis = ["ponumber"];


  /* ******************************************************************
   * Fill frame with currently selected module
   *
   */
  this.fill = function() {
    if (frame[0].slot[this.cursor].name != "Empty Slot") {
      for (var i = 0; i < frame[0].slots; i++) {
        if (i != this.cursor && frame[0].slot[i].name == "Empty Slot") {
          if (frame[0].place(i, modHash[frame[0].slot[this.cursor].name], true)) {
            frame[0].slot[i].subTransfer(frame[0].slot[this.cursor]);
            if (i == 0 && frame[0].fc.length && !frame[0].fc.find(frame[0].slot[this.cursor].name)) setTimeout(function() { self.fcswitch(); }, 10);
            i += frame[0].slot[this.cursor].slots - 1;
          }
        }
      } frame[0].refresh();
    }
  };


  /* ******************************************************************
   * Controls Cut/Copy/Paste/Delete commands
   *
   */
  this.clip = function(action) {
    this.hideDialogs();

    if (action == "copy" || action == "cut") {
      if (frame[0].slot[this.cursor].name == "Empty Slot") return false;

      this.clipboard = cloneObject(frame[0].slot[this.cursor]);

      document.getElementById('scPaste').className = "";
      document.getElementById("clipboard").innerHTML = this.clipboard.fullName(0);
      document.getElementById('buttonPaste').className = "button";

    } else if (action == "paste") {
      if (frame[0].place(this.cursor, modHash[this.clipboard.name], false)) {
        frame[0].slot[this.cursor].subTransfer(this.clipboard);
        frame[0].refresh();
      }
      return frame[0].slot[this.cursor].slots;
    }

    if (action == "cut" || action == "delete") {
      frame[0].place(this.cursor, 0, false);
      frame[0].refresh();
    }
  };


  /* ******************************************************************
   * Convenience method to close all open dialogue windows
   *
   */
  this.hideDialogs = function() {
    for (var item in Pop) Pop[item].hide();
    document.getElementById('slotContext').style.visibility = "";
    document.getElementById('selectorContext').style.visibility = "";
    var manual = document.getElementById('manual');
    if (manual && manual.open) manual.animate(false, 10);
  };


  /* ******************************************************************
   * Raise or lower the module selection dialogue box
   *
   */
  this.selectorDisplay = function(display) {
    if (this.dragging) return false;
    var preview = document.getElementById('preview');
    var modfilt = document.getElementsByName("module_filter")[0];
    var slottype = (frame[this.working].image.length) ? frame[this.working].image[this.cursor].ptype : frame[this.working].type;

    if (display) {
      this.hideDialogs();
      modfilt.value = "";
      this.selectorFilter();
      Pop.selector.show();

      var nam = frame[this.working].slot[this.cursor].name;
      if (nam != "Empty Slot") {
        for (var x = 0; x < this.fopts.length; x++)
          if (this.fopts[x].firstChild.nodeValue == nam)
            { this.selkirk = x; break; }
        this.fopts[this.selkirk].className += " chosen";
        preview.src = modules[modHash[this.fopts[this.selkirk].firstChild.nodeValue]].fileName(slottype);
        var sellist = Pop.selector.element.getElementsByTagName('ul')[0];
        sellist.scrollTop = Math.max(0, this.selkirk * 16 - sellist.offsetHeight / 2 + 8);
      } else preview.src = modules[0].fileName(slottype);
      modfilt.focus();
    } else {
      Pop.selector.hide();
      modfilt.blur();
    }
  };


  /* ******************************************************************
   * Filter and display the list of available modules
   *
   */
  this.selectorFilter = function() {
    var modfilt = document.getElementsByName("module_filter")[0];
        modfilt.value = modfilt.value.toUpperCase();
    var pickitem = Pop.selector.element.getElementsByTagName('ul')[0].getElementsByTagName('li');
    var slottype = (frame[this.working].image.length) ? frame[this.working].image[this.cursor].ptype : frame[this.working].type;

    this.fopts = [];
    for (var x = 0; x < pickitem.length; x++) {
      pickitem[x].className = "";
      if ((!modfilt.value || pickitem[x].uname.indexOf(modfilt.value) != -1) && modules[modHash[pickitem[x].firstChild.nodeValue]].allowed(slottype, frame[this.working].frame, this.cursor)) {
        pickitem[x].style.display = "";
        this.fopts.push(pickitem[x]);
      } else pickitem[x].style.display = "none";
    } this.selkirk = -1;
    document.getElementById('preview').src = modules[0].fileName(slottype);
  };


  /* ******************************************************************
   * Raise or lower the module options selection dialogue box
   *
   */
  this.optionsDisplay = function(display) {
    if (this.dragging) return false;
    var cSlot = frame[this.working].slot[this.cursor];

    if (display) {
      this.hideDialogs();
      var optionsUL = Pop.options.element.getElementsByTagName("ul")[0], ti = 21;
      while (optionsUL.firstChild) optionsUL.removeChild(optionsUL.firstChild);

      if (cSlot.name.indexOf("xxx") > 0) {
        var li = document.createElement('li');
            li.className = "wdm";
          var h3 = document.createElement('h3');
              h3.appendChild(document.createTextNode("DWDM Wavelength"));
            li.appendChild(h3);
          var select = document.createElement('select');
              select.size = "1";
              select.name = "dwdm";
              select.tabIndex = ti++;
            for (var x = 200; x <= 600; x += 10) {
              var option = document.createElement('option');
                  option.value = x;
                if (cSlot.dwdm == x)
                    option.setAttribute("selected", "selected");
                  option.appendChild(document.createTextNode(x));
                select.appendChild(option);
            } li.appendChild(select);
           cancelInputBubble(select);
         optionsUL.appendChild(li);

      } else if (cSlot.name.indexOf("xx") > 0) {
        var li = document.createElement('li');
            li.className = "wdm";
          var h3 = document.createElement('h3');
              h3.appendChild(document.createTextNode("CWDM Wavelength"));
            li.appendChild(h3);
          var select = document.createElement('select');
              select.size = "1";
              select.name = "cwdm";
              select.tabIndex = ti++;
            for (var x = 27; x <= 61; x += 2) {
              /* HACK for 7707VT-8-HS+Cxx & 7708GT-4+Cxx wavelength */
              if ((cSlot.name.indexOf("7707VT-8-HS+") < 0 && cSlot.name.indexOf("7708GT-4+") < 0) || x >= 47) { /* */
                if (x != 39 && x != 41) {
                  var option = document.createElement('option');
                      option.value = x;
                    if (cSlot.cwdm == x) option.setAttribute("selected", "selected");
                      option.appendChild(document.createTextNode(x));
                    select.appendChild(option);
                }
              } /* */
            } li.appendChild(select);
           cancelInputBubble(select);
          optionsUL.appendChild(li);
      }

      if (cSlot.fibers) {
        var li = document.createElement('li');
          var h3 = document.createElement('h3');
              h3.appendChild(document.createTextNode("Fiber Connector"));
            li.appendChild(h3);
          var ul = document.createElement('ul');
            for (var fbr in cSlot.fiber) {
              if (typeof cSlot.fiber[fbr] == "boolean") {
                var li2 = document.createElement('li');
                  var label = document.createElement('label');
                    try {
                      var input = document.createElement('<input type="radio" name="fibers" value="' + fbr + '" tabindex="' + ti + '"' + ((cSlot.fiber[fbr]) ? ' checked="checked"' : '') + ' />');
                    } catch(e) {
                      var input = document.createElement('input');
                          input.type = "radio";
                          input.name = "fibers";
                        if (cSlot.fiber[fbr]) input.checked = "checked";
                          input.value = fbr;
                          input.tabIndex = ti;
                    } label.appendChild(input);
                    cancelInputBubble(input);
                      label.appendChild(document.createTextNode(" +" + fbr));
                    li2.appendChild(label);
                  ul.appendChild(li2);
              }
            } li.appendChild(ul);
          optionsUL.appendChild(li);
      }

      for (var opt in cSlot.option) {
        if (typeof cSlot.option[opt] == "boolean") {
          var li = document.createElement('li');
            var description = optionDescriptions[opt] || "\u2014";
            var label = document.createElement('label');
              try {
                var input = document.createElement('<input type="checkbox" value="' + opt + '" tabindex="' + ti + '"' + ((cSlot.option[opt]) ? ' checked="checked"' : '') + ' />');
              } catch(e) {
                var input = document.createElement('input');
                    input.type = "checkbox";
                  if (cSlot.option[opt]) input.checked = "checked";
                    input.value = opt;
                    input.tabIndex = ti;
              } label.appendChild(input);
              cancelInputBubble(input);
                label.appendChild(document.createTextNode(" +" + opt + ": "));
              if (description) {
                var small = document.createElement('small');
                    small.appendChild(document.createTextNode(description));
                  label.appendChild(small);
              }
              li.appendChild(label);
            optionsUL.appendChild(li);
        } else if (cSlot.option[opt] instanceof Array) {
          var li = document.createElement('li');
            var ul = document.createElement('ul');
                ul.className = "group";
              for (var subopt in cSlot.option[opt]) {
                if (typeof cSlot.option[opt][subopt] == "boolean") {
                  var description = optionDescriptions[subopt] || "\u2014";
                  var li2 = document.createElement('li');
                    var label = document.createElement('label');
                      try {
                        var input = document.createElement('<input type="radio" name="' + opt + '" value="' + subopt + '" tabindex="' + ti + '"' + ((cSlot.option[opt][subopt]) ? ' checked="checked"' : '') + ' />');
                      } catch(e) {
                        var input = document.createElement('input');
                            input.type = "radio";
                            input.name = opt;
                          if (cSlot.option[opt][subopt]) input.checked = "checked";
                            input.value = subopt;
                            input.tabIndex = ti;
                      } label.appendChild(input);
                      cancelInputBubble(input);
                        label.appendChild(document.createTextNode(" " + ((subopt) ? "+" + subopt : "\u2014") + ": "));
                      if (description) {
                        var small = document.createElement('small');
                           small.appendChild(document.createTextNode(description));
                          label.appendChild(small);
                      }
                      li2.appendChild(label);
                    ul.appendChild(li2);
                }
              }
            li.appendChild(ul);
          optionsUL.appendChild(li);
        }
      }

      Pop.options.element.getElementsByTagName('button')[0].tabIndex = ti;
      Pop.options.show();
      Pop.options.element.getElementsByTagName('form')[0].elements[0].focus();

    } else {
      if (Pop.options.element.style.display == "block") {
        Pop.options.hide();

        var inputs = Pop.options.element.getElementsByTagName('input');
        for (var x = 0; x < inputs.length; x++) {
          if (inputs[x].name == "fibers") {
            if (typeof cSlot.fiber[inputs[x].value] == "boolean")
              cSlot.fiber[inputs[x].value] = (inputs[x].checked);
          } else if (inputs[x].name.indexOf("group") === 0) {
            if (cSlot.option[inputs[x].name] && typeof cSlot.option[inputs[x].name][inputs[x].value] == "boolean")
              cSlot.option[inputs[x].name][inputs[x].value] = (inputs[x].checked);
          } else
            if (typeof cSlot.option[inputs[x].value] == "boolean")
              cSlot.option[inputs[x].value] = (inputs[x].checked);
        }

        var wavelength = Pop.options.element.getElementsByTagName('select');
        if (wavelength.length) {
          wavelength = wavelength[0];
          if (wavelength.name == "dwdm") cSlot.dwdm = wavelength.value;
          if (wavelength.name == "cwdm") cSlot.cwdm = wavelength.value;
        }

        if (this.recentCommand) this.recentCommand(true);
        frame[this.working].refresh();
      }
    }
  };


  /* ******************************************************************
   * Raise or lower the module label dialogue box
   *
   */
  this.labelDisplay = function(display) {
    var input = Pop.label.element.getElementsByTagName('input')[0];

    if (display) {
      this.hideDialogs();
      Pop.label.show();
      input.value = frame[0].slot[this.cursor].label;
      input.focus();
    } else {
      if (Pop.label.element.style.display == "block") {
        frame[0].slot[this.cursor].label = input.value.substr(0, 32);
        Pop.label.hide();
        frame[0].refresh();
      }
    }
  };


  /* ******************************************************************
   * Switch Control Panels
   *
   */
  this.switchControl = function(tab) {
    if (this.mirror) return alert("This function is only available in the online version @ http:\x2f\x2fwww.evertz.com\x2fframe\x2f" + page.frame);
    if (this.dragging) return false;
    this.hideDialogs();

    if (this.view != tab) {
      if (tab != "edit") {
        if (this.view == "edit") {
          this.quoteMessage("Working...", "", "");
          setTimeout(function() { self.sendOutput(true); }, 5);
        } document.getElementById('tabs_' + tab).className = "selected";
      } else document.getElementById('frameimage').style.visibility = "";

      if (this.view != "edit") document.getElementById('tabs_' + this.view).className = "";
      document.getElementById('controls_' + this.view).style.display = "none";
      document.getElementById('controls_' + tab).style.display = "block";

      for (var x = 0, pbox, ibox, psub, ppow, pfid; x < this.infofields.length; x++) {
        ibox = document.getElementsByName('info_' + this.infofields[x])[0];
				pbox = document.getElementById('print_' + this.infofields[x]);
        if (pbox && ibox) pbox.firstChild.nodeValue = (this.infofields[x] == "agent") ? ibox.options[ibox.selectedIndex].text : ibox.value + "\xa0";
      }

      if (pfid = document.getElementById('print_frameid'))
        pfid.firstChild.nodeValue = frame[0].frameid;

      if (psub = document.getElementsByTagName('h2')[0])
        psub.firstChild.nodeValue = document.getElementsByName('info_project')[0].value;

      if (ppow = document.getElementById('print_power'))
        ppow.firstChild.nodeValue = document.getElementById('display').firstChild.nodeValue + " / " + frame[0].power;

      var today = new Date();
      var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
      document.getElementById('print_info').getElementsByTagName('var')[0].firstChild.nodeValue = months[today.getMonth()] + " " + today.getDate() + ", " + today.getFullYear();

      this.view = tab;
    }
  };


  /* ******************************************************************
   * Handle global keyboard input
   * - Return false if key should not repeat if held down
   *
   */
  this.keyCheck = function(keyCode, ctrlKey, shiftKey) {
    if (this.locked) return false;
    if (this.view != "edit") this.switchControl('edit');

    switch (keyCode) {
      case 67: // ******************** CTRL+C
        if (ctrlKey) this.clip('copy');
        return false;

      case 86: // ******************** CTRL+V
        if (ctrlKey) this.clip('paste');
        return false;

      case 88: // ******************** CTRL+X
        if (ctrlKey) this.clip('cut');
        return false;

      case 46: // ******************** Delete
        this.clip('delete');
        return false;

      case 38: // ******************** Up
      case 39: // ******************** Right
        frame[0].highlight(Math.max(0, --this.cursor), true);
        break;

      case 40: // ******************** Down
      case 37: // ******************** Left
        frame[0].highlight(Math.min(frame[0].slots - 1, this.cursor + frame[0].slot[this.cursor].slots), true);
        break;

      case 13: // ******************** Enter
        if (Pop.selector.element.style.display != "block" &&
            Pop.options.element.style.display != "block") {
          this.selectorDisplay(true);
        } return false;

      case 27: // ******************** ESC
        this.hideDialogs();
        return false;

      default: return false;
    } return true;
  };


  /* ******************************************************************
   * Gather form information and send via AJAX
   *
   */
  this.sendOutput = function(preview) {
    if (this.mirror) return alert("This function is only available in the online version @ http:\x2f\x2fwww.evertz.com\x2fframe\x2f" + page.frame);
    if (preview && !frame[this.working].isFull()) return alert("Frame is empty");

    var link = document.getElementById('link_send');
    link.disabled = "disabled";
    link.onclick = null;
    Pop.waiter.show();

    for (var x = 0, values = ["frame=" + page.frame]; x < this.infofields.length; x++)
      if (document.getElementsByName('info_' + this.infofields[x])[0])
        values.push("info_" + this.infofields[x] + "="  + encodeURIComponent(document.getElementsByName('info_' + this.infofields[x])[0].value));

    for (var x = 0; x < this.infomultis.length; x++) {
      for (var y = 0, val = [], mult = document.getElementsByName('info_' + this.infomultis[x]); y < mult.length; y++)
        if (mult[y].value) val.push(mult[y].value);
      values.push("info_" + this.infomultis[x] + "="  + encodeURIComponent(val.join(", ")));
    }

    for (var x = 0, pfx; x < frame.length; x++) {
      if (frame[x].isFull()) {
        pfx = (this.type == "Bulk") ? "f" + x : "";
        values.push(pfx + "id=" + encodeURIComponent(frame[x].frameid));
        values.push(pfx + "rps=" + ((frame[x].hasrps) ? "Yes" : "No"));
        values.push(pfx + "rfc=" + ((frame[x].hasrfc) ? "Yes" : "No"));
        if (this.type == "Bulk") {
          values.push(pfx + "frame=" + encodeURIComponent(frame[x].frame));
          for (var prop in this.infoips)
            values.push(pfx + "ip_" + prop + "=" + frame[x].ip[prop]);
        }

        for (var y = 0; y < frame[x].slots; y++) {
          if (!frame[x].slot[y].multi && frame[x].slot[y].name != "Empty Slot") {
            values.push(pfx + "module" + y + "=" + encodeURIComponent(frame[x].slot[y].fullName(2)));
            values.push(pfx + "image" + y + "=" + encodeURIComponent(frame[x].slot[y].fileName(0, frame[x].rotate)));
          }
        }
      }
    }

    var http = getHTTPObject();
    http.open("POST", "store", true);
    http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    http.onreadystatechange = function() {
      if (http.readyState == 4) {
        self.md5 = http.responseXML.getElementsByTagName('md5');
        var err = http.responseXML.getElementsByTagName('error');

        if (self.md5.length) {
          var sent = http.responseXML.getElementsByTagName('sent')[0].firstChild.nodeValue;
          self.md5 = self.md5[0].firstChild.nodeValue;

          if (document.getElementById('tabs_quote')) {
            if (sent == "no") {
              for (var x = 0, full = 0; x < frame.length; x++) if (frame[x].isFull()) full++;
              if (full) {
                if (document.getElementsByName('info_agent')[0].value != "" &&
                    document.getElementsByName('info_contact')[0].value) {
                  link.disabled = "";
                  link.onclick = function() { self.getQuote(); };

                  self.quoteMessage("Ready to Send", "Click the \"Send\" button to send this " + self.wname + " to " + document.getElementsByName('info_agent')[0].options[document.getElementsByName('info_agent')[0].selectedIndex].text + ".", "");
                } else self.quoteMessage("Incomplete", "To send your " + self.wname + ", please select a Sales Agent and include your Contact Information.", "");
              } else self.quoteMessage("Nothing to Send", "Please select your desired modules before sending a " + self.wname + ".", "");
            } else self.quoteMessage("Already Sent", "This " + self.wname + " has already been sent. To send multiple, similar " + self.wname + "s, use different Project Names for each.", "");
          }

          if (preview) {

            // Firefox hack to force reset of image.complete
            var img = document.createElement('img');
            img.src = "frame?frame=" + page.frame + "&id=" + self.md5 + ((self.type == "Bulk") ? "&offset=" + self.working + "&width=920" : "");
            img.alt = "Completed " + frame[self.working].name;
            document.getElementById('frameimage').parentNode.replaceChild(img, document.getElementById('frameimage'));
            img.id = "frameimage";

            img.wait = setInterval(function() { (function() {
              if (this.complete && (!this.readyState || this.readyState == "complete")) {
                Pop.waiter.hide();
                if (Pop.framePreview) {
                  Pop.framePreview.element.getElementsByTagName('span')[0].firstChild.nodeValue = frame[self.working].frameid;
                  Pop.framePreview.show();
                } else {
                  this.onclick = function() { self.switchControl('edit'); };
                  this.style.visibility = "visible";
                } clearInterval(this.wait);
              }
            }).call(img) }, 200);
          } else Pop.waiter.hide();

        } else if (err.length) {
          self.quoteMessage("ID Error", "There was an error finding the " + self.wname + " ID.  Please try again.", "");
          alert(err[0].firstChild.nodeValue);
          Pop.waiter.hide();
        }
      }
    };
    http.send(values.join("&"));
    return true;
  };


  /* ******************************************************************
   * Change the Get Quote interface text
   *
   */
  this.quoteMessage = function(title, message, className) {
    var inter = document.getElementById('interface');
    while (inter.firstChild) inter.removeChild(inter.firstChild);
        inter.className = className;
      var h2 = document.createElement('h2');
          h2.appendChild(document.createTextNode(title));
        inter.appendChild(h2);
      var p = document.createElement('p');
          p.appendChild(document.createTextNode(message));
        inter.appendChild(p);
  };


  /* ******************************************************************
   * Send this frame ID via AJAX to get a quote
   *
   */
  this.getQuote = function() {
    document.getElementById('link_send').disabled = "disabled";
    Pop.waiter.show();

    var http = getHTTPObject();
    http.open("GET", "/includes/ajax/framesend?type=" + this.type + "&id=" + this.md5, true);
    http.onreadystatechange = function() {
      if (http.readyState == 4) {
        switch (http.responseText) {
          case "Success": self.quoteMessage("Thank you!", "This " + page.name + " " + self.wname + " has been sent to your selected sales agent.", "complete"); break;
          case "Invalid": self.quoteMessage("Error!", "There was a problem in transferring the data to be mailed. Please go back to the editing page and try again.", "error"); break;
          case "AlreadySent": self.quoteMessage("Already Sent", "This " + self.wname + " and the associated information has already been mailed to your selected sales agent.", "error"); break;
          case "NotFound": self.quoteMessage("I'm sorry!", "I could not find any completed " + self.wname + "s in the database which match yours; it may have expired.", "error"); break;
          case "Fail": default: self.quoteMessage("Error!", "I'm sorry, but it seems the message could not be sent. Please go back to the editing page and try again.", "error"); break;
        }
        Pop.waiter.hide();
      }
    };
    http.send(null);
  };


  /* ******************************************************************
   * Bookmark this configuration
   *
   */
  this.getBookmark = function() {
    var http = getHTTPObject();
    http.open("GET", "/frame/saverecall?type=" + page.type + "&method=bookmark&id=" + this.md5, true);
    http.onreadystatechange = function() {
      if (http.readyState == 4) {
        if (http.responseText == "Saved") {
          Taint = false;
          var savedlink = Pop.savedNotice.element.getElementsByTagName('a')[0];
              savedlink.href = savedlink.getElementsByTagName('span')[0].firstChild.nodeValue = page.frame + "?id=" + self.md5;
            Pop.savedNotice.show();
        } else alert(http.responseText);
      }
    };
    http.send(null);
    return false;
  };


  /* ******************************************************************
   * Find the block diagram link of a parent product
   *
   */
  this.findBlock = function(parentProd) {
    var http = getHTTPObject();
    http.open("HEAD", "/products/block/" + parentProd.replace(/\//g, ".") + ".pdf?" + (new Date()).getTime(), true);
    http.onreadystatechange = function() {
      if (http.readyState == 4) {
        var scbd = document.getElementById('scBlockDiagram');
        var slcbd = document.getElementById('slcBlockDiagram');
        scbd.className = slcbd.className = (http.status == 200) ? "" : "disabled";
        scbd.getElementsByTagName('a')[0].href = slcbd.getElementsByTagName('a')[0].href = "../products/block/" + parentProd.replace(/\//g, ".") + ".pdf";
      }
    };
    http.send(null);
  };


  /* ******************************************************************
   * Request data export from the server
   *
   */
  this.exportData = function(type) {
    if (this.dragging) return false;

    var remTaint = Taint;

    if (type == "dxf") {
      Pop.waiter.show();
      setTimeout(function() { Pop.waiter.hide(); }, 7000);
    }
    Taint = false;
    window.location.href = "frame?frame=" + frame[0].frame + "&method=" + type + "&id=" + this.md5;
    setTimeout((function(remTaint) { return function() { Taint = remTaint; }})(remTaint), 5);
    return false;
  };
}



/* ***************************************************************************************************************************
 * Frame object
 *
 */
function Frame(id, ftype) {
  var self = this;

  this.id        = id;
  this.frameid   = (id + 1) + "";
  this.ip        = cloneObject(app.infoips);
  this.frame     = "";
  this.type      = "";
  this.loose     = false;
  this.ps        = "";
  this.rps       = "";
  this.hasrps    = false;
  this.fc        = [];
  this.rfc       = false;
  this.hasrfc    = false;
  this.split     = [];
  this.reverse   = false;
  this.slots     = 0;
  this.slot      = [];
  this.newmodule = false;

  this.labelMode = false;
  this.image     = [];


  /* ******************************************************************
   * Set properties associated with a certain frame type
   *
   */
  this.setType = function(ftype) {
    var retyped = false;
    if (ftype == "Bulk") ftype = "7800FR";
    for (var x = 0; x < enclosures.length; x++) {
      if (ftype == enclosures[x].frame || ftype == enclosures[x].type) {
        if (this.type != enclosures[x].type) retyped = true;
        for (var prop in enclosures[x]) this[prop] = cloneObject(enclosures[x][prop]);
      }
    }
    if (retyped) {
      this.slot = [];
      for (var i = 0; i < this.slots; i++) this.slot[i] = cloneObject(modules[0]);
    } else if (this.slots > this.slot.length) {
      for (var i = this.slot.length; i < this.slots; i++) this.slot[i] = cloneObject(modules[0]);
    } else if (this.slots < this.slot.length) {
      var cutoff = this.slots;
      while (this.slot[cutoff].multi) cutoff--;
      this.place(cutoff, 0, true);
      for (var i = this.slot.length - 1; i >= this.slots; i--) this.slot.pop();
    }
  };
  this.setType(ftype);


  /* ******************************************************************
   * Highlight a selected module
   *
   */
  this.highlight = function(slot, wipe) {
    if (app.dragging) return false;
    if (app.view != "edit") app.switchControl('edit');

    app.working = this.id;
    if (wipe) app.multimove = 0;
    slot = Math.min(this.slots - 1, slot);
    while (this.slot[slot].multi) slot--;

    if (slot != app.cursor) {
      app.hideDialogs();
      app.cursor = slot;
    }

    for (var i = 0, sel; i < this.slots; i++) {
      sel = (i >= slot && i < slot + this.slot[slot].slots);
      document.getElementById("label" + i).className = (sel) ? "selected" : "";
      document.getElementById("status_no" + i).className = (sel) ? "selected" : "";
    }

    document.getElementById("module").firstChild.nodeValue = this.slot[app.cursor].name;
    var fcClass = (this.slot[app.cursor].name == "Empty Slot") ? "button inactive" : "button";
    document.getElementById('buttonFill').className = document.getElementById('buttonCopy').className = fcClass;
    document.getElementById('buttonClearAll').className = (this.isFull() & 4) ? "button" : "button inactive";

    if (this.newmodule && this.slot[app.cursor].anyopt) {
      app.optionsDisplay(true);
    }

    if (document.activeElement && document.activeElement != document.body) document.activeElement.blur();
    this.newmodule = false;
    return app.cursor;
  };


  /* ******************************************************************
   * Refresh the entire frame
   *
   */
  this.refresh = function() {
    var frameid = document.getElementsByName("frameid")[0];
    if (frameid) frameid.value = this.frameid;

    for (var i = 0, power = 10, modu; i < this.slots; i++) {
      this.slot[i].multi = false;
      var title = this.slot[i].descrip;

      if (this.slot[i].name == "Future-Use") this.slot[i].flags.NO = true;

      for (var j = 0, k, className = ""; k = extras[j++];) {
        if (this.slot[i].flags[k.code]) {
          className += " " + k.className;
          if (this.slot[i].name != "Future-Use") title += "; " + k.title;
        }
      }

      modu = document.getElementById("module" + i);
      modu.style.height = ((this.rotate) ? this.slot[i].slots * this.panel[1] : this.panel[1]) + "px";
      modu.style.width = ((this.rotate) ? this.panel[0] : this.slot[i].slots * this.panel[0]) + "px";
      var offset = this.slots - i - this.slot[i].slots;
      switch (this.frame) {
        case "3700FR":
          if (i < 16) {
            modu.ptype = "7700";
            if (i < 8) {
              modu.style.top = "329px";
              modu.style.left = (offset - 14) * 50 + "px";
            } else modu.style.left = (offset - 6) * 50 + "px";
          } else {
            modu.style.height = "645px";
            modu.style.left = (offset + 9) * 50 + "px";
          }
          break;
        case "7801FR":
          modu.style.top = ((i < 2) ? 1 : -99) + 50 * i + "px";
          modu.style.left = (i < 2) ? "417px" : "1px";
          break;
        case "EMX3-FR":
          modu.style.top = 18 + 50 * offset + "px";
          modu.style.left = "105px";
          break;
        default:
          modu.style.left = (this.panel[0] - this.overlap) * offset + "px";
      } modu.src = this.slot[i].fileName(modu.ptype, this.rotate);
      modu.title = (title != "Empty Slot") ? title + ((this.slot[i].label) ? " (" + this.slot[i].label + ")" : "") : "";
      modu.style.display = "block";
      modu.className = className;

      var stat = document.getElementById("status_entry" + i);
      stat.className = className;
      stat.firstChild.innerHTML = this.slot[i].fullName(0);
      power += modules[modHash[this.slot[i].name]].power;

      for (var j = i + 1, modj; j < i + this.slot[i].slots; j++) {
        this.slot[j].multi = true;
        modj = document.getElementById("module" + j);
        modj.style.display = "";
        modj.src = modules[0].fileName(modj.ptype, this.rotate);
        modj.title = (title != "Empty Slot") ? title : "";
        document.getElementById("status_entry" + j).firstChild.innerHTML = " &nbsp; &ndash;";
      } i = j - 1;
    }
    if (this.power) {
      if (this.rfc && frame[0].hasrfc) power += 10;
      if (power <= (this.power * 0.8)) document.getElementById('value').style.backgroundColor = "#00c800";
      if (power > (this.power * 0.8) && power <= this.power) document.getElementById('value').style.backgroundColor = "#ffd700";
      if (power > this.power) document.getElementById('value').style.backgroundColor = "#ff0000";
      document.getElementById('value').style.width = Math.ceil(power / this.power * 150) + "px";
      document.getElementById('display').firstChild.nodeValue = power;
    }
    if (this.id == app.working) this.highlight(app.cursor, true);
  };


  /* ******************************************************************
   * Place a module into the frame
   *
   */
  this.place = function(slot, modNum, silent) {
    app.hideDialogs();
    var slottype = (this.image.length) ? this.image[slot].ptype : this.type;
    if (!modules[modNum].allowed(slottype, this.frame, slot) || this.slot[slot].multi) return false;

    for (var i = slot + 1; i < slot + modules[modNum].slots; i++) {
      if (i >= this.slots || this.split.find(i) || this.slot[i].name != "Empty Slot") {
        if (slot == 0 || this.split.find(slot) || this.slot[slot - 1].multi || this.slot[slot - 1].name != "Empty Slot") {
          if (!silent) alert("There is not enough room to place this module here.\nThis module requires " + modules[modNum].slots + " available slots.");
          this.newmodule = false;
          return false;
        } else i = --slot + 1;
      }
    }

    // Remove existing module
    for (var i = slot, end = this.slot[slot].slots; i < slot + end; i++)
      this.slot[i] = cloneObject(modules[0]);

    if (modNum) {
      // Paste new module
      this.slot[slot] = cloneObject(modules[modNum]);
      for (var x = slot + 1; x < slot + this.slot[slot].slots; x++) this.slot[x].multi = true;
      if (this.slot[slot].name == "Future-Use") this.slot[slot].flags.NO = true;

      if (!silent) {
        app.cursor = slot;
        this.refresh();
        if (slot == 0 && modules[modNum].name != "Empty Slot" && this.fc.length && !this.fc.find(modules[modNum].name))
          setTimeout(function() { self.fcswitch(); }, 10);
      }
    } else if (!silent) this.refresh();
    return Taint = true;
  };


  /* ******************************************************************
   * Warn user about having a Frame Controller in slot #1
   *
   */
  this.fcswitch = function() {
    if (confirm("To monitor and/or control this frame using VistaLINK PRO or a 9000NCP Control Panel, slot one must contain a Frame Controller module.\n\nReplace the current module in slot one with a " + this.fc[0] + "?")) {
      this.place(0, modHash[this.fc[0]], false);
      this.highlight(0, true);
    }
  };


  /* ******************************************************************
   * Check whether this frame is full
   *  - Returns a binary value telling exactly what has been set
   *    - 001 => Has a modified Frame ID
   *    - 010 => Has a Redundant PS or Redundant FC set
   *    - 100 => Has at least one module
   *
   */
  this.isFull = function() {
    for (var i = 0, full = 0; i < this.slots; i++) if (this.slot[i].name != "Empty Slot") { full += 4; break; }
    if (this.hasrps || this.hasrfc) full += 2;
    if (this.frameid.length && this.frameid != (this.id + 1).toString()) full += 1;
    return full;
  };


  /* ******************************************************************
   * Clear all modules from the frame
   *
   */
  this.clear = function() {
    if (app.type == "Bulk") app.exitLabelMode();
    if (this.isFull() && confirm('Really clear this frame?')) {
      for (var i = 0; i < this.slots; i++) this.slot[i] = cloneObject(modules[0]);
      this.setrps(false);
      this.frameid = (this.id + 1) + "";
      this.ip = cloneObject(app.infoips);
      this.refresh();
    }
  };


  /* ******************************************************************
   * Toggle redundant power supply
   *
   */
  this.setrps = function(chk) {
    if (app.view != "edit") app.switchControl('edit');
    app.hideDialogs();

    if (this.rps) {
      Taint = true;

      document.getElementById('ps2').src = (chk) ? "rear/" + this.type + "/frame/" + this.ps + this.rps + ".png" : "rear/" + this.type + "/frame/" + this.ps + ".png";
      document.getElementById('rpstog').src = (chk) ? "img/check-on.png" : "img/check-off.png";
      document.getElementsByName('option_rps')[0].value = (this.hasrps = chk) ? "Yes" : "No";
    }
  };


  /* ******************************************************************
   * Toggle redundant frame controller
   *
   */
  this.setrfc = function(chk) {
    if (app.view != "edit") app.switchControl('edit');
    app.hideDialogs();

    if (this.rfc) {
      document.getElementById('rfctog').src = (chk) ? "img/check-on.png" : "img/check-off.png";
      document.getElementsByName('option_rfc')[0].value = (this.hasrfc = chk) ? "Yes" : "No";
      if (this.power) this.refresh();
    }
  };
}










/* ***************************************************************************************************************************
 * Handles drag'n of the Panel Images
 *
 */
function Dragon() {
  var self = this;

  this.obj = null;
  this.target = 0;
  this.targets = [];
  this.mPos = [0, 0];
  this.pPos = [0, 0];

  this.updateAdjustment = function() {
    self.pPos = findPos(document.getElementById('frame'));
  };

  if (window.addEventListener) {
    window.addEventListener("resize", this.updateAdjustment, false);
  } else if (window.attachEvent) window.attachEvent("onresize", this.updateAdjustment);

  this.drag = function(e, x) {
    if (app.dragging) return false;
    document.documentElement.onmousemove = null;
    document.documentElement.onmouseup = null;

    e = e || event;
    this.obj = e.target || e.srcElement;
    frame[0].highlight(x, true);

    if (frame[0].slot[app.cursor].name != "Empty Slot") {
      this.origpos = [this.obj.style.left, this.obj.style.top];
      this.mPos[0] = e.clientX - this.obj.parentNode.parentNode.offsetLeft - this.obj.offsetLeft;
      this.mPos[1] = e.clientY - this.obj.parentNode.parentNode.offsetTop - this.obj.offsetTop;
      document.documentElement.onmousemove = function(e) {
        e = e || event;
        if (self.obj) {
          if (!app.dragging) {
            app.hideDialogs();
            self.obj.className += " dragon";
          } app.dragging = true;
          self.obj.style.left = Math.min(page.drag[0] + 20 - self.obj.offsetWidth , Math.max(-25, e.clientX - self.obj.parentNode.parentNode.offsetLeft - self.mPos[0])) + "px";
          self.obj.style.top  = Math.min(page.drag[1] + 20 - self.obj.offsetHeight, Math.max( -5, e.clientY - self.obj.parentNode.parentNode.offsetTop  - self.mPos[1])) + "px";
          e.cancelBubble = true;
        }
      };
      document.documentElement.onmouseup = function(e) {
        if (app.dragging) {
          e = e || event;
          app.dragging = false;
          var targetPos = findPos(self.obj)
          self.obj.style.left = self.origpos[0];
          self.obj.style.top = self.origpos[1];
          self.target = -1;

          // Find the dropzone and compare notes
          for (var x = 0, d, dists = []; x < self.targets.length; x++)
            if ((d = Math.sqrt(Math.pow(targetPos[0] - self.targets[x][0] - self.pPos[0], 2) + Math.pow(targetPos[1] - self.targets[x][1] - self.pPos[1], 2))) < 40)
              dists.push([x, d]);
          if (dists.length) {
            dists.sort(function(a, b) { return a[1] - b[1]; });
            self.target = dists[0][0] - ((frame[0].reverse) ? 0 : frame[0].slot[app.cursor].slots - 1);
          }

          if (self.target != -1 && self.target != app.cursor && modules[modHash[frame[0].slot[app.cursor].name]].allowed(frame[0].image[self.target].ptype, page.frame, self.target)) {
            var dummy = cloneObject(frame[0].slot[app.cursor]);
            frame[0].place(app.cursor, 0, true);
            for (var x = self.target, allowed = true; x < self.target + frame[0].slot[self.target].slots; x++)
              if (frame[0].slot[x].name != "Empty Slot" || frame[0].slot[x].multi) allowed = false;

            if (allowed && frame[0].place(self.target, modHash[dummy.name], true)) {
              app.cursor = self.target;
              frame[0].slot[self.target].subTransfer(dummy);
              if (self.target == 0 && frame[0].slot[self.target].name != "Empty Slot" && page.fc.length && !page.fc.find(frame[0].slot[self.target].name))
                setTimeout(function() { frame[0].fcswitch(); }, 10);
            } else {
              frame[0].place(app.cursor, modHash[dummy.name], true);
              frame[0].slot[app.cursor].subTransfer(dummy);
            }
          } frame[0].refresh();
          self.obj.className = self.obj.className.removeWord("dragon");
        } self.obj = null;
        document.documentElement.onmousemove = null;
        document.documentElement.onmouseup = null;
      };
    } else this.obj = null;
  };
}





/* ***** Global Variables ****************************************** */
var page = enclosures[encHash[window.location.pathname.replace(/^.+[\/\\]([^\/\\]+?)(\.(html|php))?$/, "$1")]];
var app = new Application(), dragon, Pop;
for (var x = 0, frame = []; x < app.fpp; x++) frame.push(new Frame(x, page.frame))


/* ***************************************************************************************************************************
 * Prepare the document onload
 *
 */
function createOnload() {
  dragon = new Dragon();

  // ***** Hook dragon claws into the headers of popup "windows" ******
  Pop = {
    selector: new Popup('selector', true, false),
    options: new Popup('options', true, true),
    savedNotice: new Popup('savednotice', true, true),
    waiter: new Popup('waiter', false, true),
    label: new Popup('label', true, true)
  };
  Pop.waiter.show();


  // ***** Apply misc event listeners *********************************
  var buttons = document.body.getElementsByTagName('div');
  for (var x = 0; x < buttons.length; x++) {
    if (buttons[x].className.match(/button/)) {
      buttons[x].onmousedown = function(e) {
        if (this.className.indexOf("inactive") == -1)
          this.className = ((this.className.indexOf("highlight") > -1) ? "highlight " : "") + "button active";
      };
      buttons[x].onmouseup = buttons[x].onmouseout = function(e) {
        if (this.className.indexOf("inactive") == -1)
          this.className = ((this.className.indexOf("highlight") > -1) ? "highlight " : "") + "button";
      };
    }
  }
  var manualink = document.getElementById('manualink');
  if (manualink) {
    var manual = document.getElementById('manual');
    manual.animate = function(direction, step) {
      var self = this, steps = [350, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0];
      this.moving = true;
      this.style.top = -steps[step] + "px";
      if (step == 0 || step == steps.length) {
        if ((step == 0 && direction) || (step == steps.length && !direction)) app.hideDialogs();
        this.style.visibility = (direction) ? "visible" : "";
        manualink.innerHTML = (direction) ? "Hide Instructions" : "View Instructions";
      }
      if (direction) { step++; } else step--;
      if (step < 0 || step >= steps.length) {
        this.open = direction;
        this.moving = false;
      } else setTimeout(function() { self.animate(direction, step); }, 30);
    };
    manualink.onclick = function() {
      if (!manual.moving) manual.animate(!manual.open,(manual.open) ? 10 : 0);
    };
  }


  // ***** Apply frame label event listeners **************************
  var labels = document.getElementById("labels").getElementsByTagName("td");
  for (var x = labels.length - 1, y; x >= 0; x--) {
    y = (page.reverse) ? labels.length - 1 - x : x;
    labels[x].id = "label" + (frame[0].slots - y - 1);
    labels[x].index = frame[0].slots - y - 1;
    labels[x].onclick = function() { frame[0].highlight(this.index, true); };
  }

  // ***** Apply status box event listeners ***************************
  var status = document.getElementById("status").tBodies[0].rows;
  for (var x = 0, y = 0, r = 0, col2 = Math.ceil(page.slots / 2); x < page.slots && y < page.slots; x++, y++, r++) {
    if (status[r].cells[0].firstChild.nodeName == "#text") {
      status[r].cells[0].id = "status_no" + x;
      status[r].cells[1].id = "status_entry" + x;
      status[r].cells[0].index = status[r].cells[1].index = x;
      status[r].cells[0].onclick = status[r].cells[1].onclick = function() { frame[0].highlight(this.index, true); };
      status[r].cells[0].ondblclick = status[r].cells[1].ondblclick = function(e) { app.selectorDisplay(true); };
    }

    if (status[r].cells[2]) {
      var adj = (status[r].cells[2].className == "divider") ? 1 : 0;
      if (status[r].cells[2 + adj].firstChild && status[r].cells[2 + adj].firstChild.nodeName == "#text") {
        status[r].cells[2 + adj].id = "status_no" + (x + col2);
        if (!(page.slots % 2) || x + status.length < 15) {
          status[r].cells[2 + adj].index = status[r].cells[3 + adj].index = x + col2;
          status[r].cells[2 + adj].onclick = status[r].cells[3 + adj].onclick = function() { frame[0].highlight(this.index, true); };
          status[r].cells[2 + adj].ondblclick = status[r].cells[3 + adj].ondblclick = function(e) { app.selectorDisplay(true); };
        }
        status[r].cells[3 + adj].id = "status_entry" + (x + col2);
      } y++;
    }
  }


  // ***** Apply frame image event listeners **************************
  var plates = document.getElementById("plates");
      plates.style.left = -page.overlap + "px";
  frame[0].image = plates.getElementsByTagName("img");
  dragon.pPos = findPos(frame[0].image[0].parentNode);
  for (var x = frame[0].image.length - 1, pos; x >= 0; x--) {
    frame[0].image[x].id = "module" + x;
    frame[0].image[x].index = x;
    frame[0].image[x].ptype = page.type;
    frame[0].image[x].src = frame[0].slot[x].fileName(frame[0].image[x].ptype, page.rotate);
    frame[0].image[x].ondblclick = function(e) { app.selectorDisplay(true); };
    frame[0].image[x].onmousedown = function(e) { dragon.drag(e, this.index); return false; };
    frame[0].image[x].oncontextmenu = function(e) {
      (e = e || event).returnValue = false;
      return contextMenu(e, 0, this.index);
    };
    frame[0].image[x].onclick = frame[0].image[x].ondragstart = frame[0].image[x].onselectstart = function(e) { return false; };
  } frame[0].refresh();
  for (var x = frame[0].image.length - 1, pos; x >= 0; x--) {
    pos = findPos(frame[0].image[x]);
    pos = [pos[0] - dragon.pPos[0], pos[1] - dragon.pPos[1]];
    dragon.targets[x] = [pos[0], pos[1], pos[0] + frame[0].image[x].offsetWidth, pos[1] + frame[0].image[x].offsetHeight];
  }


  // ***** Position the frame image and add RPS listener **************
  var ps1, ps2, sheet = document.styleSheets[0];
  if (ps1 = document.getElementById('ps1')) {
    if (sheet.insertRule) {
      sheet.insertRule('#frame #frameimage { left:-' + ps1.width + 'px; }', sheet.cssRules.length);
    } else if (sheet.addRule) sheet.addRule('#frame #frameimage','left:-' + ps1.width + 'px;');
  }
  if (ps2 = document.getElementById('ps2')) {
    if (page.rps) {
      ps2.style.cursor = "pointer";
      ps2.onclick = function() { frame[0].setrps(!frame[0].hasrps); };
    } ps2.style.marginLeft = -page.overlap + "px";
    ps2.style.display = "block";
  }


  // ***** Handle keypress input in the label dialog ******************
  Pop.label.element.getElementsByTagName('form')[0].onsubmit = function() {
    app.labelDisplay(false);
    return false;
  };


  // ***** Enable the Frame Power Interface ***************************
  if (page.power) {
    var powerli = document.getElementById('power');
    powerli.title = page.power + "W max**";
    powerli.style.display = "block";

    var pdl = document.getElementById('print_info').getElementsByTagName('dl')[0];
      var dt = document.createElement('dt');
          dt.appendChild(document.createTextNode('Frame Power:'));
        pdl.appendChild(dt);
      var dd = document.createElement('dd');
          dd.id = "print_power";
          dd.appendChild(document.createTextNode('\u2013'));
        pdl.appendChild(dd);

    var fpwr = document.getElementById('footer_power');
        fpwr.getElementsByTagName('strong')[1].firstChild.nodeValue = page.power + "W";
        fpwr.style.display = "block";
  }
}
