(function($){
  
  window.initializeFilters = function() {
  
    // Bail early for Konqueror and IE5.2/Mac, which don't fully support dynamic
    // creation of form controls
    try {
      var test = document.createElement("input");
      test.type = "button";
      if (test.type != "button") throw Error();
    } catch (e) {
      return;
    }
  
    // Removes an existing row from the filters table
    function removeRow(button, propertyName) {
      var tr = getAncestorByTagName(button, "tr");
  
      var mode = null;
      var selects = tr.getElementsByTagName("select");
      for (var i = 0; i < selects.length; i++) {
        if (selects[i].name == propertyName + "_mode") {
          mode = selects[i];
          break;
        }
      }
      if (mode && (getAncestorByTagName(mode, "tr") == tr)) {
        // Check whether there are more 'or' rows for this filter
        var next = tr.nextSibling;
        if (next && (next.className == propertyName)) {
          function getChildElementAt(e, idx) {
            e = e.firstChild;
            var cur = 0;
            while (cur <= idx) {
              while (e && e.nodeType != 1) e = e.nextSibling;
              if (cur++ == idx) break;
              e = e.nextSibling;
            }
            return e;
          }
  
          var thisTh = getChildElementAt(tr, 0);
          var nextTh = getChildElementAt(next, 0);
          next.insertBefore(thisTh, nextTh);
          nextTh.colSpan = 1;
  
          thisTd = getChildElementAt(tr, 0);
          nextTd = getChildElementAt(next, 1);
          next.replaceChild(thisTd, nextTd);
        }
      }
  
      var tBody = tr.parentNode;
      tBody.deleteRow(tr.sectionRowIndex);
      if (!tBody.rows.length) {
          tBody.parentNode.removeChild(tBody);
      }
      
      if (propertyName) {
        var select = document.forms["query"].elements["add_filter"];
        for (var i = 0; i < select.options.length; i++) {
          var option = select.options[i];
          if (option.value == propertyName) option.disabled = false;
        }
      }
    }
  
    // Initializes a filter row, the 'input' parameter is the submit
    // button for removing the filter
    function initializeFilter(input) {
      var removeButton = document.createElement("input");
      removeButton.type = "button";
      removeButton.value = input.value;
      if (input.name.substr(0, 10) == "rm_filter_") {
        removeButton.onclick = function() {
          var endIndex = input.name.search(/_\d+$/);
          if (endIndex < 0) endIndex = input.name.length;
          removeRow(removeButton, input.name.substring(10, endIndex));
          return false;
        }
      } else {
        removeButton.onclick = function() {
          removeRow(removeButton);
          return false;
        }
      }
      input.parentNode.replaceChild(removeButton, input);
    }
  
    // Make the submit buttons for removing filters client-side triggers
    var filters = document.getElementById("filters");
    var inputs = filters.getElementsByTagName("input");
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      if (input.type == "submit" && input.name
       && input.name.match(/^rm_filter_/)) {
        initializeFilter(input);
      }
    }
  
    // Make the drop-down menu for adding a filter a client-side trigger
    var addButton = document.forms["query"].elements["add"];
    addButton.parentNode.removeChild(addButton);
    var select = document.getElementById("add_filter");
    select.onchange = function() {
      if (select.selectedIndex < 1) return;
  
      if (select.options[select.selectedIndex].disabled) {
        // Neither IE nor Safari supported disabled options at the time this was
        // written, so alert the user
        alert("A filter already exists for that property");
        return;
      }
  
      // Convenience function for creating a <label>
      function createLabel(text, htmlFor) {
        var label = document.createElement("label");
        if (text) label.appendChild(document.createTextNode(text));
        if (htmlFor) label.htmlFor = htmlFor;
        return label;
      }
  
      // Convenience function for creating an <input type="checkbox">
      function createCheckbox(name, value, id) {
        var input = document.createElement("input");
        input.type = "checkbox";
        if (name) input.name = name;
        if (value) input.value = value;
        if (id) input.id = id;
        return input;
      }
  
      // Convenience function for creating an <input type="radio">
      function createRadio(name, value, id) {
        var str = '<input type="radio"';
        if (name) str += ' name="' + name + '"';
        if (value) str += ' value="' + value + '"';
        if (id) str += ' id="' + id + '"'; 
        str += '/>';
        var span = document.createElement('span');
        // create radio button with innerHTML to avoid IE mangling it.
        span.innerHTML = str; 
        return span;
      }
  
      // Convenience function for creating a <select>
      function createSelect(name, options, optional) {
        var e = document.createElement("select");
        if (name) e.name = name;
        if (optional) e.options[0] = new Option();
        if (options) {
          for (var i = 0; i < options.length; i++) {
            var option;
            if (typeof(options[i]) == "object") {
              option = new Option(options[i].text, options[i].value);
            } else {
              option = new Option(options[i], options[i]);
            }
            e.options[e.options.length] = option;
          }
        }
        return e;
      }
  
      var propertyName = select.options[select.selectedIndex].value;
      var property = properties[propertyName];
      var table = document.getElementById("filters").getElementsByTagName("table")[0];
      var tr = document.createElement("tr");
      tr.className = propertyName;
  
      var alreadyPresent = false;
      for (var i = 0; i < table.rows.length; i++) {
        if (table.rows[i].className == propertyName) {
          var existingTBody = table.rows[i].parentNode;
          alreadyPresent = true;
          break;
        }
      }
  
      // Add the row header
      var th = document.createElement("th");
      th.scope = "row";
      if (!alreadyPresent) {
        th.appendChild(createLabel(property.label));
      } else {
        th.colSpan = 2;
        th.appendChild(createLabel("or"));
      }
      tr.appendChild(th);
  
      var td = document.createElement("td");
      if (property.type == "radio" || property.type == "checkbox") {
        td.colSpan = 2;
        td.className = "filter";
        if (property.type == "radio") {
          for (var i = 0; i < property.options.length; i++) {
            var option = property.options[i];
            td.appendChild(createCheckbox(propertyName, option,
              propertyName + "_" + option));
            td.appendChild(createLabel(option ? option : "none",
              propertyName + "_" + option));
          }
        } else {
          td.appendChild(createRadio(propertyName, "1", propertyName + "_on"));
          td.appendChild(document.createTextNode(" "));
          td.appendChild(createLabel("yes", propertyName + "_on"));
          td.appendChild(createRadio(propertyName, "0", propertyName + "_off"));
          td.appendChild(document.createTextNode(" "));
          td.appendChild(createLabel("no", propertyName + "_off"));
        }
        tr.appendChild(td);
      } else {
        if (!alreadyPresent) {
          // Add the mode selector
          td.className = "mode";
          var modeSelect = createSelect(propertyName + "_mode",
                                        modes[property.type]);
          td.appendChild(modeSelect);
          tr.appendChild(td);
        }
  
        // Add the selector or text input for the actual filter value
        td = document.createElement("td");
        td.className = "filter";
        if (property.type == "select") {
          var element = createSelect(propertyName, property.options, true);
        } else if (property.type == "text") {
          var element = document.createElement("input");
          element.type = "text";
          element.name = propertyName;
          element.size = 42;
        }
        td.appendChild(element);
        element.focus();
        tr.appendChild(td);
      }
  
      // Add the add and remove buttons
      td = document.createElement("td");
      td.className = "actions";
      var removeButton = document.createElement("input");
      removeButton.type = "button";
      removeButton.value = "-";
      removeButton.onclick = function() { removeRow(removeButton, propertyName) };
      td.appendChild(removeButton);
      tr.appendChild(td);
  
      if (alreadyPresent) {
        existingTBody.appendChild(tr);
      } else {
        // Find the insertion point for the new row. We try to keep the filter rows
        // in the same order as the options in the 'Add filter' drop-down, because
        // that's the order they'll appear in when submitted.
        var insertionPoint = getAncestorByTagName(select, "tbody");
        outer: for (var i = select.selectedIndex + 1; i < select.options.length; i++) {
          for (var j = 0; j < table.tBodies.length; j++) {
            if (table.tBodies[j].rows[0].className == select.options[i].value) {
              insertionPoint = table.tBodies[j];
              break outer;
            }
          }
        }
        // Finally add the new row to the table
        var tbody = document.createElement("tbody");
        tbody.appendChild(tr);
        insertionPoint.parentNode.insertBefore(tbody, insertionPoint);
      }
  
      // Disable the add filter in the drop-down list
      if (property.type == "radio" || property.type == "checkbox") {
        select.options[select.selectedIndex].disabled = true;
      }
      select.selectedIndex = 0;
    }
  }

})(jQuery);