
MK = MochiKit;

// Array.prototype.insert = function(i, v) {
//   this.splice(i, 0, v);
// };

// Array.prototype.remove = function(i) {
//   return this.splice(i, 1)[0];
// };


// Use this namespace to avoid polluting the global namespace.
var app = {

  object: function(id) {
    return getElement(id);
  },
  obj: function(id) {
    return getElement(id);
  },


  // Treat empty arrays, zeros, and empty strings as false.
  f: function(obj) {
    return obj == '' || obj == 0 || obj == null || obj == undefined ||
           (typeof(obj) == 'object' && MK.Base.keys(obj).length == 0);
  },

  t: function(obj) {
    return !app.f(obj);
  },


  node_attr: function(node, attr, default_val) {
    if (typeof(node) == 'string') {
      node = app.obj(node);
    }
    return (x = node && node.getAttributeNode(attr)) ? x.nodeValue : default_val;
  },


  nodes: function(opts) {
    return MK.DOM.getElementsByTagAndClassName(opts['tag'], opts['class'], opts['parent']);
  },


  event_target: function(e) {
    return YAHOO.util.Event.getTarget(e);
  },


  trap_enter: function(e, fn) {
    //var key = YAHOO.util.Event.getCharCode(e);
    var key;
    try {
      key = e.keyCode;
    }
    catch(ex) {
      key = window.event.keyCode;
    }
    if (key == 13) {
      fn();
    }
    return key != 13;
  },


  request: function(url, params, opts) {
    var scope = {};
    if (!params) {
      params = {};
    }
    if (!opts) {
      opts = {};
    }

    if (opts.scope) {
      scope = opts.scope;
    }
    else {
      if (opts.success) {
        scope.success = opts.success;
      }
      if (opts.failure) {
        scope.failure = opts.failure;
      }
    }
    var method = 'POST';
    if (opts.method) {
      method = opts.method;
    }
    return YAHOO.util.Connect.asyncRequest(method, url, scope, MK.Base.queryString(params));
  },


  ajax: function(url, params, callback) {
    wrapper = function(reply) {
      try {
        data = MK.Base.evalJSON(reply.responseText);
      }
      catch (error) {
        return;
      }
      callback(data);
    };
    app.request(url, params, {success: wrapper});
  },


  // Set given node's contents to results of tpl.process(data).
  draw_jst: function(node, tpl, data) {
    app.obj(node).innerHTML = TrimPath.processDOMTemplate(tpl, data);
  },


  first_defined: function(items) {
    for (i in items) {
      if (items[i] != undefined) {
        return items[i];
      }
    }
    return undefined;
  },


  show: function(obj, visible) {
    if (visible) {
      app.make_visible(obj);
    }
    else {
      app.make_invisible(obj);
    }
  },

  toggle_visible: function(elem) {
    MK.DOM.toggleElementClass("invisible", elem);
  },

  make_visible: function(elem) {
    MK.DOM.removeElementClass(elem, "invisible");
    if (MK.Style.getStyle(elem, 'display') == 'none') {
      MK.Style.setStyle(elem, {'display': 'block'});
    }
  },

  make_invisible: function(elem) {
    MK.DOM.addElementClass(elem, "invisible");
  },

  is_visible: function(elem) {
    if (MK.DOM.hasElementClass(elem, "invisible")) {
      return false;
    }
    if (MK.Style.getStyle(elem, 'display') == "none") {
      return false;
    }
    return true;
  },


  submit: function(form, url) {
    if (typeof(form) == 'string') {
      if (document[form]) {
        form = document[form];
      }
      else {
        form = app.object(form);
      }
    }
    form.action = url;
    form.submit();
  },


  formval: function(form, field) {
    if (typeof(form) == 'string') {
      form = app.first_defined([document[form], app.object(form)]);
    }
    var ctrl = form[field];
    if (!ctrl) {
      return null;
    }

    if (ctrl.type) {
      return app.ctrlval(ctrl);
    }
    else {
      // Assume it's an Array
      var vals = new Array();
      for (i=0; i < ctrl.length; i++) {
        var x = app.ctrlval(ctrl[i]);
        if (x != null) {
          vals.push(x);
        }
      }
      return ctrl[0].type == 'radio' ? vals[0] : vals;
    }
  },

  ctrlval: function(ctrl) {
    if (typeof(ctrl) == 'string') {
      ctrl = app.object(ctrl);
    }
    switch (ctrl.type) {
      case "select-one":
        return ctrl.selectedIndex == -1 ? null : ctrl.options[ctrl.selectedIndex].value;
      case "select-multiple":
        vals = new Array();
        for (opt in ctrl.options) {
          if (opt.selected) {
            vals = vals.concat(new Array(opt.value));
          }
        }
        return vals;
      case "checkbox":
      case "radio":
        return ctrl.checked ? ctrl.value : null;
      case "text":
      case "textarea":
      case "password":
      case "hidden":
        return ctrl.value;
      default:
        return null;
    }
  },

  // Addressing lameness in Select interface.

  option_by: function(list, key, val) {
    // Return first Option that matches given key ('selected', 'value', 'text').
    if (typeof(list) == 'string') {
      list = app.obj(list);
    }
    if (key == 'selected') {
      return list.options[list.selectedIndex];
    }
    else {
      return MK.Iter.ifilter(function (o) { return o[key] == val }, list.options).next();
    }
  },

  option_add: function(list, option, idx) {
    // Add given Option to given Select at given point.  If no idx
    // supplied, then append to end of option list.
    if (idx == null) {
      idx = list.length;
    }
    try {
      list.options.add(option, list.options[idx]);
    }
    catch (e) {
      try {
        list.add(option, idx)
      }
      catch (e) {
        // For Safari.  Always appends.  Should use DOM function instead?
        list.options.add(option, idx);
      }
    }
  },

  option_remove: function(list, idx) {
    list.remove(idx);
  },


  set_all: function(ctrls, checked) {
    if (ctrls.length) {
      for (i=0; i < ctrls.length; i++) {
        if (!ctrls[i].disabled) {
          ctrls[i].checked = checked;
        }
      }
    }
    else {
      if (!ctrls.disabled) {
        ctrls.checked = checked;
      }
    }
  },

  set_ranges: function(ctrls) {
    if (!ctrls.length) {
      return;
    }
    var in_range = false;
    for (i=0; i < ctrls.length; i++) {
      if (ctrls[i].checked) {
        in_range = !in_range;
        if (!ctrls[i].disabled) {
          ctrls[i].checked = true;
        }
      }
      else {
        if (!ctrls[i].disabled) {
          ctrls[i].checked = in_range;
        }
      }
    }
  },


  elements_by_tag: function(search, parent, filter) {
    if (!search.length) {
      search = search.split(',');
    }
    if (!parent) {
      parent = document;
    }
    if (!filter) {
      filter = function(i) {return true;};
    }
    var results = new Array();
    for (var i=0; i < search.length; i++) {
      var tags = parent.getElementsByTagName(search[i]);
      for (var j=0; j < tags.length; j++) {
        results.push(tags[j]);
      }
    }
    var test_node = results[0];
    if (!test_node) {
      return [];
    }
    if (test_node.sourceIndex) {
      results.sort(function(a,b) {return a.sourceIndex - b.sourceIndex;});
    }
    else if (test_node.compareDocumentPosition) {
      results.sort(function(a,b) {return 3 - (a.compareDocumentPosition(b) & 6);});
    }
    return results;
  },


  to_url: function(params) {
    // Return given name/val pairs as a URL query string.
    var url = '';
    for (name in params) {
      if (url) {
        url += '&';
      }
      url += name + '=' + params[name];
    }
    return encodeURI(url);
  },


  ok_to_report: function(submit_form, input_form) {
    var ids = strip(app.formval(input_form, 'ids').join(' '));
    if (pg.queued) {
      if (pg.queued.length > 0) {
        ids += ' ' + pg.queued.join(' ');
      }
    }
    submit_form.ids.value = ids;
    submit_form.group_id.value = app.formval(input_form, 'group_id');
    if (!submit_form.ids.value) {
      alert('Please check one or more hosts to report.');
      return false;
    };
    return true;
  },


  scroll_height: function() {
    var y;
    if (self.pageYOffset) {
      y = self.pageYOffset;
    }
    else if (document.documentElement && document.documentElement.scrollTop) {
      y = document.documentElement.scrollTop;
    }
    else if (document.body) {
      y = document.body.scrollTop;
    }
    return parseInt(y) + app.window_height();
  },


  window_height: function() {
    var height;
    if (self.innerHeight) {
      height = self.innerHeight;
    }
    else if (document.documentElement && document.documentElement.clientHeight) {
      height = document.documentElement.clientHeight;
    }
    else if (document.body) {
      height = document.body.clientHeight;
    }
    return parseInt(height);
  },


  hilite: function(element, klass) {
    if (!klass) {
      klass = 'hilite';
    }
    MK.DOM.addElementClass(element, klass);
  },

  unhilite: function(element, klass) {
    if (!klass) {
      klass = 'hilite';
    }
    MK.DOM.removeElementClass(element, klass);
  },


  Panel: function(id, options) {
    if (!options) {
      options = {};
    }
    
    var defaults = { 
      fixedcenter          : false,
      constraintoviewport  : true,
      visible              : false,
      modal                : false,
      effect               : {effect: YAHOO.widget.ContainerEffect.FADE, duration: 0.2},
      zindex               : 10
    };
    app.Panel.superclass.constructor.call(this, id, merge(defaults, options));

    var body = options.body;
    var title = options.title;
    if (!body) {
      body = app.obj(id + '-bd');
      app.make_visible(body);
      if (!title) {
        title = app.node_attr(body, 'title');
      }
    }
    delete options.body;
    delete options.title;

    this.setTitle(title);
    this.setBody(body);
    this.render(document.body);

    var esc_key = new YAHOO.util.KeyListener(document, {keys: 27}, MK.Base.bind(this.hide, this));
    esc_key.enable();
  },

  show_cmds: function(body, opts) {
    if (!app.cmd_panels) {
      app.cmd_panels = {};
    }
    if (!opts) {
      opts = {};
    }
    if (opts.event) {
      var target = YAHOO.util.Event.getTarget(opts.event);
      if (/^(a|div|span|td|p)$/i.test(target.tagName)) {
        if (!app.cmd_panels[body]) {
          if (opts.params) {
            opts.params.zindex = 9998;
          }
          else {
            opts.params = {zindex: 9998};
          }
          var panel = new app.Panel(body, opts.params);
          panel.body.onclick = function(e) { app.cmd_panels[body].hide(); }
          app.cmd_panels[body] = panel;
        }
        app.cmd_panels[body].show(opts);
      }
    }
  }
};

YAHOO.lang.extend(app.Panel, YAHOO.widget.Panel);

app.Panel.prototype.setTitle = function(title) {
  this.setHeader('<div class="title">' + title + '</div>');
};

app.Panel.prototype.show = function(opts) {
  if (!opts) {
    opts = {};
  }
  if (opts.event) {
    this.cfg.setProperty('xy', YAHOO.util.Event.getXY(opts.event));
  }
  app.Panel.superclass.show.call(this);
  try {
    var nodes = MK.DOM.getElementsByTagAndClassName(null, null, this.element);
    for (i=0; i < nodes.length; i++) {
      if (app.node_attr(nodes[i], 'tabindex')) {
        nodes[i].focus();
        break;
      }
    }
  }
  catch (e) {
    // Can't set focus before panel is visible in IE. Cannot find a
    // method in YUI that fires after show event in Panel.
  }
};


app.FormSubmission = function(form, opts) {

  this.submit = function() {
    if (this.jerky) {
      return;
    }
    this.jerky = true;
    MK.Async.callLater(3, MK.Base.bind(this.enable, this));

    this.wait = new app.Panel('wait-' + form, {zindex: 9999, fixedcenter: true, title: 'Processing...', body: '<center><img src="/images/progress.gif" width="180px" /></center>'});
    this.wait.show();
    this.form = app.obj(form);

    YAHOO.util.Connect.setForm(this.form, app.node_attr(this.form, 'isupload'));
    this.req = YAHOO.util.Connect.asyncRequest('POST', this.url, this);
  };
 
  this.enable = function() {
    this.jerky = false;
  };


  this.success = function(reply) {
    this.wait.hide();
    delete this.wait;
    try {
      this.reply = MK.Base.evalJSON(reply.responseText);
    }
    catch (error) {
      // console.log("Bad reply from server: %o : %s", error, reply.responseText);
      return;
    }
    this.process_errors(this.reply.errors);
    this.process_results(this.reply);
  };
  this.upload = this.success;

  this.failure = function(reply) {
    // console.log('Failed form submission: %1.o', reply);
  };  

  
  this.process_results = function(reply) {
    // console.log('%1.o', reply);
  };


  this.process_errors = function(errors) {
    var rm_class = function(label) {
      MK.DOM.removeElementClass(label, 'error');
    }
    MK.Iter.forEach(this.erred_fields, rm_class);
    this.erred_fields = [];
    this.errors = {};
    if (!errors) {
      return;
    }
    for (field in errors) {
      this.errors[field] = errors[field];
      var ctrl = this.form[field];
      var label = app.obj(field + '-label');
      if (!label) {
        label = getElementsByTagAndClassName(null, 'field', ctrl.parentNode)[0];
      }
      if (!label) {
        label = getElementsByTagAndClassName('legend', null, ctrl.parentNode)[0];
      }
      label.onclick = bind('show_errors_for_field', this, field);
      MK.DOM.addElementClass(label, 'error');
      this.erred_fields.push(label);
    }
    app.msg('<p>There were problems with the input.  Click the <span class="field error">field labels</span> to see what is wrong.</p>', {id: 'error-msg-' + this.form.id, title: 'Update Failed'});
  };


  this.show_errors_for_field = function(name, event) {
    var body = '<ul class="errors"><li>' + this.errors[name].join('</li><li>') + '</li></ul>';
    if (this.error_panel) {
      this.error_panel.setBody(body);
      this.error_panel.render(document.body);
    }
    else {
      this.error_panel = new app.Panel('errors-' + this.form.id, {zindex: 10000, title: 'Input Errors', body: body, fixedcenter: true});
    }
    this.error_panel.show({event: event});
  };

  // Constructor code
  if (!opts) {
    opts = {};
  }
  if (opts.url) {
    this.url = opts.url;
  }
  else {
    this.url = form + '.cgi';
  }
  this.erred_fields = [];

  if (opts.on_results) {
    this.process_results = opts.on_results;
  }
  if (opts.on_errors) {
    this.process_errors = opts.on_errors;
  }
};

app.submit_form = function(form, opts) {
  if (!app.form_submissions) {
    app.form_submissions = {};
  }
  if (!app.form_submissions[form]) {
    app.form_submissions[form] = new app.FormSubmission(form, opts);
  }
  app.form_submissions[form].submit();
  return false;
};


app.msg = function(msg, opts) {
  if (!opts) {
    opts = {};
  }
  if (!opts.id) {
    opts.id = msg;
  }
  if (!opts.title) {
    opts.title = 'Message';
  }
  if (el = app.obj(msg + '-bd')) {
    msg = null;
    opts.title = el.title;
  }
  var panel = new app.Panel(opts.id, {body: msg, title: opts.title, fixedcenter: true, zindex: 9999, modal: opts.modal});
  panel.show();
  if (opts.timeout == null || opts.timeout > 0) {
    MK.Async.callLater(opts.timeout || 3, MK.Base.bind(panel.hide, panel));
  }
};

app.wait = function(title) {
  if (!app.wait_form) {
    if (!title) {
      title = 'Processing';
    }
    app.wait_form = new app.Panel('app-wait', {fixedcenter: true, title: title, body: '<center><img src="/images/progress.gif" width="180px" /></center>'});
  }
  app.wait_form.show();
};

app.stop_waiting = function() {
  app.wait_form.hide();
  delete app.wait_form;
};

// Replace give node's contents with our progess meter.
app.processing = function(node) {
  app.obj(node).innerHTML = '<center><img src="/images/progress.gif" /></center>';
};


app.auto_completion = function(input_el, results_el, ds, opts) {
  var defaults = {
    allowBrowserAutocomplete  : false,
    minQueryLength            : 0,
    autoHighlight             : true,
    queryDelay                : 1.0,
    // useIFrame                 : true,
    typeAhead                 : true,
    queryMatchSubset          : true,
    maxCacheEntries           : 100
  };
  return new YAHOO.widget.AutoComplete(input_el, results_el, ds, merge(defaults, opts));
};


if (TrimPath) {
  TrimPath.parseTemplate_etc.modifierDef = merge
    (TrimPath.parseTemplate_etc.modifierDef,
     {
     strftime: function(ts, format) {
         return (new Date(1000 * ts)).format(format);
       }
     });
};


// Local Variables:
//   mode              : javascript
//   font-lock-mode    : t
//   tab-width         : 2
//   indent-tabs-mode  : nil
// End:
