Source: Source.js

/** A data source
 * @constructor
 */
hui.ui.Source = function(options) {
  this.options = hui.override({url:null,parameters:[],lazy:false},options);
  this.name = options.name;
  this.data = null;
  this.parameters = [];
    // Clone parameters so they can be reused
    for (var i = 0; i < this.options.parameters.length; i++) {
      var p = this.options.parameters[i];
      this.parameters.push({key:p.key,value:p.value,separate:p.separate});
    }
  hui.ui.extend(this);
  if (options.delegate) {
    this.listen(options.delegate);
  }
  this.initial = true;
  this.busy = false;
  hui.ui.onReady(this._init.bind(this));
};

hui.ui.Source.prototype = {
  _init : function() {
    var self = this;
    hui.each(this.parameters,function(parm) {
      var val = hui.ui.bind(parm.value,function(value) {
        self.changeParameter(parm.key,value);
      });
      parm.value = self._convertValue(val);
    });
    if (!this.options.lazy) {
      this.refresh();
    }
  },
  _convertValue : function(value) {
    if (value && value.getTime) {
      return value.getTime();
    }
    return value;
  },
  refreshFirst : function() {
    if (this.initial) {
      this.refresh();
    }
  },
  /** Will refresh, but wait a little to let others contribute */
  refreshLater : function() {
    window.clearTimeout(this.paramDelay);
    this.paramDelay = window.setTimeout(function() {
      this.refresh();
    }.bind(this),100);
  },

  /** Refreshes the data source */
  refresh : function() {
    if (this.options.delay<1) {
      this._refresh();
    } else {
      window.clearTimeout(this._refreshDelay);
      this._refreshDelay = window.setTimeout(this._refresh.bind(this),this.options.delay);
    }
  },
  _refresh : function() {
    if (this.delegates.length===0) {
      return;
    }
    var shouldRefresh = this.delegates.length === 0;
    for (var i=0; i < this.delegates.length; i++) {
      var d = this.delegates[i];
      if (d.$sourceShouldRefresh) {
        shouldRefresh = shouldRefresh || d.$sourceShouldRefresh();
      } else {
        shouldRefresh = true;
      }
    }
    if (!shouldRefresh) {return;}
    if (this.busy) {
      this.pendingRefresh = true;
      // It might be better to cue rather than abort
      //if (this.transport) {
      //  this.transport.abort();
      //}
      return;
    }
    this.initial = false;
    this.pendingRefresh = false;
    var self = this;
    if (this.options.url) {
      var prms = [];
      for (var j=0; j < this.parameters.length; j++) {
        var p = this.parameters[j];
        if (hui.isArray(p.value) && p.separate) {
          for (var k = 0; k < p.value.length; k++) {
            prms.push({
              name : p.key,
              value : p.value[k]
            });
          }
        } else {
          prms.push({name : p.key, value : p.value});
        }
      }
      this.busy = true;
      hui.ui.callDelegates(this,'sourceIsBusy');
      this.transport = hui.request({
        method : 'POST',
        url : this.options.url,
        parameters : prms,
        $success : this._parse.bind(this),
        $exception : function(e,t) {
          hui.log('Exception while loading source...');
          hui.log(e);
          self._end();
        },
        $forbidden : function() {
          hui.ui.handleForbidden(self);
          hui.ui.callDelegates(self,'sourceFailed');
          self._end();
        },
        $failure : function(t) {
          hui.log('sourceFailed');
          hui.ui.callDelegates(self,'sourceFailed');
          self._end();
        }
      });
    }
  },
  _end : function() {
    this.busy = false;
    hui.ui.callDelegates(this,'sourceIsNotBusy');
    if (this.pendingRefresh) {
      this.refresh();
    }
  },
  _parse : function(t) {
    if (hui.request.isXMLResponse(t)) {
      this._parseXML(t.responseXML);
    } else {
      var str = t.responseText.replace(/^\s+|\s+$/g, ''),
        json = null;
      if (str.length>0) {
        json = hui.string.fromJSON(t.responseText);
      }
      this.fire('objectsLoaded',json);
    }
    this._end();
  },
  _parseXML : function(doc) {
    var root = doc.documentElement.tagName;
    if (root=='items' || root=='options') {
      this.data = this._parseItems(doc);
      this.fire('optionsLoaded',this.data);
    } else if (root=='list') {
      this.fire('listLoaded',doc);
    } else if (root=='articles') {
      this.fire('articlesLoaded',doc);
    }
  },
  _parseItems : function(doc) {
    var root = doc.documentElement;
    var out = [];
    this._parseSubItems(root,out);
    return out;
  },
  _parseSubItems : function(parent,array) {
    var children = parent.childNodes;
    for (var i=0; i < children.length; i++) {
      var node = children[i];
      if (node.nodeType==1 && node.nodeName=='title') {
        array.push({title:node.getAttribute('title'),type:'title'});
      } else if (node.nodeType==1 && (node.nodeName=='item' || node.nodeName=='option')) {
        var sub = [];
        this._parseSubItems(node,sub);
        array.push({
          text : node.getAttribute('text'),
          title : node.getAttribute('title'),
          value : node.getAttribute('value'),
          icon : node.getAttribute('icon'),
          kind : node.getAttribute('kind'),
          badge : node.getAttribute('badge'),
          children : sub
        });
      }
    }
  },
  addParameter : function(parm) {
    this.parameters.push(parm);
    var val = hui.ui.bind(parm.value,function(value) {
      this.changeParameter(parm.key,value);
    }.bind(this));
    parm.value = this._convertValue(val);
  },
  setParameter : function(key,value) {
    value = this._convertValue(value);
    for (var i=0; i < this.parameters.length; i++) {
      var p = this.parameters[i];
      if (p.key==key) {
        p.value=value;
        return;
      }
    }
    this.parameters.push({key:key,value:value});
  },
  changeParameter : function(key,value) {
    value = this._convertValue(value);
    for (var i=0; i < this.parameters.length; i++) {
      var p = this.parameters[i];
      if (p.key==key) {
        p.value=value;
      }
    }
    this.refreshLater();
  }
};