/* ********************************************************************
 * Evertz Xenon Frame Layout Tool - Web Application
 *
 * Build a router right before your eyes, using this tool
 *
 */

var Taint = false;

// ***** Prototype additions ******************************************
String.prototype.trim = function() {
  return this.replace(/^\s+|\s+$/g,"");
};
String.prototype.removeWord = function(word) {
  for (var foo = this.trim().split(" "), bar = [], baz, x = 0; baz = foo[x++];)
    if (baz.length && baz != word) bar.push(baz);
  return bar.join(" ");
};

Array.prototype.contains = function(data) {
  for (var x = 0; x < this.length; x++)
    if (this[x] == data) return true;
  return false;
};
Array.prototype.count_values = function() {
  var __countValue = function(value) {
    switch (typeof(value)) {
      case "number": if (Math.floor(value) !== value) return;
      case "string":
        if (value in this && this.hasOwnProperty(value)) {
          ++this[value];
        } else this[value] = 1;
    }
  };
  
  var tmp_arr = {};
  for (var key in this)
    if (this.hasOwnProperty(key))
      __countValue.call(tmp_arr, this[key]);
  return tmp_arr;
}


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

  this.md5 = "";
  this.online = false;
  this.mirror = false;
  this.cursor = 0;
  this.wname = "layout";
  this.type = "Xenon";

  this.size = 4;

  this.infofields = ["author", "company", "country", "project", "contact", "agent"];


  /* ******************************************************************
   * Check if a new module (id) is allowed in a particular slot
   *  - if check is defined and true, verify if it's a valid base frame
   *
   */
  this.isAllowed = function(slot, id, check, silent) {
    var other = slots[Math.floor(slot / 2) * 2 + 1 - slot].module;
    var sltmd = modules[modules.index(id)];
    var othmd = modules[modules.index(other.value)];
    var ptrmd = modules[modules.index(slots[((slot < 4) ? 3 : 11) - slot].module.value)];

    if (slot == 1 || slot == 2 || slot == 5 || slot == 6) {
      if (ptrmd.type.indexOf("xlink") > -1) {
        if (sltmd.type.indexOf("xlink") > -1) {
          if (!silent) alert("Only one +XLINK output module is allowed in each 4RU section.");
          return false;
        }
        if (sltmd.type.indexOf("audio") > -1) {
          if (!silent) alert("Audio output modules cannot be paired with +XLINK output modules.");
          return false;
        }
      }
      if (ptrmd.type.indexOf("audio") > -1) {
        if (sltmd.type.indexOf("xlink") > -1) {
          if (!silent) alert("Audio output modules cannot be paired with +XLINK output modules.");
          return false;
        }
      }
    }

    if (sltmd.type.indexOf("video") > -1) {
      var chk = (inputs.contains(slot)) ? inputs : outputs;
      for (var x = chk.length - 1, tst; x >= 0; x--) {
        tst = modules[modules.index(slots[chk[x]].module.value)];
        if (chk[x] < slot && tst.type.indexOf('video') == -1 && tst.type.indexOf('none') == -1) {
          if (!silent) alert("Video modules must appear above modules of other types.");
          return false;
        }
      }
    }

    if (sltmd.type.indexOf("audio") > -1) {
      var chk = (inputs.contains(slot)) ? inputs : outputs;
      for (var x = 0, tst; x < chk.length; x++) {
        tst = modules[modules.index(slots[chk[x]].module.value)];
        if (chk[x] > slot && tst.type.indexOf('video') > -1) {
          if (!silent) alert("Audio modules must appear below video modules.");
          return false;
        }
      }
    }

    if (slot <= 1) {
      for (var x = 0, ver = false; x < baseFrame.length; x++) {
        if (baseFrame[x].config[slot] == id) {
          if (check) {
            if (!other.value || baseFrame[x].config[1 - slot] == other.value) ver = true;
          } else ver = true;
        }
      } return ver;
    } return true;
  };


  /* ******************************************************************
   * Switch the frame from 4RU to 8RU or vice versa
   *
   */
  this.switchSize = function() {
    document.getElementById('frameSize').firstChild.nodeValue = "Switch to " + this.size + "RU frame";
    this.size = 12 - this.size;
    document.getElementById('framelower').style.display = (this.size == 8) ? "block" : "";
    this.frameStatus(true);
  };


  /* ******************************************************************
   * Call after each change and make appropriate visual changes to the
   *  page
   *
   */
  this.frameStatus = function(silent) {
    var curRef = document.getElementsByName('option_rrf')[0];
    var curDis = curRef.disabled;
    var cursfp = [
      document.getElementsByName('option_sfpi')[0],
      document.getElementsByName('option_sfpo')[0]
    ];

    for (var x = 0, out = 0, fiber = [0, 0], xlink = false; x < this.size / 2; x++) {
      var ix = slots[inputs[x]].module, ox = slots[outputs[x]].module;
      if (ox.selectedIndex == -1) ox.selectedIndex = 0;
      if (ix.selectedIndex == -1) ix.selectedIndex = 0;
      if (x < 2) out += modules[modules.index(ox.value)].io;
      if (modules[modules.index(ix.value)].type.indexOf("fiber") > -1) fiber[0]++;
      if (modules[modules.index(ox.value)].type.indexOf("fiber") > -1) fiber[1]++;
      if (modules[modules.index(ix.value)].type.indexOf("xlink") > -1 ||
          modules[modules.index(ox.value)].type.indexOf("xlink") > -1) xlink = true;
    }
    if (curRef.disabled = (out >= 64) ? "" : "disabled") curRef.checked = "";

    var xlabel = document.getElementsByName('option_rfc')[0].parentNode;
    xlabel.title = (xlink) ? "+FC" : "+FU";

    for (var x = 0, nowVal; x < fiber.length; x++) {
      nowVal = parseInt(cursfp[x].value);
      while (cursfp[x].firstChild) cursfp[x].removeChild(cursfp[x].firstChild);
      for (var y = 0; y <= fiber[x] * 16; y++) {
        var option = document.createElement('option');
            option.value = y;
            option.appendChild(document.createTextNode(y));
          cursfp[x].appendChild(option);
      } cursfp[x].value = nowVal;
      if (cursfp[x].value != nowVal) cursfp[x].value = fiber[x] * 16;
    }

    var lst = document.getElementById('finFinder').tBodies[0].getElementsByTagName('tr');
    for (var x = 0, config = []; x < lst.length; x++) {
      var ixv = "-", oxv = "-";
      if (this.size == 8 || x < 2) {
        var ixs = parseInt(slots[inputs[x]].module.value);
        config.push((!x) ? modules[modules.index(ixs)].id : "0");
        ixv = (ixs) ? modules[modules.index(ixs)].input + slots[inputs[x]].option.firstChild.nodeValue : "Empty Slot";

        var oxs = parseInt(slots[outputs[x]].module.value);
        config.push((!x) ? modules[modules.index(oxs)].id : "0");
        oxv = (oxs) ? modules[modules.index(oxs)].output + slots[outputs[x]].option.firstChild.nodeValue : "Empty Slot";
      }
      lst[x].cells[0].firstChild.nodeValue = ixv;
      lst[x].cells[1].firstChild.nodeValue = oxv;
    }

    var qf0 = document.getElementsByName('quickframe')[0];
    qf0.value = config.join(":");
    if (qf0.selectedIndex == -1) qf0.selectedIndex = 0;

    if (!silent)
      if (curDis && out >= 64 && !curRef.checked &&
          confirm('The redundant Reference Module option (+REF) is available for systems with 64 outputs or greater. Would you like to add this option?'))
        curRef.checked = "checked";
  };


  /* ******************************************************************
   * Raise or lower the module options selection dialogue box
   *   display = boolean - raise/lower
   *             number - silently test if fin has options
   *
   */
  this.optionsDisplay = function(display) {
    if (display !== false) {
      var fin = (typeof display == "number") ? display : this.cursor;
      var opts = false, selInd = modules.index(slots[fin].module.value);

      if (display === true) {
        var ul = Pop.options.element.getElementsByTagName('ul')[0];
        while (ul.firstChild) ul.removeChild(ul.firstChild);
        var title = Pop.options.element.getElementsByTagName('h2')[0].getElementsByTagName('span')[0];
      }

      if (Math.ceil(fin / 2) % 2) { // Output options
        if (modules[selInd].type.indexOf("video") > -1) {
          if (display === true) { // +R Series
            title.firstChild.nodeValue = "Output #" + Math.ceil((fin + 1) / 2);
            var mat = /\+R(\d\d?)/.exec(slots[fin].option.firstChild.nodeValue);
            var rvo = (mat) ? parseInt(mat[1]) : 0;

            var li = document.createElement('li');
                li.style.textAlign = "center";
                li.appendChild(document.createTextNode("Reclocked outputs: "));
              var select = document.createElement('select');
                  select.size = "1";
                for (var x = 0; x <= 32; x += 8) {
                  var opt = document.createElement('option');
                      opt.value = (x) ? "+R" + x : "";
                      opt.appendChild(document.createTextNode(x));
                    select.appendChild(opt);
                } select.value = "+R" + rvo;
                li.appendChild(select);
              ul.appendChild(li);
          } opts = true;
        }

      } else { // Input options
        if (modules[selInd].type.indexOf("digital") > -1 &&
            modules[selInd].type.indexOf("audio") > -1 &&
            modules[selInd].type.indexOf("madi") == -1) {
          if (display === true) { // +SS, +SRC
            title.firstChild.nodeValue = "Input #" + Math.ceil((fin + 1) / 2);

            var li = document.createElement('li');
              var input1 = document.createElement('input');
                  input1.setAttribute('type', "checkbox");
                  input1.value = "+SS";
                  var checked = (slots[fin].option.firstChild.nodeValue.indexOf("+SS") > -1) ? "checked" : "";
                  input1.onclick = function() {
                    this.sibling.disabled = (this.checked) ? "" : "disabled";
                    if (!this.checked) this.sibling.checked = "";
                  };
                li.appendChild(input1);
                li.appendChild(document.createTextNode(' +SS '));
              var small = document.createElement('small');
                  small.appendChild(document.createTextNode('Synchronous AES'));
                li.appendChild(small);
              ul.appendChild(li);
                  input1.checked = checked;

            var li = document.createElement('li');
              var input2 = document.createElement('input');
                  input2.setAttribute('type', "checkbox");
                  input2.value = "+SRC";
                  if (slots[fin].option.firstChild.nodeValue.indexOf("+SS") > -1) {
                    var checked = (slots[fin].option.firstChild.nodeValue.indexOf("+SRC") > -1) ? "checked" : "";
                  } else input2.disabled = "disabled";
                li.appendChild(input2);
                li.appendChild(document.createTextNode(' +SRC '));
              var small = document.createElement('small');
                  small.appendChild(document.createTextNode('Sample Rate Converter'));
                li.appendChild(small);
              ul.appendChild(li);
                  input2.checked = checked;

                  input1.sibling = input2;
          } opts = true;
        }
      }

      slots[fin].button.disabled = (opts) ? "" : "disabled";
      if (display === true) {
        if (opts) {
          Pop.options.element.style.top  = (fin < 4) ?  "90px" : "375px";
          Pop.options.show();
          Pop.options.element.getElementsByTagName('form')[0].elements[0].focus();
        }
      } else slots[fin].button.animate();
      return opts;

    } else {
      if (typeof this.cursor == "number") {
        var inputs = Pop.options.element.getElementsByTagName('input');
        for (var x = 0, optstr = ""; x < inputs.length; x++)
          if (inputs[x].checked) optstr += inputs[x].value;

        var selects = Pop.options.element.getElementsByTagName('select');
        for (var x = 0; x < selects.length; x++) optstr += selects[x].value;

        slots[this.cursor].option.firstChild.nodeValue = optstr;
        Pop.options.hide();
        setTimeout(function() { slots[self.cursor].onclick(); self.cursor = false; }, 500);
        this.frameStatus(false);
      }
    }
  };


  /* ******************************************************************
   * Check frame for strict validity before sending via Ajax
   *
   */
  this.checkFrame = function() {
    var atLeast = false, num = Math.floor(outputs[x] / 2) + 1;
    for (var x = 0, checkDepth = this.size / 2; x < this.size / 2; x++)
      if (slots[outputs[x]].module.value != "0" || slots[inputs[x]].module.value != "0") atLeast = true;
    if (!atLeast) return alert('Frame is empty');

    for (var x = 0, error = 0; !error && x < this.size / 2; x++) {
      switch (slots[outputs[x]].module.value) {
        case "0":
          if (!x) {
            error++;
            alert('Please select a base frame, or select a fin for slots #1 and #2');
          } break;
        case "1": case "2": case "3":
          for (var y = 0, test = 1; y < this.size / 2; y++) {
            var syv = slots[inputs[y]].module.value;
            if (syv == "1" || syv == "2" || syv == "3") test = 0;
          }
          if (error += test)
            alert('The Analog Audio or Single Density Digital Audio Output Fin in Output #' + num +
                  ' requires at least one Analog Audio or Single Density Digital Audio Input Fin in the frame.');
          break;
        case "4":
          for (var y = 0, test = 1; y < this.size / 2; y++)
            if (slots[inputs[y]].module.value == "4") test = 0;
          if (error += test)
            alert('The Double Density Digital Audio Output Fin in Output #' + num + 
                  ' requires at least one Double Density Digital Audio Input Fin in the frame.');
          break;
        case "5": case "8": case "10":
          for (var y = 0, test = 1; y < this.size / 2; y++) {
            var syv = slots[inputs[y]].module.value;
            if (syv == "6" || syv == "5" || syv == "9" || syv == "8" || syv == "11" || syv == "10" || syv == "12") test = 0;
          }
          if (error += test)
            alert('The SD Only Video Output Fin in Output #' + num + 
                  ' requires at least one HD/SD Video Input Fin or SD Only Video Input Fin in the frame.');
          break;
        case "6": case "9": case "11":
          for (var y = 0, test = 1; y < this.size / 2; y++) {
            var syv = slots[inputs[y]].module.value;
            if (syv == "6" || syv == "9" || syv == "11" || syv == "12") test = 0;
          }
          if (error += test)
            alert('The HD/SD Video Output Fin in Output #' + num + 
                  ' requires at least one HD/SD Video Input Fin in the frame.');
      }
    } return !error;
  };


  /* ******************************************************************
   * 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);

    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));

    values.push("size=" + this.size);

    var options = ["rps", "rfc", "rrf", "sfpi", "sfpo"];
    for (var x = 0, elem; x < options.length; x++) {
      elem = document.getElementsByName('option_' + options[x])[0];
      if (elem.type == "checkbox") {
        values.push(options[x] + "=" + ((elem.checked) ? "Yes" : "No"));
      } else values.push(options[x] + "=" + encodeURIComponent(elem.value));
    }

    for (var y = 0, full = false, config = []; y < this.size; y++) {
      if (slots[y].module.value != "0") {
        full = true;
        values.push("module" + y + "=" + slots[y].module.value);
        values.push("option" + y + "=" + encodeURIComponent(slots[y].option.firstChild.nodeValue));
      } config.push(parseInt(slots[y].module.value));
    }


    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") {
              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.", "");
          }

          // Populate the Completed Layout screen
          var completed = document.getElementById('completed');
          completed.getElementsByTagName('h1')[0].getElementsByTagName('span')[0].firstChild.nodeValue = self.size + "RU";

          var now = new Date(), month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
          document.getElementById('date').firstChild.nodeValue = month[now.getMonth()] + " " + now.getDay() + ", " + now.getFullYear();

          for (var x = 0; x < self.infofields.length; x++) {
            if (self.infofields[x] == "agent") {
              var agent = document.getElementsByName('info_' + self.infofields[x])[0];
              document.getElementById('view_' + self.infofields[x]).firstChild.nodeValue = (agent.selectedIndex) ? agent.options[agent.selectedIndex].firstChild.nodeValue : "None selected";
            } else document.getElementById('view_' + self.infofields[x]).firstChild.nodeValue = document.getElementsByName('info_' + self.infofields[x])[0].value || "\xa0";
          }

          var finName = {input: [], output: []};
          for (var x = 0, fins = [], mods = [], fopts = "", xlink = false; x < self.size; x++) {
            var io = (Math.ceil(x / 2) % 2) ? "output" : "input";
            var grab = slots[x].module.value;
            var opt = slots[x].option.firstChild.nodeValue;
            if (x < 2) fopts += opt;
            finName[io].push(modules[modules.index(parseInt(grab))]);
            if (modules[modules.index(parseInt(grab))].type.indexOf("xlink") > -1) xlink = true;
            fins.push(grab);
            if (x > 1 && modules[modules.index(parseInt(grab))][io] != "blank")
              mods.push(modules[modules.index(parseInt(grab))][io] + opt);
          } mods = mods.count_values();
          var sfpi = parseInt(document.getElementsByName('option_sfpi')[0].value);
          if (sfpi) mods['SFP1R-2'] = sfpi;
          var sfpo = parseInt(document.getElementsByName('option_sfpo')[0].value);
          if (sfpo) mods['SFP1T-13-2'] = sfpo;

          var slotView = document.getElementById('slotView').tBodies;
          for (var x = 0, tds; x < self.size / 2; x++) {
            tds = slotView[Math.floor(x / 2)].rows[x % 2].cells;
            while (tds[1].firstChild) tds[1].removeChild(tds[1].firstChild);
            if (finName.input[x].input != "blank") {
              var strong = document.createElement('strong');
                  strong.appendChild(document.createTextNode(finName.input[x].input));
                tds[1].appendChild(strong);
            } tds[1].appendChild(document.createTextNode(finName.input[x].name.replace(/I\/O/, "Input")));
            while (tds[2].firstChild) tds[2].removeChild(tds[2].firstChild);
            if (finName.output[x].output != "blank") {
              var strong = document.createElement('strong');
                  strong.appendChild(document.createTextNode(finName.output[x].output));
                tds[2].appendChild(strong);
            } tds[2].appendChild(document.createTextNode(finName.output[x].name.replace(/I\/O/, "Output")));
          } slotView[1].style.display = (self.size == 4) ? "none" : "";

          var ordering = document.getElementById('ordering').getElementsByTagName('table')[0].tBodies[0];
          while (ordering.firstChild) ordering.removeChild(ordering.firstChild);
          var tr = document.createElement('tr');
            var th = document.createElement('th');
                th.appendChild(document.createTextNode("1 x"));
              tr.appendChild(th);
            var td = document.createElement('td');
              for (var x = 0, match = false; !match && x < baseFrame.length; x++)
                if (baseFrame[x].config.length == config.length)
                  for (var y = 0, match = true; match && y < 2; y++)
                    if (baseFrame[x].config[y] != config[y]) match = false;
                td.appendChild(document.createTextNode(baseFrame[x - 1].name));
              var opt = {rps: "+2PS", rfc: (xlink) ? "+FC" : "+FU", rrf: "+REF"};
              for (var prop in opt)
                if (document.getElementsByName('option_' + prop)[0].checked)
                  td.appendChild(document.createTextNode(opt[prop]));
                if (fopts) td.appendChild(document.createTextNode(fopts));
              tr.appendChild(td);
            ordering.appendChild(tr);
          for (prop in mods) {
            var tr = document.createElement('tr');
              var th = document.createElement('th');
                  th.appendChild(document.createTextNode(mods[prop] + " x"));
                tr.appendChild(th);
              var td = document.createElement('td');
                  td.appendChild(document.createTextNode(prop));
                tr.appendChild(td);
              ordering.appendChild(tr);
          }

          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 " + page.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.style.visibility = "visible";
                  completed.style.display = "block";
                  document.body.style.height = completed.offsetHeight + "px";
                } 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;
  };
}




/* ***** Global Variables ****************************************** */
var page = {frame: "Xenon", name: "Router"};
var app = new Application(), Pop;
var inputs = [0, 3, 4, 7], outputs = [1, 2, 5, 6], slots = [];



/* ***************************************************************************************************************************
 * Prepare the document onload
 *
 */
window.onload = function(e) {

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

  for (var x = 0, butts = document.getElementsByTagName('button'); x < butts.length; x++) {
    butts[x].onmouseover = function() { if (!this.disabled) this.className += " active"; };
    butts[x].onmouseout = function() { this.className = this.className.removeWord("active"); };
  }


  // ***** Build Base frame dropdown **********************************
  var quickframe = document.getElementsByName('quickframe')[0];
  while (quickframe.firstChild) quickframe.removeChild(quickframe.firstChild);
  for (var x = 0, f, size = 0, optgroup = false; f = baseFrame[x++];) {
    if (f.size != size) {
      if (optgroup) quickframe.appendChild(optgroup);
      var optgroup = document.createElement('optgroup');
          optgroup.label = f.size + "RU";
      size = f.size;
    }
    var option = document.createElement('option');
        option.value = f.config.join(":");
        option.appendChild(document.createTextNode(f.name + ((f.type != "Empty") ? " (" + f.type + ")" : "")));
      if (x == 1) option.selected = "selected";
      optgroup.appendChild(option);

  } quickframe.appendChild(optgroup);
  quickframe.onchange = function() {
    var code = this.value.split(":");
    for (var x = 0, empty = true, valid = true; x < 2; x++) {
      var slotCheck = parseInt(slots[x].module.value);
      if (slotCheck && slotCheck != code[x]) empty = false;
      if (!app.isAllowed(x, code[x], false, true)) valid = false;
    }

    if (!valid) {
      if (confirm('Other modules in the frame are not valid within this base frame. Press OK to clear all modules and use the new base frame.')) {
        if (app.size != code.length) app.switchSize();
        for (var x = code.length - 1; x >= 0; x--) {
          slots[x].option.firstChild.nodeValue = "";
          slots[x].module.value = code[x];
          slots[x].flipped = (!app.optionsDisplay(x) && code[x] != "0") ? true : false;
          slots[x].onclick();
        } Taint = true;
      }
    } else if (empty || confirm('Input and Output #1 are not empty. Press OK to clear these slots and use the selected base frame.')) {
      if (app.size != code.length) app.switchSize();
      for (var x = 0; x < 2; x++) {
        if (slots[x].module.value != code[x]) {
          slots[x].option.firstChild.nodeValue = "";
          slots[x].module.value = code[x];
          slots[x].flipped = (!app.optionsDisplay(x) && code[x] != "0") ? true : false;
          slots[x].onclick();
        }
      } Taint = true;
    } else Taint = true;
    app.frameStatus(true);
  };


  // ***** Build slots ************************************************
  for (var x = 0; x < 8; x++) {
    slots[x] = document.getElementById('fin' + x);

    slots[x].x = x;
    slots[x].io = (Math.ceil(x / 2) % 2) ? "Output" : "Input";
    slots[x].img = slots[x].getElementsByTagName('img')[0];
    slots[x].content = slots[x].getElementsByTagName('div')[0];
    slots[x].size = 6;
    slots[x].flipped = true;
    slots[x].onclick = function() {
      if (this.size != 6) return false;
      var self = this;

      Pop.options.hide();
      this.content.style.display = "none";
      this.interval = setInterval(function() {
        var h = Math.pow(2, Math.abs(--self.size));
        self.img.style.height = (h = (h != 64) ? h : 65) + "px";
        self.img.style.top = 32 - h / 2 + "px";
        switch (Math.abs(self.size)) {
          case 0:
            var titleDisplay = (Number(self.module.value)) ? self.module.options[self.module.selectedIndex].text : "Empty slot";
            self.img.title = self.io + " #" + (Math.floor(self.x / 2) + 1) + ": " + titleDisplay;
            self.img.src = "rear/Xenon/" + ((self.flipped) ? modules[modules.index(self.module.value)][self.io.toLowerCase()] : "blank") + ".png";
            break;
          case 6:
            clearInterval(self.interval);
            if (self.flipped = !self.flipped) self.content.style.display = "";
            self.size = Math.abs(self.size);
        }
      }, 15);
    };

    slots[x].option = slots[x].getElementsByTagName('var')[0];
    slots[x].option.firstChild.nodeValue = "";

    slots[x].button = slots[x].getElementsByTagName('button')[0];
    slots[x].button.x = x;
    slots[x].button.onclick = function(e) {
      app.cursor = this.x;
      app.optionsDisplay(true);
      return (e || event).cancelBubble = true;
    };
    slots[x].button.animate = function() {
      var self = this;
      this.flash = 0;
      if (!this.disabled) {
        this.interval = setInterval(function() {
          self.style.border = (++self.flash % 2) ? "2px solid #ffd700" : "";
          if (self.flash > 6) clearInterval(self.interval);
        }, 100);
      } else self.style.border = "";
    };

    slots[x].module = slots[x].getElementsByTagName('select')[0];
    slots[x].module.x = x;
    slots[x].module.old = 0;
    slots[x].module.group = Math.floor(x / 4) * 4;
    slots[x].module.onfocus = function() { Pop.options.hide(); };
    slots[x].module.onclick = function(e) { return (e || event).cancelBubble = true; }
    slots[x].module.onchange = function() {
      var changed = false;

      // check if the sibling slot (1 or 2) needs to be changed
      if (this.x < 2 && !app.isAllowed(1 - this.x, slots[1 - this.x].module.value, true))
        for (var y = 0; !changed && y < baseFrame.length; y++)
          if (baseFrame[y].config[this.x] == this.value)
            changed = baseFrame[y].config[1 - this.x];

      if (app.isAllowed(this.x, this.value)) {
        this.old = this.selectedIndex;

        slots[this.x].option.firstChild.nodeValue = "";
        if (!app.optionsDisplay(this.x)) slots[this.x].onclick();

        if (changed) {
          var sibling = slots[1 - this.x];
          sibling.module.value = changed;
          sibling.option.firstChild.nodeValue = "";
          if (!app.optionsDisplay(1 - this.x)) {
            sibling.flipped = true;
            sibling.onclick();
          } else if (!sibling.flipped) sibling.onclick();
        } app.frameStatus(false);
        Taint = true;
      } else return (this.selectedIndex = this.old);
    };
    for (var y = 1; y < modules.length; y++) {
      if (modules[y][slots[x].io.toLowerCase()]) {
        var opt = document.createElement('option');
            opt.value = modules[y].id;
            opt.appendChild(document.createTextNode(modules[y].name.replace(/I\/O/g, slots[x].io)));
          slots[x].module.appendChild(opt);
      }
    }
  }


  var manualink = document.getElementById('manualink');
  if (manualink) {
    var manual = document.getElementById('manual');
    manual.animate = function(direction, step) {
      var self = this, steps = [512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0];
      this.moving = true;
      this.style.top = -steps[step] + 26 + "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);
    };
  }


  if (document.getElementById('alert_contact')) {
    document.getElementById('alert_contact').onclick = function() {
      alert('For the fastest response time, please include an email address in the Contact Information field');
      return false;
    };
  }
  var saleslink = document.getElementById('saleslink');
  if (saleslink) saleslink.target = "_blank";


  loadFrame();
  document.body.className = document.body.className.removeWord("wait");
  Pop.waiter.hide();
};

window.onbeforeunload = function(e) {
  var e = e || window.event;
  if (Taint) return e.returnValue = "You are about to close or leave this page; all changes will be lost. Are you sure?";
};
