// timer for updating job status
var job_status_timer;
var job_status_interval = 15000;
var job_status_min = 15000;

//Returns true if it is a DOM node
function isNode(o){
  return (
    typeof Node === "object" ? o instanceof Node : 
    o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
  );
}

function replace_tokens(htmlWithTokens) {
  "use strict";
  var container, tokens, values, locations, i, pos, which, chunk;
  if (arguments.length % 2 != 1) {
    throw new Error("Expected token and value pairs after the html-with-tokens variable.");
  }
  tokens = [];
  values = [];
  locations = [];
  for (i = 1; i < arguments.length; i += 2) {
    tokens.push(arguments[i]);
    values.push(arguments[i+1]);
    locations.push(htmlWithTokens.indexOf(arguments[i]));
  }
  pos = 0;
  container = document.createElement("span");
  while (pos < htmlWithTokens.length) {
    // find the first token
    which = null;
    for (i = 0; i < locations.length; i++) {
      if (locations[i] != -1) {
        if (which == null) which = i;
        else if (locations[i] < locations[which]) which = i;
      }
    }
    if (which == null) break;
    // output everything up to the first token
    if (pos < locations[which]) {
      chunk = document.createElement("span");
      chunk.innerHTML = htmlWithTokens.substring(pos, locations[which]);
      container.appendChild(chunk);
    }
    // output the replacement value, if it is text then wrap in a text node, otherwise clone
    if (typeof values[which] === "string" || typeof values[which] === "number") {
      container.appendChild(document.createTextNode(values[which]));
    } else if (isNode(values[which])) {
      container.appendChild(values[which].cloneNode(true));
    }
    // skip over the token
    pos = locations[which] + tokens[which].length;
    // update the token locations where required
    for (i = 0; i < locations.length; i++) {
      if (locations[i] != -1 && locations[i] < pos) {
        locations[i] = htmlWithTokens.indexOf(tokens[i], pos);
      }
    }
  }
  // output the final chunk
  if (pos < htmlWithTokens.length) {
    chunk = document.createElement("span");
    chunk.innerHTML = htmlWithTokens.substring(pos);
    container.appendChild(chunk);
  }
  return container;
}

function create_row(even, name, value) {
  "use strict";
  var row, cell;
  row = document.createElement("tr");
  row.className = (even ? "even" : "odd");
  cell = document.createElement("th");
  if (typeof name === "string") {
    cell.innerHTML = name;
  } else if (isNode(name)) {
    cell.appendChild(name);
  }
  row.appendChild(cell);
  cell = row.insertCell(row.cells.length);
  if (typeof value === "string") {
    cell.innerHTML = value;
  } else if (isNode(value)) {
    cell.appendChild(value);
  }
  return row;
}

function get_job(job_id) {
  var job_str, job_obj;
  // check for local storage support
  try {
    if (!('localStorage' in window && window['localStorage'] !== null)) return;
  } catch (e) {
    return null;
  }
  // check for JSON support
  if (!window.JSON) return null;
  // get the job data in string format
  job_str = localStorage.getItem(job_id);
  if (job_str == null) return null;
  // parse the job data into object format
  job_obj = JSON.parse(job_str);
  // return job data
  return job_obj;
}

function create_job_detail_sequence(item, inputs, even) {
  var input, value, text, alphabet, file;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  value = document.createElement("span");
  if (input["count"] > 1) {
    text = "A set of " + input["count"];
  } else {
    text = "One";
  }
  value.appendChild(document.createTextNode(text));
  text = null;
  // Check to see if the custom name has been set.
  if (input["custom_name"] != null) {
    text = input["custom_name"];
  } else if (typeof input["alphabet"] === "string") {
    if (input["alphabet"] == "DNA") {
      text = "DNA";
    } else if (input["alphabet"] == "RNA") {
      text = "RNA";
    } else if (input["alphabet"] == "PROTEIN") {
      text = "protein";
    }
  } else if (input.alphabet != null && typeof input.alphabet === "object" && input.alphabet.name != null) {
    text = input.alphabet.name;
  }
  if (text != null) {
    alphabet = document.createElement("span");
    alphabet.className = "alphabet";
    alphabet.appendChild(document.createTextNode(text));
    value.appendChild(document.createTextNode(" "));
    value.appendChild(alphabet);
  }
  if (input["count"] > 1) {
    text = " sequences, ";
    if (input["min"] === input["max"]) {
      text += "all " + input["min"] + " in length,";
    } else {
      text += "between " + input["min"] + " and " + input["max"] + 
        " in length (average length " + input["avg"].toFixed(1) + "),";
    }
  } else {
    text = " sequence, with a length of " + input["min"];
  }
  value.appendChild(document.createTextNode(text));
  if (input["source"] == "file") {
    value.appendChild(document.createTextNode(" from the file "));
    file = document.createElement("span");
    file.className = "file";
    file.appendChild(document.createTextNode(input["orig-file"]));
    value.appendChild(file);
    if (input["orig-file"] !== input["safe-file"]) {
      value.appendChild(document.createTextNode(" which was renamed to "));
      file = document.createElement("span");
      file.className = "file";
      file.appendChild(document.createTextNode(input["safe-file"]));
      value.appendChild(file);
    }
  } else if (input["source"] == "db") {
    value.appendChild(document.createTextNode(" from the database "));
    file = document.createElement("span");
    file.className = "file";
    file.appendChild(document.createTextNode(input["db_name"]));
    value.appendChild(file);
  }
  value.appendChild(document.createTextNode("."));
  return create_row(even, item["name"], value);
}

function create_job_detail_motif(item, inputs, even) {
  var input, value, text, file;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  value = document.createElement("span");
  if (input["count"] > 1) {
    text = "A set of " + input["count"];
  } else {
    text = "One";
  }
  value.appendChild(document.createTextNode(text));
  text = null;
  var alph_obj = input["alphabet"];
  var alph = (alph_obj != null) ? alph_obj["name"] : null;
  if (alph === "DNA") {
    text = "DNA";
  } else if (alph === "RNA") {
    text = "RNA";
  } else if (alph === "Protein") {
    text = "protein";
  }
  if (text != null) {
    alphabet = document.createElement("span");
    alphabet.className = "alphabet";
    alphabet.appendChild(document.createTextNode(text));
    value.appendChild(document.createTextNode(" "));
    value.appendChild(alphabet);
  }
  if (input["count"] > 1) {
    text = " motifs, ";
    if (input["min"] === input["max"]) {
      text += "all " + input["min"] + " in width";
    } else {
      text += "between " + input["min"] + " and " + input["max"] + 
        " in width (average width " + input["avg"].toFixed(1) + ")";
    }
  } else {
    text = " motif, with a width of " + input["min"];
  }
  value.appendChild(document.createTextNode(text));
  if (input["source"] == "file") {
    value.appendChild(document.createTextNode(", from the file "));
    file = document.createElement("span");
    file.className = "file";
    file.appendChild(document.createTextNode(input["orig-file"]));
    value.appendChild(file);
    if (input["orig-file"] !== input["safe-file"]) {
      value.appendChild(document.createTextNode(" which was renamed to "));
      file = document.createElement("span");
      file.className = "file";
      file.appendChild(document.createTextNode(input["safe-file"]));
      value.appendChild(file);
    }
  } else if (input["source"] == "db") {
    value.appendChild(document.createTextNode(", from the database "));
    file = document.createElement("span");
    file.className = "file";
    file.appendChild(document.createTextNode(input["db_name"]));
    value.appendChild(file);
  }
  value.appendChild(document.createTextNode("."));
  return create_row(even, item["name"], value);
}

function create_job_detail_gomo(item, inputs, even) {
  "use strict";
  var input, value, file, description;
  if (typeof inputs[item["key"]] === "undefined" || inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  value = document.createElement("span");
  value.appendChild(document.createTextNode("The database "));
  file = document.createElement("span");
  file.className = "file";
  file.innerHTML = input["db_name"];
  value.appendChild(file);
  value.appendChild(document.createTextNode(" described as "));
  description = document.createElement("span");
  description.innerHTML = input["db_description"];
  value.appendChild(description);
  return create_row(even, item["name"], value);
}

function create_job_detail_loci(item, inputs, even) {
  "use strict";
  var input, fileOrig, fileSafe, text, value;
  input = inputs["loci"];
  text = input["safe-file"];
  fileOrig = document.createElement("span");
  fileOrig.className = "file";
  fileOrig.appendChild(document.createTextNode(input["orig-file"]));
  fileSafe = document.createElement("span");
  fileSafe.className = "file";
  fileSafe.appendChild(document.createTextNode(input["safe-file"]));
  value = replace_tokens(text, "!!ORIG-NAME!!", fileOrig, "!!SAFE-NAME!!", fileSafe);
  return create_row(even, item["name"], value);
}

function create_job_detail_background(item, inputs, even) {
  "use strict";
  var input, source, order, file, text, fileOrig, fileSafe, value;
  if (typeof inputs[item["key"]] === "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  source = input["source"];
  if (source == "UNIFORM") {
    value = document.createTextNode("A uniform background.");
  } else if (source == "MEME") {
    value = document.createTextNode("The background specified in the motif input.");
  } else if (source == "FILE") {
    file = input["file"];
    text = (file["safe-file"] === file["orig-file"] ? 
        "The background from !!SAFE-NAME!!." : 
        "The background from !!ORIG-NAME!! which was renamed to !!SAFE-NAME!!.");
    fileOrig = document.createElement("span");
    fileOrig.className = "file";
    fileOrig.appendChild(document.createTextNode(file["orig-file"]));
    fileSafe = document.createElement("span");
    fileSafe.className = "file";
    fileSafe.appendChild(document.createTextNode(file["safe-file"]));
    value = replace_tokens(text, "!!ORIG-NAME!!", fileOrig, "!!SAFE-NAME!!", fileSafe);
  } else { // source == "ORDER_X" where X is 0, 1, 2, 3 or 4
    order = input["order"];
    value = document.createTextNode("A " + order + "-order background model generated from the supplied sequences.");
  }
  return create_row(even, item["name"], value);
}

function create_job_detail_file(item, inputs, even) {
  var input, text, fileOrig, fileSafe;
  if (typeof inputs[item["key"]] == "undefined" || inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  text = (input["safe-file"] === input["orig-file"] ? item["normal"] : item["rename"]);
  fileOrig = document.createElement("span");
  fileOrig.className = "file";
  fileOrig.appendChild(document.createTextNode(input["orig-file"]));
  fileSafe = document.createElement("span");
  fileSafe.className = "file";
  fileSafe.appendChild(document.createTextNode(input["safe-file"]));
  return create_row(even, item["name"], replace_tokens(text, "!!ORIG-NAME!!", fileOrig, "!!SAFE-NAME!!", fileSafe));
}

function create_job_detail_psms(item, inputs, even) {
  var psms = inputs[item["key"]];
  if (psms == null) return null;
  var container = document.createElement("ul");
  var i, psm, li, fileOrig, fileSafe, filterField, filterType, filterThresh, text;
  for (i = 0; i < psms.length; i++) {
    psm = psms[i];
    li = document.createElement("li");
    text = "Matches from the file !!ORIG-NAME!!";
    if (psm["safe-file"] != psm["orig-file"]) {
      text += ", which was renamed to !!SAFE-NAME!!";
    }
    if (psm["filter-field"] != null) {
      text += "; filtered on !!FILTER-FIELD!! !!FILTER-TYPE!! !!FILTER-THRESH!!";
      filterField = document.createElement("span");
      filterField.className = "filter_field";
      filterField.appendChild(document.createTextNode(psm["filter-field"]));
      filterType = document.createElement("span");
      filterType.appendChild(
        document.createTextNode(
          psm["filter-type"] == "lt" ? "\u003C" :
          psm["filter-type"] == "le" ? "\u2264" :
          psm["filter-type"] == "eq" ? "=" :
          psm["filter-type"] == "ge" ? "\u2265" :
          psm["filter-type"] == "gt" ? "\u003E" :
          "ERROR"
        )
      );
      filterThresh = document.createElement("span");
      filterThresh.appendChild(document.createTextNode(psm["filter-thresh"]));
    }
    
    fileOrig = document.createElement("span");
    fileOrig.className = "file";
    fileOrig.appendChild(document.createTextNode(psm["orig-file"]));
    fileSafe = document.createElement("span");
    fileSafe.className = "file";
    fileSafe.appendChild(document.createTextNode(psm["safe-file"]));
    li.appendChild(replace_tokens(text, "!!ORIG-NAME!!", fileOrig, "!!SAFE-NAME!!",
          fileSafe, "!!FILTER-FIELD!!", filterField, "!!FILTER-TYPE!!",
          filterType, "!!FILTER-THRESH!!", filterThresh));
    container.appendChild(li);
  }
  return create_row(even, item["name"], container);
}

function create_job_detail_cname(item, inputs, even) {
  var cname, text;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  cname = inputs[item["key"]];
  
  if (typeof cname !== "string") throw new Error("Expected string for property value");
  
  text = item["any"];
  
  return create_row(even, item["name"], replace_tokens(text, "!!VALUE!!", document.createTextNode(cname)));
}

//
// Use this for text with embedded URLs
//
function create_job_detail_span(item, inputs, even) {
  var cname, text;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  cname = inputs[item["key"]];
  
  if (typeof cname !== "string") throw new Error("Expected string for property value");
  
  text = document.createElement("span");
  text.innerHTML = cname;
  return create_row(even, item["name"], text);
}

function create_job_detail_choice(item, inputs, even) {
  var input;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  if (typeof input !== "string") throw new Error("Expected string for property value");
  if (typeof item["options"][input] == "undefined") return null;
  return create_row(even, item["name"], item["options"][input]);
}

function create_job_detail_number(item, inputs, even) {
  var input, text;
  if (typeof inputs[item["key"]] == "undefined" ||  inputs[item["key"]] == null) {
    return null;
  }
  input = inputs[item["key"]];
  if (typeof input !== "number") throw new Error("Expected number for property value");
  if (input == 0 && typeof item["zero"] === "string") {
    text = item["zero"];
  } else if (input == 1 && typeof item["one"] === "string") {
    text = item["one"];
  } else if (typeof item["any"] === "string") {
    text = item["any"];
  } else {
    text = "!!VALUE!!";
  }
  return create_row(even, item["name"], replace_tokens(text, "!!VALUE!!", input));
}

function create_job_detail_count_range(item, inputs, even) {
  var low, high, text;
  low = null;
  high = null;
  if (typeof inputs[item["keyLow"]] == "number") {
    low = inputs[item["keyLow"]];
  }
  if (typeof inputs[item["keyHigh"]] == "number") {
    high = inputs[item["keyHigh"]];
  }
  if (low == null && high == null) return null;
  if (low != null && high != null) {
    if (low == high) {
      text = item["same"];
    } else {
      text = item["both"];
    }
  } else if (low != null) {
    text = item["low"];
  } else {
    text = item["high"];
  }
  return create_row(even, item["name"], replace_tokens(text, "!!LOW!!", low, "!!HIGH!!", high));
}

function create_job_detail_filter(item, inputs, even) {
  var field, type, thresh, text;
  if (typeof inputs[item["keyField"]] == "undefined" ||  inputs[item["keyField"]] == null) {
    return null;
  }
  if (typeof inputs[item["keyType"]] == "undefined" ||  inputs[item["keyType"]] == null) {
    return null;
  }
  if (typeof inputs[item["keyThresh"]] == "undefined" ||  inputs[item["keyThresh"]] == null) {
    return null;
  }
  
  field = inputs[item["keyField"]];
  type = inputs[item["keyType"]];
  thresh = inputs[item["keyThresh"]];

  if (typeof field !== "string") throw new Error("Expected string for property value");
  if (typeof type !== "string") throw new Error("Expected string for property value");
  if (typeof thresh !== "number") throw new Error("Expected number for property value");
  
  text = item["any"];
//  text = replace_tokens(text, "!!FIELD!!", field);
//  text = replace_tokens(text, "!!TYPE!!", type);
//  text = replace_tokens(text, "!!THRESH!!", thresh);
  
/*  if (input == 0 && typeof item["zero"] === "string") {
    text = item["zero"];
  } else if (input == 1 && typeof item["one"] === "string") {
    text = item["one"];
  } else if (typeof item["any"] === "string") {
    text = item["any"];
  } else {
    text = "!!VALUE!!";
  }
  return create_row(even, item["name"], replace_tokens(text, "!!VALUE!!", input));*/
  var typeString;
  if (type == "lt") {
    typeString = "\u003C";
  } else if (type == "le") {
    typeString = "\u2264";
  } else if (type == "eq") {
    typeString = "=";
  } else if (type == "ge") {
    typeString = "\u2265";
  } else if (type == "gt") {
    typeString = "\u003E";
  }
  
  return create_row(even, item["name"], replace_tokens(text, "!!FIELD!!", document.createTextNode(field), "!!TYPE!!", typeString, "!!THRESH!!", thresh));
}

function create_job_detail_flag(item, inputs, even) {
  var input;
  if (typeof inputs[item["key"]] != "boolean") return null;
  input = inputs[item["key"]];
  if (typeof item[(input ? "on" : "off")] != "string") return null;
  return create_row(even, item["name"], item[(input ? "on" : "off")]);
}

function create_job_row(item, inputs, even) {
  var type;
  type = item["type"];
  if (type == "sequences") {
    return create_job_detail_sequence(item, inputs, even);
  } else if (type == "loci") {
    return create_job_detail_loci(item, inputs, even);
  } else if (type == "motifs") {
    return create_job_detail_motif(item, inputs, even);
  } else if (type == "gomo") {
    return create_job_detail_gomo(item, inputs, even);
  } else if (type == "background") {
    return create_job_detail_background(item, inputs, even);
  } else if (type == "file") {
    return create_job_detail_file(item, inputs, even);
  } else if (type == "psms") {
    return create_job_detail_psms(item, inputs, even);
  } else if (type == "choice") {
    return create_job_detail_choice(item, inputs, even);
  } else if (type == "count" || type == "number") {
    return create_job_detail_number(item, inputs, even);
  } else if (type == "range") {
    return create_job_detail_count_range(item, inputs, even);
  } else if (type == "flag") {
    return create_job_detail_flag(item, inputs, even);
  } else if (type == "cname") {
    return create_job_detail_cname(item, inputs, even);
  } else if (type == "span") {
    return create_job_detail_span(item, inputs, even);
  } else if (type == "filter") {
    return create_job_detail_filter(item, inputs, even);
  }
  return null;
}

function display_job_information(container, layout, job) {
  var table, thead, tbody, row, cell, inputs, i, even, item;
  // output: when, description, inputs
  table = document.createElement("table");
  table.className = "job_details";
  tbody = document.createElement("tbody");
  tbody.appendChild(create_row(false, document.createTextNode("Submitted"), 
        document.createTextNode((new Date(job["when"])).toLocaleString())));
  tbody.appendChild(create_row(true, document.createTextNode("Expires"), 
        document.createTextNode((new Date(job["expiry"])).toLocaleString())));
  // description row
  even = false;
  if (typeof job["description"] === "string") {
    tbody.appendChild(create_row(false, document.createTextNode("Description"), 
          document.createTextNode(job["description"])));
    even = true;
  }
  if (typeof job["inputs"] !== "undefined" && layout != null) {
    inputs = job["inputs"];
    for (i = 0; i < layout.length; i++) {
      item = layout[i];
      if ((row = create_job_row(item, inputs, even)) != null) {
        tbody.appendChild(row);
        even = !even;
      }
    }
  }
  table.appendChild(tbody);
  container.innerHTML = "";
  container.appendChild(table);
}


function viewport_height() {
  if (typeof window.innerWidth != 'undefined') {
    // the more standards compliant browsers (mozilla/netscape/opera/IE7)
    // use window.innerWidth and window.innerHeight
    return window.innerHeight
  } else if (typeof document.documentElement != 'undefined' &&
      typeof document.documentElement.clientWidth != 'undefined' 
      && document.documentElement.clientWidth != 0) {
    // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
    return document.documentElement.clientHeight
  } else {
    // older versions of IE
    return document.getElementsByTagName('body')[0].clientHeight
  }
}

function resize_preview() {
  var container, iframe_elems, iframe, rect, scroll;      
  container = $("preview_container");
  if (container == null) return;
  iframe_elems = container.getElementsByTagName("iframe");
  iframe = (iframe_elems.length > 0 ? iframe_elems[0] : null);
  if (iframe == null) return;

  rect = iframe.getBoundingClientRect();
  scroll = ((typeof window.pageYOffset !== "undefined") ?
      window.pageYOffset : document.body.scrollTop);
  h = viewport_height() - (rect.top + scroll) - 20;
  iframe.style.height = h + "px";
}

function update_preview(job_url) {
  var container = $("preview_container");
  // get the preview iframe if it already exists
  var iframe_elems = container.getElementsByTagName("iframe");
  var iframe = (iframe_elems.length > 0 ? iframe_elems[0] : null);
  // if the iframe doesn't exist then create it
  preview = (iframe != null ? iframe : document.createElement("iframe"));
  // randomise the ID to stop safari from caching the content
  preview.id ="IF_" + new Date().getTime();
  // refresh the preview, try to prevent caching by appending the current time in seconds
  preview.src = job_url + "?stopcache=" + (new Date()).getTime();
  // for a new preview, wait a moment before adding it to the page
  if (iframe == null) {
    // wait a second before displaying the page to allow it to load
    window.setTimeout( function() {
      "use strict";
      container.appendChild(preview);
      toggle_class($("details"), "expanded", false);
      resize_preview();
    }, 1000);
  }
}

function query_status() {
  if (typeof service === "undefined" || typeof id === "undefined") {
    clearInterval(job_status_timer);
    return;
  }
  var url, request;
  url = "../info/status?service=" + service + "&id=" + id + "&interval=" + job_status_interval + "&xml=1";
  request = new XMLHttpRequest();
  request.addEventListener("load", function(e) {
    "use strict";
    var preview;
    var doc = request.responseXML;
    // query the job status and if a index page is present
    var status_elems = doc.getElementsByTagName("status");
    var job_status = (status_elems.length > 0 ? status_elems[0].textContent : null);
    var url_elems = doc.getElementsByTagName("url");
    var job_url = (url_elems.length > 0 ? url_elems[0].textContent : null);
    if (job_status != null) {
      // stop refreshing if we are done
      var stop = (job_status == "expired" || job_status == "failed" || job_status == "done" || job_status == "unknown");
      // if stopping and the job status has changed since we last saw it then
      if (stop && !(new RegExp("\\b"+job_status+"\\b")).test($("message").className)) {
        // force a full page refresh to try to avoid caching issues
        document.location.reload(true);
        clearInterval(job_status_timer);
        return;
      }
      // show the correct message for the job status
      substitute_classes($("message"), 
        ["expired", "pending", "active", "suspended", "failed", "done", "unknown"], [job_status]);
      if (job_url != null) update_preview(job_url);
      if (stop) {
        clearInterval(job_status_timer);
        return;
      }
    }
    // query server load and probablistically modify the interval
    var load = parseFloat(doc.getElementsByTagName("load")[0].textContent);
    var interval_elems = doc.getElementsByTagName("interval");
    var average_interval = (interval_elems.length > 0 ? parseInt(interval_elems[0].textContent, 10) : null);
    // output server load to log
    if (window.console && window.console.log) {
      console.log("Server Load: " + (load * 100).toFixed(1) + "% Average Interval: " + average_interval);
    }
    // randomly generate modification
    var modifier = Math.round(5000 * (load - Math.random()));
    // disallow moving away from the average when more than 5 seconds away
    // already even if server load would otherwise demand it
    if (!(average_interval != null && (
            (modifier > 0 && job_status_interval - average_interval > 5000) ||
            (modifier < 0 && average_interval - job_status_interval > 5000)))) {
      // output interval modification to log
      if (window.console && window.console.log) {
        console.log("Interval: " + job_status_interval + (modifier > 0 ? "+" : "") + modifier);
      }
      job_status_interval += modifier;
      if (job_status_interval < job_status_min) job_status_interval = job_status_min;
      clearInterval(job_status_timer);
      job_status_timer = setInterval(query_status, job_status_interval);
    }
  }, false);
  request.open("GET", url, true);
  request.send();
}

function page_load() {
  resize_preview();
  var message = $("message");
  if (message != null && /\b(pending|active|suspended)\b/.test(message.className)) {
    job_status_timer = setInterval(query_status, job_status_interval);
  }
}

window.addEventListener('load', page_load, false);
window.addEventListener('resize',resize_preview, false); 
