<commit_before>UI = {};

// A very basic operation like Underscore's `_.extend` that
// copies `src`'s own, enumerable properties onto `tgt` and
// returns `tgt`.
_extend = function (tgt, src) {
  for (var k in src)
    if (src.hasOwnProperty(k))
      tgt[k] = src[k];
  return tgt;
};

// Defines a single non-enumerable, read-only property
// on `tgt`.
// It won't be non-enumerable in IE 8, so its
// non-enumerability can't be relied on for logic
// purposes, it just makes things prettier in
// the dev console.
var _defineNonEnum = function (tgt, name, value) {
  try {
    Object.defineProperty(tgt, name, {value: value});
  } catch (e) {
    // IE < 9
    tgt[name] = value;
  }
  return tgt;
};

// Named function (like `function Component() {}` below) make
// inspection in debuggers more descriptive. In IE, this sets the
// value of the `Component` var in the function scope in which it's
// executed. We already have a top-level `Component` var so we create
// a new function scope to not write it over in IE.
(function () {

  // Components and Component kinds are the same thing, just
  // objects; there are no constructor functions, no `new`,
  // and no `instanceof`.  A Component object is like a class,
  // until it is inited, at which point it becomes more like
  // an instance.
  //
  // `y = x.extend({ ...new props })` creates a new Component
  // `y` with `x` as its prototype, plus additional properties
  // on `y` itself.  `extend` is used both to subclass and to
  // create instances (and the hope is we can gloss over the
  // difference in the docs).
  UI.Component = (function (constr) {

    // Make sure the "class name" that Chrome infers for
    // UI.Component is "Component", and that
    // `new UI.Component._constr` (which is what `extend`
    // does) also produces objects whose inferred class
    // name is "Component".  Chrome's name inference rules
    // are a little mysterious, but a function name in
    // the source code (as in `function Component() {}`)
    // seems to be reliable and high precedence.
    var C = new constr;
    _defineNonEnum(C, '_constr', constr);
    _defineNonEnum(C, '_super', null);
    return C;
  })(function Component() {});
})();

_extend(UI, {
  nextGuid: 2, // Component is 1!

  isComponent: function (obj) {
    return obj && UI.isKindOf(obj, UI.Component);
  },
  // `UI.isKindOf(a, b)` where `a` and `b` are Components
  // (or kinds) asks if `a` is or descends from
  // (transitively extends) `b`.
  isKindOf: function (a, b) {
    while (a) {
      if (a === b)
        return true;
      a = a._super;
    }
    return false;
  },
  // use these to produce error messages for developers
  // (though throwing a more specific error message is
  // even better)
  _requireNotDestroyed: function (c) {
    if (c.isDestroyed)
      throw new Error("Component has been destroyed; can't perform this operation");
  },
  _requireInited: function (c) {
    if (! c.isInited)
      throw new Error("Component must be inited to perform this operation");
  },
  _requireDom: function (c) {
    if (! c.dom)
      throw new Error("Component must be built into DOM to perform this operation");
  }
});

Component = UI.Component;

_extend(UI.Component, {
  kind: "Component",
  guid: "1",
  dom: null,
  // Has this Component ever been inited?
  isInited: false,
  // Has this Component been destroyed?  Only inited Components
  // can be destroyed.
  isDestroyed: false,
  // Component that created this component (typically also
  // the DOM containment parent).
  // No child pointers (except in `dom`).
  parent: null,

  // create a new subkind or instance whose proto pointer
  // points to this, with additional props set.
  extend: function (props) {
    // this function should never cause `props` to be
    // mutated in case people want to reuse `props` objects
    // in a mixin-like way.

    if (this.isInited)
      // Disallow extending inited Components so that
      // inited Components don't inherit instance-specific
      // properties from other inited Components, just
      // default values.
      throw new Error("Can't extend an inited Component");

    var constr;
    var constrMade = false;
    if (props && props.kind) {
      // If `kind` is different from super, set a constructor.
      // We used to set the function name here so that components
      // printed better in the console, but we took it out because
      // of CSP (and in hopes that Chrome finally adds proper
      // displayName support).
      constr = function () {};
      constrMade = true;
    } else {
      constr = this._constr;
    }

    // We don't know where we're getting `constr` from --
    // it might be from some supertype -- just that it has
    // the right function name.  So set the `prototype`
    // property each time we use it as a constructor.
    constr.prototype = this;

    var c = new constr;
    if (constrMade)
      c._constr = constr;

    if (props)
      _extend(c, props);

    // for efficient Component instantiations, we assign
    // as few things as possible here.
    _defineNonEnum(c, '_super', this);
    c.guid = String(UI.nextGuid++);

    return c;
  }
});

//callChainedCallback = function (comp, propName, orig) {
  // Call `comp.foo`, `comp._super.foo`,
  // `comp._super._super.foo`, and so on, but in reverse
  // order, and only if `foo` is an "own property" in each
  // case.  Furthermore, the passed value of `this` should
  // remain `comp` for all calls (which is achieved by
  // filling in `orig` when recursing).
//  if (comp._super)
//    callChainedCallback(comp._super, propName, orig || comp);
//
//  if (comp.hasOwnProperty(propName))
//    comp[propName].call(orig || comp);
//};

// Returns 0 if the nodes are the same or either one contains the other;
// otherwise, -1 if a comes before b, or else 1 if b comes before a in
// document order.
// Requires: `a` and `b` are element nodes in the same document tree.
var compareElementIndex = function (a, b) {
  // See http://ejohn.org/blog/comparing-document-position/
  if (a === b)
    return 0;
  if (a.compareDocumentPosition) {
    var n = a.compareDocumentPosition(b);
    return ((n & 0x18) ? 0 : ((n & 0x4) ? -1 : 1));
  } else {
    // Only old IE is known to not have compareDocumentPosition (though Safari
    // originally lacked it).  Thankfully, IE gives us a way of comparing elements
    // via the "sourceIndex" property.
    if (a.contains(b) || b.contains(a))
      return 0;
    return (a.sourceIndex < b.sourceIndex ? -1 : 1);
  }
};

findComponentWithProp = function (id, comp) {
  while (comp) {
    if (typeof comp[id] !== 'undefined')
      return comp;
    comp = comp.parent;
  }
  return null;
};

var findHelperHostComponent = function (comp) {
  while (comp) {
    if (comp.__helperHost) {
      return comp;
    }
    comp = comp.parent;
  }
  return null;
};

findComponentWithHelper = function (id, comp) {
  while (comp) {
    if (comp.__helperHost) {
      if (typeof comp[id] !== 'undefined')
        return comp;
      else
        return null;
    }
    comp = comp.parent;
  }
  return null;
};

getComponentData = function (comp) {
  comp = findComponentWithProp('data', comp);
  return (comp ?
          (typeof comp.data === 'function' ?
           comp.data() : comp.data) :
          null);
};

updateTemplateInstance = function (comp) {
  // Populate `comp.templateInstance.{firstNode,lastNode,data}`
  // on demand.
  var tmpl = comp.templateInstance;
  tmpl.data = getComponentData(comp);

  if (comp.dom && !comp.isDestroyed) {
    tmpl.firstNode = comp.dom.startNode().nextSibling;
    tmpl.lastNode = comp.dom.endNode().previousSibling;
    // Catch the case where the DomRange is empty and we'd
    // otherwise pass the out-of-order nodes (end, start)
    // as (firstNode, lastNode).
    if (tmpl.lastNode && tmpl.lastNode.nextSibling === tmpl.firstNode)
      tmpl.lastNode = tmpl.firstNode;
  } else {
    // on 'created' or 'destroyed' callbacks we don't have a DomRange
    tmpl.firstNode = null;
    tmpl.lastNode = null;
  }
};

_extend(UI.Component, {
  // We implement the old APIs here, including how data is passed
  // to helpers in `this`.
  helpers: function (dict) {
    _extend(this, dict);
  },
  events: function (dict) {
    var events;
    if (this.hasOwnProperty('_events'))
      events = this._events;
    else
      events = (this._events = []);

    _.each(dict, function (handler, spec) {
      var clauses = spec.split(/,\s+/);
      // iterate over clauses of spec, e.g. ['click .foo', 'click .bar']
      _.each(clauses, function (clause) {
        var parts = clause.split(/\s+/);
        if (parts.length === 0)
          return;

        var newEvents = parts.shift();
        var selector = parts.join(' ');
        events.push({events: newEvents,
                     selector: selector,
                     handler: handler});
      });
    });
  }
});

// XXX we don't really want this to be a user-visible callback,
// it's just a particular signal we need from DomRange.
UI.Component.notifyParented = function () {
  var self = this;
  for (var comp = self; comp; comp = comp._super) {
    var events = (comp.hasOwnProperty('_events') && comp._events) || null;
    if ((! events) && comp.hasOwnProperty('events') &&
        typeof comp.events === 'object') {
      // Provide limited back-compat support for `.events = {...}`
      // syntax.  Pass `comp.events` to the original `.events(...)`
      // function.  This code must run only once per component, in
      // order to not bind the handlers more than once, which is
      // ensured by the fact that we only do this when `comp._events`
      // is falsy, and we cause it to be set now.
      UI.Component.events.call(comp, comp.events);
      events = comp._events;
    }
    _.each(events, function (esh) { // {events, selector, handler}
      // wrap the handler here, per instance of the template that
      // declares the event map, so we can pass the instance to
      // the event handler.
      var wrappedHandler = function (event) {
        var comp = UI.DomRange.getContainingComponent(event.currentTarget);
        var data = comp && getComponentData(comp);
        var args = _.toArray(arguments);
        updateTemplateInstance(self);
        return Deps.nonreactive(function () {
          // put self.templateInstance as the second argument
          args.splice(1, 0, self.templateInstance);
          // Don't want to be in a deps context, even if we were somehow
          // triggered synchronously in an existing deps context
          // (the `blur` event can do this).
          // XXX we should probably do what Spark did and block all
          // event handling during our DOM manip.  Many apps had weird
          // unanticipated bugs until we did that.
          return esh.handler.apply(data === null ? {} : data, args);
        });
      };

      self.dom.on(esh.events, esh.selector, wrappedHandler);
    });
  }

  if (self.rendered) {
    // Defer rendered callback until flush time.
    Deps.afterFlush(function () {
      if (! self.isDestroyed) {
        updateTemplateInstance(self);
        self.rendered.call(self.templateInstance);
      }
    });
  }
};

// past compat
UI.Component.preserve = function () {
  Meteor._debug("The 'preserve' method on templates is now unnecessary and deprecated.");
};

// Gets the data context of the enclosing component that rendered a
// given element
UI.getElementData = function (el) {
  var comp = UI.DomRange.getContainingComponent(el);
  return comp && getComponentData(comp);
};

var jsUrlsAllowed = false;
UI._allowJavascriptUrls = function () {
  jsUrlsAllowed = true;
};
UI._javascriptUrlsAllowed = function () {
  return jsUrlsAllowed;
};

UI._templateInstance = function () {
  var currentComp = currentComponent.get();
  if (! currentComp) {
    throw new Error("You can only call UI._templateInstance() from within" +
                    " a helper function.");
  }

  // Find the enclosing component that is a template. (`currentComp`
  // could be, for example, an #if or #with, and we want the component
  // that is the surrounding template.)
  var template = findHelperHostComponent(currentComp);
  if (! template) {
    throw new Error("Current component is not inside a template?");
  }

  // Lazily update the template instance for this helper, and do it only
  // once.
  if (! currentTemplateInstance) {
    updateTemplateInstance(template);
    currentTemplateInstance = template.templateInstance;
  }
  return currentTemplateInstance;
};

// Returns the data context of the parent which is 'numLevels' above the
// component. Same behavior as {{../..}} in a template, with 'numLevels'
// occurrences of '..'.
UI._parentData = function (numLevels) {
  var component = currentComponent.get();
  while (component && numLevels >= 0) {
    // Decrement numLevels every time we find a new data context. Break
    // once we have reached numLevels < 0.
    if (component.data !== undefined && --numLevels < 0) {
      break;
    }
    component = component.parent;
  }

  if (! component) {
    return null;
  }

  return getComponentData(component);
};
<commit_msg>Add comment to `findHelperHostComponent`<commit_after>UI = {};

// A very basic operation like Underscore's `_.extend` that
// copies `src`'s own, enumerable properties onto `tgt` and
// returns `tgt`.
_extend = function (tgt, src) {
  for (var k in src)
    if (src.hasOwnProperty(k))
      tgt[k] = src[k];
  return tgt;
};

// Defines a single non-enumerable, read-only property
// on `tgt`.
// It won't be non-enumerable in IE 8, so its
// non-enumerability can't be relied on for logic
// purposes, it just makes things prettier in
// the dev console.
var _defineNonEnum = function (tgt, name, value) {
  try {
    Object.defineProperty(tgt, name, {value: value});
  } catch (e) {
    // IE < 9
    tgt[name] = value;
  }
  return tgt;
};

// Named function (like `function Component() {}` below) make
// inspection in debuggers more descriptive. In IE, this sets the
// value of the `Component` var in the function scope in which it's
// executed. We already have a top-level `Component` var so we create
// a new function scope to not write it over in IE.
(function () {

  // Components and Component kinds are the same thing, just
  // objects; there are no constructor functions, no `new`,
  // and no `instanceof`.  A Component object is like a class,
  // until it is inited, at which point it becomes more like
  // an instance.
  //
  // `y = x.extend({ ...new props })` creates a new Component
  // `y` with `x` as its prototype, plus additional properties
  // on `y` itself.  `extend` is used both to subclass and to
  // create instances (and the hope is we can gloss over the
  // difference in the docs).
  UI.Component = (function (constr) {

    // Make sure the "class name" that Chrome infers for
    // UI.Component is "Component", and that
    // `new UI.Component._constr` (which is what `extend`
    // does) also produces objects whose inferred class
    // name is "Component".  Chrome's name inference rules
    // are a little mysterious, but a function name in
    // the source code (as in `function Component() {}`)
    // seems to be reliable and high precedence.
    var C = new constr;
    _defineNonEnum(C, '_constr', constr);
    _defineNonEnum(C, '_super', null);
    return C;
  })(function Component() {});
})();

_extend(UI, {
  nextGuid: 2, // Component is 1!

  isComponent: function (obj) {
    return obj && UI.isKindOf(obj, UI.Component);
  },
  // `UI.isKindOf(a, b)` where `a` and `b` are Components
  // (or kinds) asks if `a` is or descends from
  // (transitively extends) `b`.
  isKindOf: function (a, b) {
    while (a) {
      if (a === b)
        return true;
      a = a._super;
    }
    return false;
  },
  // use these to produce error messages for developers
  // (though throwing a more specific error message is
  // even better)
  _requireNotDestroyed: function (c) {
    if (c.isDestroyed)
      throw new Error("Component has been destroyed; can't perform this operation");
  },
  _requireInited: function (c) {
    if (! c.isInited)
      throw new Error("Component must be inited to perform this operation");
  },
  _requireDom: function (c) {
    if (! c.dom)
      throw new Error("Component must be built into DOM to perform this operation");
  }
});

Component = UI.Component;

_extend(UI.Component, {
  kind: "Component",
  guid: "1",
  dom: null,
  // Has this Component ever been inited?
  isInited: false,
  // Has this Component been destroyed?  Only inited Components
  // can be destroyed.
  isDestroyed: false,
  // Component that created this component (typically also
  // the DOM containment parent).
  // No child pointers (except in `dom`).
  parent: null,

  // create a new subkind or instance whose proto pointer
  // points to this, with additional props set.
  extend: function (props) {
    // this function should never cause `props` to be
    // mutated in case people want to reuse `props` objects
    // in a mixin-like way.

    if (this.isInited)
      // Disallow extending inited Components so that
      // inited Components don't inherit instance-specific
      // properties from other inited Components, just
      // default values.
      throw new Error("Can't extend an inited Component");

    var constr;
    var constrMade = false;
    if (props && props.kind) {
      // If `kind` is different from super, set a constructor.
      // We used to set the function name here so that components
      // printed better in the console, but we took it out because
      // of CSP (and in hopes that Chrome finally adds proper
      // displayName support).
      constr = function () {};
      constrMade = true;
    } else {
      constr = this._constr;
    }

    // We don't know where we're getting `constr` from --
    // it might be from some supertype -- just that it has
    // the right function name.  So set the `prototype`
    // property each time we use it as a constructor.
    constr.prototype = this;

    var c = new constr;
    if (constrMade)
      c._constr = constr;

    if (props)
      _extend(c, props);

    // for efficient Component instantiations, we assign
    // as few things as possible here.
    _defineNonEnum(c, '_super', this);
    c.guid = String(UI.nextGuid++);

    return c;
  }
});

//callChainedCallback = function (comp, propName, orig) {
  // Call `comp.foo`, `comp._super.foo`,
  // `comp._super._super.foo`, and so on, but in reverse
  // order, and only if `foo` is an "own property" in each
  // case.  Furthermore, the passed value of `this` should
  // remain `comp` for all calls (which is achieved by
  // filling in `orig` when recursing).
//  if (comp._super)
//    callChainedCallback(comp._super, propName, orig || comp);
//
//  if (comp.hasOwnProperty(propName))
//    comp[propName].call(orig || comp);
//};

// Returns 0 if the nodes are the same or either one contains the other;
// otherwise, -1 if a comes before b, or else 1 if b comes before a in
// document order.
// Requires: `a` and `b` are element nodes in the same document tree.
var compareElementIndex = function (a, b) {
  // See http://ejohn.org/blog/comparing-document-position/
  if (a === b)
    return 0;
  if (a.compareDocumentPosition) {
    var n = a.compareDocumentPosition(b);
    return ((n & 0x18) ? 0 : ((n & 0x4) ? -1 : 1));
  } else {
    // Only old IE is known to not have compareDocumentPosition (though Safari
    // originally lacked it).  Thankfully, IE gives us a way of comparing elements
    // via the "sourceIndex" property.
    if (a.contains(b) || b.contains(a))
      return 0;
    return (a.sourceIndex < b.sourceIndex ? -1 : 1);
  }
};

findComponentWithProp = function (id, comp) {
  while (comp) {
    if (typeof comp[id] !== 'undefined')
      return comp;
    comp = comp.parent;
  }
  return null;
};

// Look up the component's chain of parents until we find one with
// `__helperHost` set (a component that can have helpers defined on it,
// i.e. a template).
var findHelperHostComponent = function (comp) {
  while (comp) {
    if (comp.__helperHost) {
      return comp;
    }
    comp = comp.parent;
  }
  return null;
};

findComponentWithHelper = function (id, comp) {
  while (comp) {
    if (comp.__helperHost) {
      if (typeof comp[id] !== 'undefined')
        return comp;
      else
        return null;
    }
    comp = comp.parent;
  }
  return null;
};

getComponentData = function (comp) {
  comp = findComponentWithProp('data', comp);
  return (comp ?
          (typeof comp.data === 'function' ?
           comp.data() : comp.data) :
          null);
};

updateTemplateInstance = function (comp) {
  // Populate `comp.templateInstance.{firstNode,lastNode,data}`
  // on demand.
  var tmpl = comp.templateInstance;
  tmpl.data = getComponentData(comp);

  if (comp.dom && !comp.isDestroyed) {
    tmpl.firstNode = comp.dom.startNode().nextSibling;
    tmpl.lastNode = comp.dom.endNode().previousSibling;
    // Catch the case where the DomRange is empty and we'd
    // otherwise pass the out-of-order nodes (end, start)
    // as (firstNode, lastNode).
    if (tmpl.lastNode && tmpl.lastNode.nextSibling === tmpl.firstNode)
      tmpl.lastNode = tmpl.firstNode;
  } else {
    // on 'created' or 'destroyed' callbacks we don't have a DomRange
    tmpl.firstNode = null;
    tmpl.lastNode = null;
  }
};

_extend(UI.Component, {
  // We implement the old APIs here, including how data is passed
  // to helpers in `this`.
  helpers: function (dict) {
    _extend(this, dict);
  },
  events: function (dict) {
    var events;
    if (this.hasOwnProperty('_events'))
      events = this._events;
    else
      events = (this._events = []);

    _.each(dict, function (handler, spec) {
      var clauses = spec.split(/,\s+/);
      // iterate over clauses of spec, e.g. ['click .foo', 'click .bar']
      _.each(clauses, function (clause) {
        var parts = clause.split(/\s+/);
        if (parts.length === 0)
          return;

        var newEvents = parts.shift();
        var selector = parts.join(' ');
        events.push({events: newEvents,
                     selector: selector,
                     handler: handler});
      });
    });
  }
});

// XXX we don't really want this to be a user-visible callback,
// it's just a particular signal we need from DomRange.
UI.Component.notifyParented = function () {
  var self = this;
  for (var comp = self; comp; comp = comp._super) {
    var events = (comp.hasOwnProperty('_events') && comp._events) || null;
    if ((! events) && comp.hasOwnProperty('events') &&
        typeof comp.events === 'object') {
      // Provide limited back-compat support for `.events = {...}`
      // syntax.  Pass `comp.events` to the original `.events(...)`
      // function.  This code must run only once per component, in
      // order to not bind the handlers more than once, which is
      // ensured by the fact that we only do this when `comp._events`
      // is falsy, and we cause it to be set now.
      UI.Component.events.call(comp, comp.events);
      events = comp._events;
    }
    _.each(events, function (esh) { // {events, selector, handler}
      // wrap the handler here, per instance of the template that
      // declares the event map, so we can pass the instance to
      // the event handler.
      var wrappedHandler = function (event) {
        var comp = UI.DomRange.getContainingComponent(event.currentTarget);
        var data = comp && getComponentData(comp);
        var args = _.toArray(arguments);
        updateTemplateInstance(self);
        return Deps.nonreactive(function () {
          // put self.templateInstance as the second argument
          args.splice(1, 0, self.templateInstance);
          // Don't want to be in a deps context, even if we were somehow
          // triggered synchronously in an existing deps context
          // (the `blur` event can do this).
          // XXX we should probably do what Spark did and block all
          // event handling during our DOM manip.  Many apps had weird
          // unanticipated bugs until we did that.
          return esh.handler.apply(data === null ? {} : data, args);
        });
      };

      self.dom.on(esh.events, esh.selector, wrappedHandler);
    });
  }

  if (self.rendered) {
    // Defer rendered callback until flush time.
    Deps.afterFlush(function () {
      if (! self.isDestroyed) {
        updateTemplateInstance(self);
        self.rendered.call(self.templateInstance);
      }
    });
  }
};

// past compat
UI.Component.preserve = function () {
  Meteor._debug("The 'preserve' method on templates is now unnecessary and deprecated.");
};

// Gets the data context of the enclosing component that rendered a
// given element
UI.getElementData = function (el) {
  var comp = UI.DomRange.getContainingComponent(el);
  return comp && getComponentData(comp);
};

var jsUrlsAllowed = false;
UI._allowJavascriptUrls = function () {
  jsUrlsAllowed = true;
};
UI._javascriptUrlsAllowed = function () {
  return jsUrlsAllowed;
};

UI._templateInstance = function () {
  var currentComp = currentComponent.get();
  if (! currentComp) {
    throw new Error("You can only call UI._templateInstance() from within" +
                    " a helper function.");
  }

  // Find the enclosing component that is a template. (`currentComp`
  // could be, for example, an #if or #with, and we want the component
  // that is the surrounding template.)
  var template = findHelperHostComponent(currentComp);
  if (! template) {
    throw new Error("Current component is not inside a template?");
  }

  // Lazily update the template instance for this helper, and do it only
  // once.
  if (! currentTemplateInstance) {
    updateTemplateInstance(template);
    currentTemplateInstance = template.templateInstance;
  }
  return currentTemplateInstance;
};

// Returns the data context of the parent which is 'numLevels' above the
// component. Same behavior as {{../..}} in a template, with 'numLevels'
// occurrences of '..'.
UI._parentData = function (numLevels) {
  var component = currentComponent.get();
  while (component && numLevels >= 0) {
    // Decrement numLevels every time we find a new data context. Break
    // once we have reached numLevels < 0.
    if (component.data !== undefined && --numLevels < 0) {
      break;
    }
    component = component.parent;
  }

  if (! component) {
    return null;
  }

  return getComponentData(component);
};
<|endoftext|>


<commit_before>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var Hailstorm = Hailstorm || {};
Hailstorm.Project = {};

Hailstorm.Project.Tracker = function(options) {
    this.readLogsPath = options.readLogsPath;
    this.projectStatusPath = options.projectStatusPath;
    this.downloadStatusPath = options.downloadStatusPath;
    this.anythingInProgress = options.anythingInProgress;
    this.logOffset = options.logOffset;
    this.updateResultsPath = options.updateResultsPath;
    this.generatedReportsPath = options.generatedReportsPath;

    this.projectStatusIntervalId = 0;
    this.downloadStatusIntervalId = 0;
    this.refreshLogsIntervalId = 0;

    this.alertCloseBtnMarkup = '<button type="button" data-dismiss="alert" class="close" aria-label="Close">×</button>';
};

Hailstorm.Project.Tracker.prototype.start = function() {

    var self = this;
    this.clrInterval();
    jQuery(document).ready(this.clrInterval.apply(this));
    jQuery(document).on('page:load', this.clrInterval.apply(this));

    // check/uncheck all for test results
    $("#checkUncheckAll").on("click", function() {
        $(".testsCheck").prop("checked", $(this).prop("checked"));
    });

    // set up handlers for triggers
    $(".worker_task").on("click", function(event) {
        self.handleTaskTriggers(this);
        event.preventDefault();
        event.stopPropagation();
    });

    $("#download-reports").click(function(event) {
        var url = jQuery(this).attr('href');
        self.getTestResults(url, 'download');
        event.preventDefault();
        event.stopPropagation();
    });

    $("#export-reports").click(function(event) {
        var url = jQuery(this).attr('href');
        self.getTestResults(url, 'export');
        event.preventDefault();
        event.stopPropagation();
    });

    if (this.anythingInProgress) {
        this.projectStatusIntervalId = window.setInterval(this.periodicProjectSync.bind(this), 60000);
        this.refreshLogsIntervalId = window.setInterval(this.refreshLogs.bind(this), 10000);
    }

    // listen for closed alerts and replace with original content
    $(document).on("close.bs.alert", function() {
       $("#project_fm").html('

');
    });

    $('a[data-toggle="tab"]').on("show.bs.tab", function(event) {
        var tabContentId = $(event.target).attr("aria-controls");
        if (tabContentId == "gen_reports") {
            self.showGeneratedReports();
        }
    });
};

Hailstorm.Project.Tracker.prototype.showError = function(message) {
    var messageText = '
' +
        this.alertCloseBtnMarkup + ' An error occurred';
    if (message) {
        messageText += ': ' + message;
    }
    messageText += '. Please try again.
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showSuccess = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showInfo = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showWarn = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.clrInterval = function() {

    if (this.projectStatusIntervalId > 0) {
        window.clearInterval(this.projectStatusIntervalId);
        this.projectStatusIntervalId = 0;
    }

    if (this.downloadStatusIntervalId > 0) {
        window.clearInterval(this.downloadStatusIntervalId);
        this.downloadStatusIntervalId = 0;
    }

    if (this.refreshLogsIntervalId > 0) {
        window.clearInterval(this.refreshLogsIntervalId);
        this.refreshLogsIntervalId = 0;
    }
};

Hailstorm.Project.Tracker.prototype.refreshLogs = function() {

    jQuery.ajax(this.readLogsPath, {
        data: {offset: this.logOffset},
        success: this.appendLog.bind(this)
    });
};

Hailstorm.Project.Tracker.prototype.periodicProjectSync = function() {

    var self = this;
    jQuery.getJSON(this.projectStatusPath, function(data) {
        self.synchProjectState(data);
    });
};

Hailstorm.Project.Tracker.prototype.checkDownloadStatus = function() {

    var self = this;
    if (this.downloadRequestId) {
        jQuery.ajax({
            url: this.downloadStatusPath,
            data: {request_id: this.downloadRequestId},
            success: function(data) {
                self.updateTestResults(data);
            }
        });
    }
};

Hailstorm.Project.Tracker.prototype.handleTaskTriggers = function(trigger) {

    var self = this;
    var actionCode = $(trigger).attr('id');
    this.lastActionCode = actionCode;
    if (actionCode == "project_abort" || actionCode == "project_terminate" || actionCode == "project_stop") {

        var modalSelector = "#" + actionCode + "-confirm-modal";
        $(modalSelector).modal("show");
        var modalOkSelector = modalSelector + " a.btn-danger";
        var yesFn = function(event) {
            event.preventDefault();
            event.stopPropagation();
            $(modalSelector).modal("hide");
            $.ajax({
                url : $(trigger).attr("href"),
                dataType : "json",
                success: function(result){
                    self.synchProjectState(result);
                    self.showInfo("Proceeding with " + actionCode.split("_")[1] + "...");
                },
                error: self.showError.apply(self)
            });
        };

        $(modalOkSelector).one("click", yesFn);
    } else {
        $.ajax({
            url : $(trigger).attr("href"),
            dataType : "json",
            success:function(result){
                self.synchProjectState(result);
                var msg = (actionCode == "project_setup" ? "The load testing environment is being setup and make take a long time. Once setup completes, this page will be auto-updated." : "Your tests will start in a short while.");
                self.showInfo(msg);
            },
            error: self.showError.apply(self)
        });
    }

    return false;
};

Hailstorm.Project.Tracker.prototype.getCheckedResultsIds = function() {
    return $('.testsCheck:checked').map(function() {
        return this.value;
    }).get();
};

Hailstorm.Project.Tracker.prototype.getTestResults = function(url, type) {

    var self = this;
    var ids = this.getCheckedResultsIds();
    if (ids.length > 0 ) {
        $("#download-reports").addClass( "disabled" );
        $("#export-reports").addClass( "disabled" );
        var message = "Request for report "+type+" has been submitted, please wait while the request is processing.";
        jQuery.ajax({
            url : url,
            data: {ids: ids},
            dataType : 'json',
            success: function(result) {
                self.downloadRequestId = result['request_id'];
                // when the gen_reports tab is shown, self.showGeneratedReports will
                // trigger an event after showing existing reports. Add a one time
                // listener for this event to add in progress message.
                $(document).one("hailstorm:event:gen_reports_showed", function() {
                    $("#generated_reports").prepend('
 Generating report...

');
                });

                $('a[href="#gen_reports"]').tab('show');
                self.showInfo(message);
                $(".testsCheck").prop("checked", false);
                $("#checkUncheckAll").prop("checked", false);
                self.downloadStatusIntervalId = window.setInterval(self.checkDownloadStatus.bind(self), 5000);
                self.refreshLogsIntervalId = window.setInterval(self.refreshLogs.bind(self), 1000);
            },
            error: self.showError.apply(self)
        });
    } else {
        this.showWarn("Please select results to " + type + ".");
    }
};

Hailstorm.Project.Tracker.prototype.appendLog = function(data) {

    var contentLen = data.trim().length;
    if (contentLen > 0) {
        this.logOffset += data.length;
        // auto-scroll
        var appender = jQuery("#logsResults");
        appender.append(data.replace(/\n/g, "
"));
        var sch = appender.prop("scrollHeight");
        var h = appender.height();
        if (sch > h) {
            appender.animate({scrollTop: sch});
        }
    }
};

Hailstorm.Project.Tracker.prototype.synchProjectState = function(result) {

    var stateCode = result.state_code;
    var triggers = result.state_triggers;
    var shaking = result.any_op_in_progress;

    // start all kinds of refreshers if it shaking or clear them
    if (shaking) {
        if (this.projectStatusIntervalId == 0) {
            this.projectStatusIntervalId = window.setInterval(this.periodicProjectSync.bind(this), 60000);
        }

        if (this.refreshLogsIntervalId == 0) {
            this.refreshLogsIntervalId = window.setInterval(this.refreshLogs.bind(this), 10000);
        }
    } else {
        window.clearInterval(this.projectStatusIntervalId);
        this.projectStatusIntervalId = 0;
        window.clearInterval(this.refreshLogsIntervalId);
        this.refreshLogsIntervalId = 0;
    }

    var statusHtml = null;
    var stateIcon = null;
    if (stateCode == "empty") {
        stateIcon = "glyphicon glyphicon-unchecked";
    } else if (stateCode == "partial_configured") {
        stateIcon = "glyphicon glyphicon-edit";
    } else if (stateCode == "configured") {
        stateIcon = "glyphicon glyphicon-check";
    } else if (stateCode == "ready_start") {
        stateIcon = "glyphicon glyphicon-send";
    } else {
        statusHtml = ' ' + result.state_title;
    }

    if (stateIcon) {
        statusHtml = ' ' + result.state_title;
    }

    jQuery("#project_status").html(statusHtml);

    if (triggers.setup) {
        jQuery("#project_setup").removeClass( "disabled" );
    } else {
        jQuery("#project_setup").addClass( "disabled" );
    }

    if (triggers.start) {
        jQuery("#project_start").removeClass( "disabled" );
    } else {
        jQuery("#project_start").addClass( "disabled" );
    }

    if (triggers.stop) {
        jQuery("#project_stop").removeClass( "disabled" );
    } else {
        jQuery("#project_stop").addClass( "disabled" );
    }

    if (triggers.abort) {
        jQuery("#project_abort").removeClass( "disabled" );
    } else {
        jQuery("#project_abort").addClass( "disabled" );
    }

    if (triggers.term) {
        jQuery("#project_terminate").removeClass( "disabled" );
    } else {
        jQuery("#project_terminate").addClass( "disabled" );
    }

    if (this.lastActionCode == "project_start") {
        if (stateCode == "ready_start") {
            var self = this;
            // send ajax call to update results
            jQuery.ajax({url: this.updateResultsPath, success: function(result) {
                $("#project_results").html(result);
                $('#result_options a[href="#test_results"]').tab('show');
                self.showSuccess("Test results for run added.");
            }});
        } else if (stateCode == "started") {
            this.showSuccess("Tests have been started.");
        } else if (stateCode == "stop_progress") {
            this.showInfo("Tests have concluded, fetching test results...");
        }
    }

    if (result.state_reason) {
        this.showError(result.state_reason);
    }
};

Hailstorm.Project.Tracker.prototype.updateTestResults = function(result) {

    var self = this;
    result = parseInt(result);
    if (result == 1) {
        this.downloadRequestId = null;
        this.showGeneratedReports(function() {
            $("#download-reports").removeClass( "disabled" );
            $("#export-reports").removeClass( "disabled" );
            self.showSuccess("Report generation completed.");
            self.clrInterval();
        });
    }
};

// optional argument - a function pointer to apply on success
Hailstorm.Project.Tracker.prototype.showGeneratedReports = function() {
    var onSuccess = arguments.length > 0 ? arguments[0] : null;
    $.get(this.generatedReportsPath)
        .done(function(data) {
            $("#generated_reports").html(data);
            if (onSuccess) {
                onSuccess();
            }

            $(document).trigger("hailstorm:event:gen_reports_showed");
        });
};
<commit_msg>Fixed incorrect use of apply() where bind() was needed<commit_after>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

var Hailstorm = Hailstorm || {};
Hailstorm.Project = {};

Hailstorm.Project.Tracker = function(options) {
    this.readLogsPath = options.readLogsPath;
    this.projectStatusPath = options.projectStatusPath;
    this.downloadStatusPath = options.downloadStatusPath;
    this.anythingInProgress = options.anythingInProgress;
    this.logOffset = options.logOffset;
    this.updateResultsPath = options.updateResultsPath;
    this.generatedReportsPath = options.generatedReportsPath;

    this.projectStatusIntervalId = 0;
    this.downloadStatusIntervalId = 0;
    this.refreshLogsIntervalId = 0;

    this.alertCloseBtnMarkup = '<button type="button" data-dismiss="alert" class="close" aria-label="Close">×</button>';
};

Hailstorm.Project.Tracker.prototype.start = function() {

    var self = this;
    this.clrInterval();
    jQuery(document).ready(this.clrInterval.bind(this));
    jQuery(document).on('page:load', this.clrInterval.bind(this));

    // check/uncheck all for test results
    $("#checkUncheckAll").on("click", function() {
        $(".testsCheck").prop("checked", $(this).prop("checked"));
    });

    // set up handlers for triggers
    $(".worker_task").on("click", function(event) {
        self.handleTaskTriggers(this);
        event.preventDefault();
        event.stopPropagation();
    });

    $("#download-reports").click(function(event) {
        var url = jQuery(this).attr('href');
        self.getTestResults(url, 'download');
        event.preventDefault();
        event.stopPropagation();
    });

    $("#export-reports").click(function(event) {
        var url = jQuery(this).attr('href');
        self.getTestResults(url, 'export');
        event.preventDefault();
        event.stopPropagation();
    });

    if (this.anythingInProgress) {
        this.projectStatusIntervalId = window.setInterval(this.periodicProjectSync.bind(this), 60000);
        this.refreshLogsIntervalId = window.setInterval(this.refreshLogs.bind(this), 10000);
    }

    // listen for closed alerts and replace with original content
    $(document).on("close.bs.alert", function() {
       $("#project_fm").html('

');
    });

    $('a[data-toggle="tab"]').on("show.bs.tab", function(event) {
        var tabContentId = $(event.target).attr("aria-controls");
        if (tabContentId == "gen_reports") {
            self.showGeneratedReports();
        }
    });
};

Hailstorm.Project.Tracker.prototype.showError = function(message) {
    var messageText = '
' +
        this.alertCloseBtnMarkup + ' An error occurred';
    if (message) {
        messageText += ': ' + message;
    }
    messageText += '. Please try again.
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showSuccess = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showInfo = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.showWarn = function(message) {
    var messageText = '
' + this.alertCloseBtnMarkup + ' ' + message + '
';
    $("#project_fm").html(messageText);
};

Hailstorm.Project.Tracker.prototype.clrInterval = function() {

    if (this.projectStatusIntervalId > 0) {
        window.clearInterval(this.projectStatusIntervalId);
        this.projectStatusIntervalId = 0;
    }

    if (this.downloadStatusIntervalId > 0) {
        window.clearInterval(this.downloadStatusIntervalId);
        this.downloadStatusIntervalId = 0;
    }

    if (this.refreshLogsIntervalId > 0) {
        window.clearInterval(this.refreshLogsIntervalId);
        this.refreshLogsIntervalId = 0;
    }
};

Hailstorm.Project.Tracker.prototype.refreshLogs = function() {

    jQuery.ajax(this.readLogsPath, {
        data: {offset: this.logOffset},
        success: this.appendLog.bind(this)
    });
};

Hailstorm.Project.Tracker.prototype.periodicProjectSync = function() {

    var self = this;
    jQuery.getJSON(this.projectStatusPath, function(data) {
        self.synchProjectState(data);
    });
};

Hailstorm.Project.Tracker.prototype.checkDownloadStatus = function() {

    var self = this;
    if (this.downloadRequestId) {
        jQuery.ajax({
            url: this.downloadStatusPath,
            data: {request_id: this.downloadRequestId},
            success: function(data) {
                self.updateTestResults(data);
            }
        });
    }
};

Hailstorm.Project.Tracker.prototype.handleTaskTriggers = function(trigger) {

    var self = this;
    var actionCode = $(trigger).attr('id');
    this.lastActionCode = actionCode;
    if (actionCode == "project_abort" || actionCode == "project_terminate" || actionCode == "project_stop") {

        var modalSelector = "#" + actionCode + "-confirm-modal";
        $(modalSelector).modal("show");
        var modalOkSelector = modalSelector + " a.btn-danger";
        var yesFn = function(event) {
            event.preventDefault();
            event.stopPropagation();
            $(modalSelector).modal("hide");
            $.ajax({
                url : $(trigger).attr("href"),
                dataType : "json",
                success: function(result){
                    self.synchProjectState(result);
                    self.showInfo("Proceeding with " + actionCode.split("_")[1] + "...");
                },
                error: self.showError.bind(self)
            });
        };

        $(modalOkSelector).one("click", yesFn);
    } else {
        $.ajax({
            url : $(trigger).attr("href"),
            dataType : "json",
            success: function(result){
                self.synchProjectState(result);
                var msg = (actionCode == "project_setup" ? "The load testing environment is being setup and make take a long time. Once setup completes, this page will be auto-updated." : "Your tests will start in a short while.");
                self.showInfo(msg);
            },
            error: self.showError.bind(self)
        });
    }

    return false;
};

Hailstorm.Project.Tracker.prototype.getCheckedResultsIds = function() {
    return $('.testsCheck:checked').map(function() {
        return this.value;
    }).get();
};

Hailstorm.Project.Tracker.prototype.getTestResults = function(url, type) {

    var self = this;
    var ids = this.getCheckedResultsIds();
    if (ids.length > 0 ) {
        $("#download-reports").addClass( "disabled" );
        $("#export-reports").addClass( "disabled" );
        var message = "Request for report "+type+" has been submitted, please wait while the request is processing.";
        jQuery.ajax({
            url : url,
            data: {ids: ids},
            dataType : 'json',
            success: function(result) {
                self.downloadRequestId = result['request_id'];
                // when the gen_reports tab is shown, self.showGeneratedReports will
                // trigger an event after showing existing reports. Add a one time
                // listener for this event to add in progress message.
                $(document).one("hailstorm:event:gen_reports_showed", function() {
                    $("#generated_reports").prepend('
 Generating report...

');
                });

                $('a[href="#gen_reports"]').tab('show');
                self.showInfo(message);
                $(".testsCheck").prop("checked", false);
                $("#checkUncheckAll").prop("checked", false);
                self.downloadStatusIntervalId = window.setInterval(self.checkDownloadStatus.bind(self), 5000);
                self.refreshLogsIntervalId = window.setInterval(self.refreshLogs.bind(self), 1000);
            },
            error: self.showError.bind(self)
        });
    } else {
        this.showWarn("Please select results to " + type + ".");
    }
};

Hailstorm.Project.Tracker.prototype.appendLog = function(data) {

    var contentLen = data.trim().length;
    if (contentLen > 0) {
        this.logOffset += data.length;
        // auto-scroll
        var appender = jQuery("#logsResults");
        appender.append(data.replace(/\n/g, "
"));
        var sch = appender.prop("scrollHeight");
        var h = appender.height();
        if (sch > h) {
            appender.animate({scrollTop: sch});
        }
    }
};

Hailstorm.Project.Tracker.prototype.synchProjectState = function(result) {

    var stateCode = result.state_code;
    var triggers = result.state_triggers;
    var shaking = result.any_op_in_progress;

    // start all kinds of refreshers if it shaking or clear them
    if (shaking) {
        if (this.projectStatusIntervalId == 0) {
            this.projectStatusIntervalId = window.setInterval(this.periodicProjectSync.bind(this), 60000);
        }

        if (this.refreshLogsIntervalId == 0) {
            this.refreshLogsIntervalId = window.setInterval(this.refreshLogs.bind(this), 10000);
        }
    } else {
        window.clearInterval(this.projectStatusIntervalId);
        this.projectStatusIntervalId = 0;
        window.clearInterval(this.refreshLogsIntervalId);
        this.refreshLogsIntervalId = 0;
    }

    var statusHtml = null;
    var stateIcon = null;
    if (stateCode == "empty") {
        stateIcon = "glyphicon glyphicon-unchecked";
    } else if (stateCode == "partial_configured") {
        stateIcon = "glyphicon glyphicon-edit";
    } else if (stateCode == "configured") {
        stateIcon = "glyphicon glyphicon-check";
    } else if (stateCode == "ready_start") {
        stateIcon = "glyphicon glyphicon-send";
    } else {
        statusHtml = ' ' + result.state_title;
    }

    if (stateIcon) {
        statusHtml = ' ' + result.state_title;
    }

    jQuery("#project_status").html(statusHtml);

    if (triggers.setup) {
        jQuery("#project_setup").removeClass( "disabled" );
    } else {
        jQuery("#project_setup").addClass( "disabled" );
    }

    if (triggers.start) {
        jQuery("#project_start").removeClass( "disabled" );
    } else {
        jQuery("#project_start").addClass( "disabled" );
    }

    if (triggers.stop) {
        jQuery("#project_stop").removeClass( "disabled" );
    } else {
        jQuery("#project_stop").addClass( "disabled" );
    }

    if (triggers.abort) {
        jQuery("#project_abort").removeClass( "disabled" );
    } else {
        jQuery("#project_abort").addClass( "disabled" );
    }

    if (triggers.term) {
        jQuery("#project_terminate").removeClass( "disabled" );
    } else {
        jQuery("#project_terminate").addClass( "disabled" );
    }

    if (this.lastActionCode == "project_start") {
        if (stateCode == "ready_start") {
            var self = this;
            // send ajax call to update results
            jQuery.ajax({url: this.updateResultsPath, success: function(result) {
                $("#project_results").html(result);
                $('#result_options a[href="#test_results"]').tab('show');
                self.showSuccess("Test results for run added.");
            }});
        } else if (stateCode == "started") {
            this.showSuccess("Tests have been started.");
        } else if (stateCode == "stop_progress") {
            this.showInfo("Tests have concluded, fetching test results...");
        }
    } else if (this.lastActionCode == "project_setup") {
        if (stateCode == "ready_start") {
            this.showSuccess("Setup concluded successfully, you can now start tests.");
        }
    }

    if (result.state_reason) {
        this.showError(result.state_reason);
    }
};

Hailstorm.Project.Tracker.prototype.updateTestResults = function(result) {

    var self = this;
    result = parseInt(result);
    if (result == 1) {
        this.downloadRequestId = null;
        this.showGeneratedReports(function() {
            $("#download-reports").removeClass( "disabled" );
            $("#export-reports").removeClass( "disabled" );
            self.showSuccess("Report generation completed.");
            self.clrInterval();
        });
    }
};

// optional argument - a function pointer to apply on success
Hailstorm.Project.Tracker.prototype.showGeneratedReports = function() {
    var onSuccess = arguments.length > 0 ? arguments[0] : null;
    $.get(this.generatedReportsPath)
        .done(function(data) {
            $("#generated_reports").html(data);
            if (onSuccess) {
                onSuccess();
            }

            $(document).trigger("hailstorm:event:gen_reports_showed");
        });
};
<|endoftext|>


<commit_before>var Db = require("mongodb").Db,
    Server = require("mongodb").Server,
    Promise = global.Packages.Promise;

function getClient(server, pools, callback) {
    var poolkey = server.host + ":" + server.port;
    var pool = pools[poolkey];

    if (!pool) {
        var db = new Db(server.db, new Server(server.host, server.port, {
            auto_reconnect: true
        }), {
            native_parser: true,
            w: 1
        });

        db.open(function(err, mongoclient) {
            if (err) {
                callback(err);
                return;
            }
            pools[poolkey] = mongoclient;
            callback(err, pools[poolkey]); //return Promise
        });
    }
    else {

        callback(null, pool);
    }

}

function initialize(params) {

    var pools = [];
    var server = {
        host: params.host,
        port: params.port,
        db: params.db,
    };

    // TODO: Mongo supports sharding and replica logic, we should compline this class to support sharding and read replica

    function deleteAllPropertiesWhichAreUndefined(object) {
        for (var k in object) {
            if (object[k] === undefined) {
                delete object[k];
            }
        }

        return object;
    }

    function extend(attributes) {

        if (!attributes.tableName) {
            throw new Error("tableName must be defined");
        }

        function saveData(context, input) {

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(context.tableName).save(deleteAllPropertiesWhichAreUndefined(context.getDBObject(input)), function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        }

        function getArrayQuery(context, data) {
            var query_obj = {};
            data = deleteAllPropertiesWhichAreUndefined(context.getDBObject(data));

            for (var property in data) {
                if (data[property] instanceof Array) {
                    query_obj[property] = {
                        $each: data[property]
                    };
                }
                else {
                    query_obj[property] = data[property];
                }
            }

            return query_obj;
        }

        function Model(obj) {

            //if model creation is not with new keyword then return it with new
            if (!(this instanceof Model)) {
                return new Model(obj);
            }

            this.tableName = attributes.tableName;

            if (attributes.idAttribute) {
                obj._id = obj[attributes.idAttribute];
                obj[attributes.idAttribute] = undefined;
            }

            this.input = obj;
        }

        Model.prototype.update = function update(where_clause, set_fields, config) {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    if (config) {
                        client.collection(self.tableName).update(self.getDBObject(where_clause),
                        self.getDBObject(set_fields), config, function(err, output) {
                            if (err) {
                                reject(err);
                                return;
                            }
                            resolve(output);
                            return;
                        });

                    }
                    else {
                        client.collection(self.tableName).update(self.getDBObject(where_clause),
                        self.getDBObject(set_fields), function(err, output) {
                            if (err) {
                                reject(err);
                                return;
                            }
                            resolve(output);
                            return;
                        });
                    }
                });
            });
        };

        Model.prototype.save = function save() {
            var self = this;
            return saveData(self, self.input);

        };

        Model.prototype.saveInBatch = function saveInBatch() {
            var self = this;

            var promises = [];

            for (var index = 0; index < self.input.length; index++) {
                promises.push(saveData(self, self.input[index]));
            }

            return Promise.all(promises).spread(function() {
                return true;
            });

        };

        function prepareSelectClause(arg) {
            var select_clause_obj = {};

            if (!arg) {
                return select_clause_obj;
            }
            for (var i = 0; i < arg.length; i++) {
                select_clause_obj[arg[i]] = "1";
            }
            return select_clause_obj;
        }

        Model.prototype.fetch = function fetch() {
            var self = this;

            var select_clause = prepareSelectClause(arguments);

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    //  deleting all properties which are undefined because otherwise mongo will make them part of query that results
                    //  inappropriate data
                    var mongo_obj = deleteAllPropertiesWhichAreUndefined(self.getDBObject(self.input));

                    client.collection(self.tableName).find(
                    mongo_obj, select_clause, {
                        limit: 1
                    }).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }

                        if (output.length > 0) {
                            resolve(self.getObjectFromDBObject(output.shift()));
                            return;
                        }
                        else {
                            resolve(null);
                            return;
                        }
                    });
                });
            });
        };

        Model.prototype.getInBatch = function getInBatch() {
            var self = this;

            if(!self.input){
                return new Promise(function(resolve){
                    return resolve(null);
                });
            }

            var select_clause = prepareSelectClause(arguments);

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).find({
                        _id: {
                            $in: self.input
                        }
                    }, select_clause, {
                        limit: self.input.length
                    }).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }

                        var result = [];

                        while (output.length > 0) {
                            result.push(self.getObjectFromDBObject(output.shift()));
                        }
                        resolve(result);
                        return;
                    });
                });
            });
        };

        Model.prototype.find = function find(criteria, select_clause, projection) {
            var self = this;
            select_clause = prepareSelectClause(select_clause);
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).find(criteria, select_clause, projection).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        var result = [];

                        while (output.length > 0) {
                            result.push(self.getObjectFromDBObject(output.shift()));
                        }
                        resolve(result);
                        return;
                    });
                });
            });
        };

        Model.prototype.getCount = function getCount(criteria, select_clause, projection) {
            var self = this;
            select_clause = prepareSelectClause(select_clause);
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    //using default value of applySkipLimit
                    client.collection(self.tableName).find(criteria, select_clause, projection).count("", function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.appendArrayItemsIfNotExist = function appendArrayItemsIfNotExist(where_clause) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $addToSet: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.appendArrayItems = function appednArrayItems(where_clause) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $push: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.getDBObject = function getDBObject(obj) {
            return obj;
        };

        Model.prototype.getObjectFromDBObject = function getObjectFromDBObject(obj) {
            return obj;
        };

        Model.prototype.deleteAll = function deleteAll() {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).remove(err, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });

                });
            });
        };

        Model.prototype.deleteObject = function deleteObject() {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    var mongo_obj = deleteAllPropertiesWhichAreUndefined(self.getDBObject(self.input));

                    client.collection(self.tableName).remove(mongo_obj, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.removeArrayItems = function removeArrayItems(where_clause) {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $pull: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.updateRow = function updateRow(where_clause, set_fields, is_upsert, is_multi) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    where_clause,
                    set_fields, {
                        upsert: is_upsert,
                        multi: is_multi
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.findAndModifyRow = function findAndModifyRow(
        query, sort, remove, update, new_data, fields, upsert) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    client.collection(self.tableName).findAndModifyRow({
                        query: query
                    }, {
                        sort: sort
                    }, {
                        remove: remove
                    }, {
                        update: update
                    }, {
                        new: new_data
                    }, {
                        fields: fields
                    }, {
                        upsert: upsert
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                        }
                        resolve(output);
                    });

                });
            });

        };

        return Model;
    }

    return {
        extend: extend

    };
}

module.exports.initialize = initialize;
<commit_msg>mongo update minor fix3<commit_after>var Db = require("mongodb").Db,
    Server = require("mongodb").Server,
    Promise = global.Packages.Promise;

function getClient(server, pools, callback) {
    var poolkey = server.host + ":" + server.port;
    var pool = pools[poolkey];

    if (!pool) {
        var db = new Db(server.db, new Server(server.host, server.port, {
            auto_reconnect: true
        }), {
            native_parser: true,
            w: 1
        });

        db.open(function(err, mongoclient) {
            if (err) {
                callback(err);
                return;
            }
            pools[poolkey] = mongoclient;
            callback(err, pools[poolkey]); //return Promise
        });
    }
    else {

        callback(null, pool);
    }

}

function initialize(params) {

    var pools = [];
    var server = {
        host: params.host,
        port: params.port,
        db: params.db,
    };

    // TODO: Mongo supports sharding and replica logic, we should compline this class to support sharding and read replica

    function deleteAllPropertiesWhichAreUndefined(object) {
        for (var k in object) {
            if (object[k] === undefined) {
                delete object[k];
            }
        }

        return object;
    }

    function extend(attributes) {

        if (!attributes.tableName) {
            throw new Error("tableName must be defined");
        }

        function saveData(context, input) {

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(context.tableName).save(deleteAllPropertiesWhichAreUndefined(context.getDBObject(input)), function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        }

        function getArrayQuery(context, data) {
            var query_obj = {};
            data = deleteAllPropertiesWhichAreUndefined(context.getDBObject(data));

            for (var property in data) {
                if (data[property] instanceof Array) {
                    query_obj[property] = {
                        $each: data[property]
                    };
                }
                else {
                    query_obj[property] = data[property];
                }
            }

            return query_obj;
        }

        function Model(obj) {

            //if model creation is not with new keyword then return it with new
            if (!(this instanceof Model)) {
                return new Model(obj);
            }

            this.tableName = attributes.tableName;

            if (attributes.idAttribute) {
                obj._id = obj[attributes.idAttribute];
                obj[attributes.idAttribute] = undefined;
            }

            this.input = obj;
        }

        Model.prototype.update = function update(where_clause, set_fields, config) {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    if (config) {
                        client.collection(self.tableName).update(deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)),
                        deleteAllPropertiesWhichAreUndefined(self.getDBObject(set_fields)), config, function(err, output) {
                            if (err) {
                                reject(err);
                                return;
                            }
                            resolve(output);
                            return;
                        });

                    }
                    else {
                        client.collection(self.tableName).update(deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)),
                        deleteAllPropertiesWhichAreUndefined(self.getDBObject(set_fields)), function(err, output) {
                            if (err) {
                                reject(err);
                                return;
                            }
                            resolve(output);
                            return;
                        });
                    }
                });
            });
        };

        Model.prototype.save = function save() {
            var self = this;
            return saveData(self, self.input);

        };

        Model.prototype.saveInBatch = function saveInBatch() {
            var self = this;

            var promises = [];

            for (var index = 0; index < self.input.length; index++) {
                promises.push(saveData(self, self.input[index]));
            }

            return Promise.all(promises).spread(function() {
                return true;
            });

        };

        function prepareSelectClause(arg) {
            var select_clause_obj = {};

            if (!arg) {
                return select_clause_obj;
            }
            for (var i = 0; i < arg.length; i++) {
                select_clause_obj[arg[i]] = "1";
            }
            return select_clause_obj;
        }

        Model.prototype.fetch = function fetch() {
            var self = this;

            var select_clause = prepareSelectClause(arguments);

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    //  deleting all properties which are undefined because otherwise mongo will make them part of query that results
                    //  inappropriate data
                    var mongo_obj = deleteAllPropertiesWhichAreUndefined(self.getDBObject(self.input));

                    client.collection(self.tableName).find(
                    mongo_obj, select_clause, {
                        limit: 1
                    }).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }

                        if (output.length > 0) {
                            resolve(self.getObjectFromDBObject(output.shift()));
                            return;
                        }
                        else {
                            resolve(null);
                            return;
                        }
                    });
                });
            });
        };

        Model.prototype.getInBatch = function getInBatch() {
            var self = this;

            if(!self.input){
                return new Promise(function(resolve){
                    return resolve(null);
                });
            }

            var select_clause = prepareSelectClause(arguments);

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).find({
                        _id: {
                            $in: self.input
                        }
                    }, select_clause, {
                        limit: self.input.length
                    }).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }

                        var result = [];

                        while (output.length > 0) {
                            result.push(self.getObjectFromDBObject(output.shift()));
                        }
                        resolve(result);
                        return;
                    });
                });
            });
        };

        Model.prototype.find = function find(criteria, select_clause, projection) {
            var self = this;
            select_clause = prepareSelectClause(select_clause);
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).find(criteria, select_clause, projection).toArray(function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        var result = [];

                        while (output.length > 0) {
                            result.push(self.getObjectFromDBObject(output.shift()));
                        }
                        resolve(result);
                        return;
                    });
                });
            });
        };

        Model.prototype.getCount = function getCount(criteria, select_clause, projection) {
            var self = this;
            select_clause = prepareSelectClause(select_clause);
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    //using default value of applySkipLimit
                    client.collection(self.tableName).find(criteria, select_clause, projection).count("", function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.appendArrayItemsIfNotExist = function appendArrayItemsIfNotExist(where_clause) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $addToSet: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.appendArrayItems = function appednArrayItems(where_clause) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $push: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.getDBObject = function getDBObject(obj) {
            return obj;
        };

        Model.prototype.getObjectFromDBObject = function getObjectFromDBObject(obj) {
            return obj;
        };

        Model.prototype.deleteAll = function deleteAll() {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).remove(err, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });

                });
            });
        };

        Model.prototype.deleteObject = function deleteObject() {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    var mongo_obj = deleteAllPropertiesWhichAreUndefined(self.getDBObject(self.input));

                    client.collection(self.tableName).remove(mongo_obj, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.removeArrayItems = function removeArrayItems(where_clause) {
            var self = this;
            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    client.collection(self.tableName).update(
                    deleteAllPropertiesWhichAreUndefined(self.getDBObject(where_clause)), {
                        $pull: getArrayQuery(self, self.input)
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.updateRow = function updateRow(where_clause, set_fields, is_upsert, is_multi) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }

                    client.collection(self.tableName).update(
                    where_clause,
                    set_fields, {
                        upsert: is_upsert,
                        multi: is_multi
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(output);
                        return;
                    });
                });
            });
        };

        Model.prototype.findAndModifyRow = function findAndModifyRow(
        query, sort, remove, update, new_data, fields, upsert) {
            var self = this;

            return new Promise(function(resolve, reject) {
                getClient(server, pools, function(err, client) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    client.collection(self.tableName).findAndModifyRow({
                        query: query
                    }, {
                        sort: sort
                    }, {
                        remove: remove
                    }, {
                        update: update
                    }, {
                        new: new_data
                    }, {
                        fields: fields
                    }, {
                        upsert: upsert
                    }, function(err, output) {
                        if (err) {
                            reject(err);
                        }
                        resolve(output);
                    });

                });
            });

        };

        return Model;
    }

    return {
        extend: extend

    };
}

module.exports.initialize = initialize;
<|endoftext|>


<commit_before>var Component = UIComponent;

_UI.List = Component.extend({
  typeName: 'List',
  _idStringify: null,
  _items: null, // OrderedDict of id -> Component
  _else: null, // Component
  constructed: function () {
    this._items = new OrderedDict(
      this._idStringify || function (x) { return x; });
  },
  addItemBefore: function (id, comp, beforeId) {
    this._items.putBefore(id, comp, beforeId);

    if (this.stage === Component.BUILT) {
      if (this._else) {
        this._else.remove();
        this._else = null;
      }

      this.insertBefore(
        comp, beforeId ? this._items.get(beforeId) : null);
    }
  },
  removeItem: function (id) {
    var comp = this._items.remove(id);

    if (this.stage === Component.BUILT) {
      comp.remove();
      if (this._items.empty()) {
        this._else = this.elseContent();
        if (this._else)
          this.append(this._else);
      }
    }
  },
  moveItemBefore: function (id, beforeId) {
    var comp = this._items.get(id);
    this._items.moveBefore(id, beforeId);

    if (this.stage === Component.BUILT) {
      comp.detach();
      this.insertBefore(
        comp, beforeId ? this._items.get(beforeId) : null);
    }
  },
  getItem: function (id) {
    return this._items.get(id) || null;
  },
  render: function (buf) {
    var self = this;
    if (self._items.empty()) {
      buf(self._else = self.elseContent());
    } else {
      self._items.forEach(function (comp) {
        buf(comp);
      });
    }
  },
  _findStartComponent: function () {
    return this._items.firstValue();
  },
  _findEndComponent: function () {
    return this._items.lastValue();
  }
});

_UI.Each = Component.extend({
  typeName: 'Each',
  List: _UI.List,
  render: function (buf) {
    var self = this;

    // XXX support arrays too.
    // XXX and objects.
    // For now, we assume the data is a database cursor.
    var cursor = self.data();
    if (! cursor)
      return;

    var list = new self.List({
      _idStringify: Meteor.idStringify,
      elseContent: self.elseContent
    });

    cursor.observe({
      _no_indices: true,
      addedAt: function (doc, i, beforeId) {
        var comp = self.content(function () {
          this.dataDep.depend();
          return this._data;
        }, {
          _data: doc,
          dataDep: new Deps.Dependency
        });
        // XXX could `before` be a falsy ID?  Technically
        // idStringify seems to allow for them -- though
        // OrderedDict won't call stringify on a falsy arg.
        list.addItemBefore(doc._id, comp, beforeId);
      },
      removed: function (doc) {
        list.removeItem(doc._id);
      },
      movedTo: function (doc, i, j, beforeId) {
        list.moveItemBefore(doc._id, beforeId);
      },
      changed: function (newDoc) {
        var comp = list.getItem(newDoc._id);
        comp._data = newDoc;
        comp.dataDep.changed();
      }
    });

    buf(list);
  }
});
<commit_msg>move idStringify from List to Each<commit_after>var Component = UIComponent;

_UI.List = Component.extend({
  typeName: 'List',
  _items: null, // OrderedDict of id -> Component
  _else: null, // Component
  constructed: function () {
    this._items = new OrderedDict;
  },
  addItemBefore: function (id, comp, beforeId) {
    this._items.putBefore(id, comp, beforeId);

    if (this.stage === Component.BUILT) {
      if (this._else) {
        this._else.remove();
        this._else = null;
      }

      this.insertBefore(
        comp, beforeId ? this._items.get(beforeId) : null);
    }
  },
  removeItem: function (id) {
    var comp = this._items.remove(id);

    if (this.stage === Component.BUILT) {
      comp.remove();
      if (this._items.empty()) {
        this._else = this.elseContent();
        if (this._else)
          this.append(this._else);
      }
    }
  },
  moveItemBefore: function (id, beforeId) {
    var comp = this._items.get(id);
    this._items.moveBefore(id, beforeId);

    if (this.stage === Component.BUILT) {
      comp.detach();
      this.insertBefore(
        comp, beforeId ? this._items.get(beforeId) : null);
    }
  },
  getItem: function (id) {
    return this._items.get(id) || null;
  },
  render: function (buf) {
    var self = this;
    if (self._items.empty()) {
      buf(self._else = self.elseContent());
    } else {
      self._items.forEach(function (comp) {
        buf(comp);
      });
    }
  },
  _findStartComponent: function () {
    return this._items.firstValue();
  },
  _findEndComponent: function () {
    return this._items.lastValue();
  }
});

_UI.Each = Component.extend({
  typeName: 'Each',
  List: _UI.List,
  render: function (buf) {
    var self = this;

    // XXX support arrays too.
    // XXX and objects.
    // For now, we assume the data is a database cursor.
    var cursor = self.data();
    if (! cursor)
      return;

    var list = new self.List({
      elseContent: self.elseContent
    });

    cursor.observe({
      _no_indices: true,
      addedAt: function (doc, i, beforeId) {
        var comp = self.content(function () {
          this.dataDep.depend();
          return this._data;
        }, {
          _data: doc,
          dataDep: new Deps.Dependency
        });
        // XXX could `before` be a falsy ID?  Technically
        // idStringify seems to allow for them -- though
        // OrderedDict won't call stringify on a falsy arg.
        list.addItemBefore(Meteor.idStringify(doc._id), comp,
                           beforeId && Meteor.idStringify(beforeId));
      },
      removed: function (doc) {
        list.removeItem(Meteor.idStringify(doc._id));
      },
      movedTo: function (doc, i, j, beforeId) {
        list.moveItemBefore(Meteor.idStringify(doc._id),
                            beforeId && Meteor.idStringify(beforeId));
      },
      changed: function (newDoc) {
        var comp = list.getItem(Meteor.idStringify(newDoc._id));
        comp._data = newDoc;
        comp.dataDep.changed();
      }
    });

    buf(list);
  }
});
<|endoftext|>