<commit_before>/*
 * Pregnancy is a node contained by a Partnership and is used to group and manage
 * monozygotic twins.
 *
 * @param x the x coordinate at which the pregnancy node will be placed
 * @param y the y coordinate at which the pregnancy node will be placed
 * @param partnership the partnership at which this node is rooted from
 * @param id the id of this node
 */

var Pregnancy = Class.create(AbstractNode, {

    initialize: function($super, x, y, partnership, id) {
        this._partnership = partnership;
        this._PersonChildren = [];
        this._PlaceHolderChildren = [];
        this._PersonGroupChildren = [];
        this._isActive = false;
        $super(x, y, id);
        this._type = 'Pregnancy';
        this._gender = "U";
    },

    /*
     * Generates and returns an instance of PregnancyVisuals
     *
     * @param x,y the x and y coordinates of this pregnancy
     */
    generateGraphics: function(x, y) {
        return new PregnancyVisuals(this, x, y);
    },

    /*
     * Returns "M", "F", or "U" to represent the gender of nodes in this pregnancy
     */
    getGender: function() {
        return this._gender;
    },

    /*
     * Changes the gender of this pregnancy
     *
     * @param gender can be "M", "F", or "U"
     */
    setGender: function(gender) {
        if(this._gender != gender && !this.isGenderLocked()) {
            this._gender = gender;
            this.getChildren().each(function(child) {
                if(child.getGender() != gender) {
                    child.setGender(gender);
                }
            })
        }
    },

    /*
     * Returns true if editing the gender of this pregnancy is currently impossible
     */
    isGenderLocked: function() {
        var lockedGender = false;
        this.getChildren("Person").each(function(child) {
            if(child.getGender() != 'U' && child.getPartners().length != 0) {
                lockedGender = true;
                throw $break;
            }
        });
        return lockedGender;
    },

    /*
     * Returns the partnership from which this node stems
     */
    getPartnership: function() {
        return this._partnership;
    },

    /*
     * Returns an array of nodes that are children of this Pregnancy
     *
     * @param type can filter the array to the specified type (eg. "PlaceHolder", "Person", etc).
     * Multiple types can be passed (eg. getChildren(type1, type2,...,typeN)
     */
    getChildren: function(type) {
        if(arguments.length == 0) {
            return this._PlaceHolderChildren.concat(this._PersonGroupChildren, this._PersonChildren);
        }
        else {
            var children = [];
            for(var i = 0; i < arguments.length; i++) {
                if(this["_" + arguments[i] + "Children"]) {
                    children = children.concat(this["_" + arguments[i] + "Children"]);
                }
            }
            return children;
        }
    },

    /*
     * Returns true if someNode is a child of this pregnancy.
     *
     * @param someNode is an AbstractPerson
     */
    hasChild: function(someNode) {
        return this.getChildren(someNode.getType()).indexOf(someNode) > -1;
    },

    /*
     * Creates a new node and sets it as a child of this pregnancy. Returns the child.
     *
     * @param type can be any type of AbstractPerson
     */
    createChild: function(type) {
        if(this.isChildTypeValid(type) ) {
            var id = this.getID();
            var pos = editor.findPosition({below: id}, ['child']);
            var child = editor.getGraph()["add" + type](pos['child'].x, pos['child'].y, this.getGender());
            this.addChild(child);
            //document.fire("pedigree:child:added", {node: child, 'relatedNodes' : [], 'sourceNode' : this});
        }
    },

    /*
     * Attaches this pregnancy to someNode
     *
     * @param someNode is an AbstractPerson
     */
    addChild: function(someNode) {
        //TODO: figure out whether to remove placeholders as well
        if(someNode && this.canBeParentOf(someNode)) {
            this["_" + someNode.getType() + "Children"].push(someNode);
            someNode.setParentPregnancy(this);
            this.getGraphics().addChild(someNode);
            this.setActive(someNode.getType() == 'Person');
            this.setGender(someNode.getGender());
        }
        return someNode;
    },

    /*
     * Breaks the connection between the pregnancy and child.
     * Returns child;
     *
     * @param child is an AbstractPerson
     */
    removeChild: function(child) {
        this.getGraphics().removeChild(child);
        child.setParentPregnancy(null);
        this["_" + child.getType() + "Children"] = this["_" + child.getType() + "Children"].without(child);
        if(this.getChildren().length == 0) {
            this.remove();
        }
        else {
            this.setActive(child.getType() == "PlaceHolder");
        }
        return child;
    },

    /*
     * Removes this pregnancy and all the visuals attached to it from the graph.
     * Set isRecursive to true to remove all children, unless they have some other connection
     * to the Proband
     *
     * @param isRecursive can be true or false
     */
    remove: function($super, isRecursive) {
        editor.getGraph().removePartnership(this);
        if(isRecursive) {
            $super(isRecursive);
        }
        else {
            var me = this;
            this.getChildren().each(function(child) {
                me.removeChild(child);
                if(child.getType() == 'PlaceHolder') {
                    child.remove(false, true);
                }
            });
            this.getGraphics().remove();
            this.getPartnership().removePregnancy(me);
        }
    },

    /*
     * Returns an array of children nodes of this pregnancy
     */
    getLowerNeighbors: function() {
        return this.getChildren();
    },

    /*
     * Returns an empty array
     */
    getSideNeighbors: function() {
        return [];
    },

    /*
     * Returns the parent partnership
     */
    getUpperNeighbors: function() {
        return this.getPartnership();
    },

    /*
     * Returns true if someNode can be a child of this pregnancy
     *
     * @param someNode is an AbstractNode
     */
    canBeParentOf: function(someNode) {
        var compatibleGender = someNode.getGender() == this.getGender() || this.getGender() == "U";
        return compatibleGender && this.isChildTypeValid(someNode.getType()) && this.getPartnership().canBeParentOf(someNode);
    },

    /*
     * Returns true if a child of type 'nodeType' can be added to this pregnancy
     *
     * @nodeType can be any type of AbstractNode, such as "Person", "PlaceHolder" and "PersonGroup"
     */
    isChildTypeValid: function(nodeType) {
        var validType = editor.getGraph()["add" + nodeType];
        var validPlaceHolder = nodeType != "PlaceHolder" || this.getChildren("PlaceHolder").length == 0;
        var validPersonGroup = nodeType != "PersonGroup" || this.getChildren("Person").length == 0;
        var noPersonGroups = this.getChildren("PersonGroup").length == 0;
        return validType && validPlaceHolder && validPersonGroup && noPersonGroups;
    },

    /*
     * Returns true is the only child of this pregnancy is a PlaceHolder
     */
    isPlaceHolderPregnancy: function() {
        var children = this.getChildren();
        return children.length = 1 && children[0].getType() == "PlaceHolder";
    },

    /*
     * Removes someNode from this pregnancy, and places it into a new one
     *
     * @param someNode is an AbstractPerson
     */
    separateFromPregnancy: function(someNode) {
        if(this.hasChild(someNode)) {
            this.removeChild(someNode);
            this.getPartnership().addChild(someNode);
        }
    },

    setActive: function(isActive) {
        if(this._isActive != isActive) {
            this._isActive = isActive;
            this.getGraphics().updateActive();
        }
    },

    isActive: function() {
        return this._isActive;
    }
});<commit_msg>Refactored options regarding whether the pregnancy is active<commit_after>/*
 * Pregnancy is a node contained by a Partnership and is used to group and manage
 * monozygotic twins.
 *
 * @param x the x coordinate at which the pregnancy node will be placed
 * @param y the y coordinate at which the pregnancy node will be placed
 * @param partnership the partnership at which this node is rooted from
 * @param id the id of this node
 */

var Pregnancy = Class.create(AbstractNode, {

    initialize: function($super, x, y, partnership, id) {
        this._partnership = partnership;
        this._PersonChildren = [];
        this._PlaceHolderChildren = [];
        this._PersonGroupChildren = [];
        this._isActive = false;
        $super(x, y, id);
        this._type = 'Pregnancy';
        this._gender = "U";
    },

    /*
     * Generates and returns an instance of PregnancyVisuals
     *
     * @param x,y the x and y coordinates of this pregnancy
     */
    generateGraphics: function(x, y) {
        return new PregnancyVisuals(this, x, y);
    },

    /*
     * Returns "M", "F", or "U" to represent the gender of nodes in this pregnancy
     */
    getGender: function() {
        return this._gender;
    },

    /*
     * Changes the gender of this pregnancy
     *
     * @param gender can be "M", "F", or "U"
     */
    setGender: function(gender) {
        if(this._gender != gender && !this.isGenderLocked()) {
            this._gender = gender;
            this.getChildren().each(function(child) {
                if(child.getGender() != gender) {
                    child.setGender(gender);
                }
            })
        }
    },

    /*
     * Returns true if editing the gender of this pregnancy is currently impossible
     */
    isGenderLocked: function() {
        var lockedGender = false;
        this.getChildren("Person").each(function(child) {
            if(child.getGender() != 'U' && child.getPartners().length != 0) {
                lockedGender = true;
                throw $break;
            }
        });
        return lockedGender;
    },

    /*
     * Returns the partnership from which this node stems
     */
    getPartnership: function() {
        return this._partnership;
    },

    /*
     * Returns an array of nodes that are children of this Pregnancy
     *
     * @param type can filter the array to the specified type (eg. "PlaceHolder", "Person", etc).
     * Multiple types can be passed (eg. getChildren(type1, type2,...,typeN)
     */
    getChildren: function(type) {
        if(arguments.length == 0) {
            return this._PlaceHolderChildren.concat(this._PersonGroupChildren, this._PersonChildren);
        }
        else {
            var children = [];
            for(var i = 0; i < arguments.length; i++) {
                if(this["_" + arguments[i] + "Children"]) {
                    children = children.concat(this["_" + arguments[i] + "Children"]);
                }
            }
            return children;
        }
    },

    /*
     * Returns true if someNode is a child of this pregnancy.
     *
     * @param someNode is an AbstractPerson
     */
    hasChild: function(someNode) {
        return this.getChildren(someNode.getType()).indexOf(someNode) > -1;
    },

    /*
     * Creates a new node and sets it as a child of this pregnancy. Returns the child.
     *
     * @param type can be any type of AbstractPerson
     */
    createChild: function(type) {
        if(this.isChildTypeValid(type) ) {
            var id = this.getID();
            var pos = editor.findPosition({below: id}, ['child']);
            var child = editor.getGraph()["add" + type](pos['child'].x, pos['child'].y, this.getGender());
            this.addChild(child);
            //document.fire("pedigree:child:added", {node: child, 'relatedNodes' : [], 'sourceNode' : this});
        }
    },

    /*
     * Attaches this pregnancy to someNode
     *
     * @param someNode is an AbstractPerson
     */
    addChild: function(someNode) {
        if(someNode && this.canBeParentOf(someNode)) {
            this["_" + someNode.getType() + "Children"].push(someNode);
            someNode.setParentPregnancy(this);
            this.getGraphics().addChild(someNode);
            this.updateActive()
            this.setGender(someNode.getGender());
        }
        return someNode;
    },

    /*
     * Breaks the connection between the pregnancy and child.
     * Returns child;
     *
     * @param child is an AbstractPerson
     */
    removeChild: function(child) {
        this.getGraphics().removeChild(child);
        child.setParentPregnancy(null);
        this["_" + child.getType() + "Children"] = this["_" + child.getType() + "Children"].without(child);
        if(this.getChildren().length == 0) {
            this.remove();
        }
        else {
            this.updateActive();
        }
        return child;
    },

    /*
     * Removes this pregnancy and all the visuals attached to it from the graph.
     * Set isRecursive to true to remove all children, unless they have some other connection
     * to the Proband
     *
     * @param isRecursive can be true or false
     */
    remove: function($super, isRecursive) {
        editor.getGraph().removePartnership(this);
        if(isRecursive) {
            $super(isRecursive);
        }
        else {
            var me = this;
            this.getChildren().each(function(child) {
                me.removeChild(child);
                if(child.getType() == 'PlaceHolder') {
                    child.remove(false, true);
                }
            });
            this.getGraphics().remove();
            this.getPartnership().removePregnancy(me);
        }
    },

    /*
     * Returns an array of children nodes of this pregnancy
     */
    getLowerNeighbors: function() {
        return this.getChildren();
    },

    /*
     * Returns an empty array
     */
    getSideNeighbors: function() {
        return [];
    },

    /*
     * Returns the parent partnership
     */
    getUpperNeighbors: function() {
        return this.getPartnership();
    },

    /*
     * Returns true if someNode can be a child of this pregnancy
     *
     * @param someNode is an AbstractNode
     */
    canBeParentOf: function(someNode) {
        var compatibleGender = someNode.getGender() == this.getGender() || this.getGender() == "U";
        return compatibleGender && this.isChildTypeValid(someNode.getType()) && this.getPartnership().canBeParentOf(someNode);
    },

    /*
     * Returns true if a child of type 'nodeType' can be added to this pregnancy
     *
     * @nodeType can be any type of AbstractNode, such as "Person", "PlaceHolder" and "PersonGroup"
     */
    isChildTypeValid: function(nodeType) {
        var validType = editor.getGraph()["add" + nodeType];
        var validPlaceHolder = nodeType != "PlaceHolder" || this.getChildren("PlaceHolder").length == 0;
        var validPersonGroup = nodeType != "PersonGroup" || this.getChildren("Person").length == 0;
        var noPersonGroups = this.getChildren("PersonGroup").length == 0;
        return validType && validPlaceHolder && validPersonGroup && noPersonGroups;
    },

    /*
     * Returns true is the only child of this pregnancy is a PlaceHolder
     */
    isPlaceHolderPregnancy: function() {
        var children = this.getChildren();
        return children.length = 1 && children[0].getType() == "PlaceHolder";
    },

    /*
     * Removes someNode from this pregnancy, and places it into a new one
     *
     * @param someNode is an AbstractPerson
     */
    separateFromPregnancy: function(someNode) {
        if(this.hasChild(someNode)) {
            this.removeChild(someNode);
            this.getPartnership().addChild(someNode);
        }
    },

    /*
     * Toggles the pregnancy junction interactivity active and inactive
     *
     * @param isActive set to true if interacting with the junction is allowed
     */
    setActive: function(isActive) {
        if(this._isActive != isActive) {
            this._isActive = isActive;
            this.getGraphics().updateActive();
        }
    },

    /*
     * Returns true if the user can currently interactively add new children to this pregnancy
     */
    isActive: function() {
        return this._isActive;
    },

    /*
     * Checks whether the pregnancy junction should be active and updates the active status
     */
    updateActive: function() {
        this.setActive(this.getChildren("PlaceHolder", "PersonGroup").length == 0);
    }
});<|endoftext|>


<commit_before>/*!
    Copyright (C) 2013 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: brad@reciprocitylabs.com
    Maintained By: brad@reciprocitylabs.com
*/

(function(can, $) {
/*  RefreshQueue
 *
 *  enqueue(obj, force=false) -> queue or null
 *  trigger() -> Deferred
 */

can.Construct("ModelRefreshQueue", {
}, {
    init: function(model) {
      this.model = model;
      this.ids = [];
      this.deferred = new $.Deferred();
      this.triggered = false;
      this.completed = false;
      this.updated_at = Date.now();
    }

  , enqueue: function(id) {
      if (this.triggered) {
        return null;
      }
      if (this.ids.indexOf(id) === -1) {
        this.ids.push(id);
        this.updated_at = Date.now();
      }
      return this;
    }

  , trigger: function () {
      var self = this;
      if (!this.triggered) {
        this.triggered = true;
        if (this.ids.length) {
          this.model.findAll({ id__in: this.ids.join(",") }).then(function () {
            self.completed = true;
            self.deferred.resolve();
          }, function () {
            self.deferred.reject.apply(self.deferred, arguments);
          });
        } else {
          this.completed = true;
          this.deferred.resolve();
        }
      }
      return this.deferred;
    }

  , trigger_with_debounce: function(delay, manager) {
      var ms_to_wait = (delay || 0) + this.updated_at - Date.now();

      if (!this.triggered) {
        if (ms_to_wait < 0 && (!manager || manager.triggered_queues().length < 6)) {
          this.trigger();
        } else {
          setTimeout(this.proxy('trigger_with_debounce', delay, manager), ms_to_wait);
        }
      }

      return this.deferred;
    }
});

can.Construct("RefreshQueueManager", {
    model_bases: {
      // This won't work until Relatable/Documentable/etc mixins can handle
      // queries with multiple `type` values.
      //  Regulation: 'Directive'
      //, Contract: 'Directive'
      //, Policy: 'Directive'
      //, Standard: 'Directive'
      //, System: 'SystemOrProcess'
      //, Process: 'SystemOrProcess'
    }
}, {
    init: function() {
      this.null_queue = new ModelRefreshQueue(null);
      this.queues = [];
    }

  , triggered_queues: function() {
      return can.map(this.queues, function(queue) {
        if (queue.triggered)
          return queue;
      });
    }

  , enqueue: function(obj, force) {
      var self = this
        , model = obj.constructor
        , model_name = model.shortName
        , found_queue = null
        , id = obj.id
        ;

      if (!obj.selfLink) {
        if (obj instanceof can.Model) {
          model_name = obj.constructor.shortName;
        } else if (obj.type) {
          // FIXME: obj.kind is to catch invalid stubs coming from Directives
          model_name = obj.type || obj.kind;
        }
      }
      model = CMS.Models[model_name];

      if (this.constructor.model_bases[model_name]) {
        model_name = this.constructor.model_bases[model_name];
        model = CMS.Models[model_name];
      }

      if (!force)
        // Check if the ID is already contained in another queue
        can.each(this.queues, function(queue) {
          if (!found_queue
              && queue.model === model && queue.ids.indexOf(id) > -1)
            found_queue = queue;
        });

      if (!found_queue) {
        can.each(this.queues, function(queue) {
          if (!found_queue && queue.model === model
              && !queue.triggered && queue.ids.length < 150) {
            found_queue = queue.enqueue(id);
            return false;
          }
        });
        if (!found_queue) {
          found_queue = new ModelRefreshQueue(model);
          this.queues.push(found_queue)
          found_queue.enqueue(id);
          found_queue.deferred.done(function() {
            var index = self.queues.indexOf(found_queue);
            if (index > -1)
              self.queues.splice(index, 1);
          });
        }
      }

      return found_queue;
    }
});

can.Construct("RefreshQueue", {
    refresh_queue_manager: new RefreshQueueManager(),
    refresh_all: function(instance, props, force) {
      var dfd = new $.Deferred();

      _refresh_all(instance, props, dfd);
      return dfd;

      // Helper function called recursively for each property
      function _refresh_all(instance, props, dfd) {
        var prop = props[0],
            next_props = props.slice(1),
            next = instance[prop],
            refresh_queue = new RefreshQueue(),
            dfds = [],
            deferred;

        if (next) {
          refresh_queue.enqueue(next, force);
          deferred = refresh_queue.trigger();
        } else if (instance.get_binding) {
          next = instance.get_binding(prop);
          if (next) {
            deferred = next.refresh_instances(force);
          }
        }
        if (deferred) {
          deferred.then(function(refreshed_items) {
            if (next_props.length) {
              can.each(refreshed_items, function(item) {
                var d = new $.Deferred();
                _refresh_all(item, next_props, d);
                dfds.push(d);
              });
              // Resolve the original deferred only when all list deferreds
              //   have been resolved
              $.when.apply($, dfds).then(function(items) {
                dfd.resolve(items);
              }, function() {
                dfd.reject.apply(this, arguments);
              });
              return;
            }
            // All items were refreshed, resolve the deferred
            if (next.push || next.list) {
              // Last refreshed property was a list
              dfd.resolve(refreshed_items);
            }
            // Last refreshed property was a single instance, return it as such
            dfd.resolve(refreshed_items[0]);
          }, function() {
            dfd.reject.apply(this, arguments);
          });
        } else {
          console.warn("refresh_all failed at", prop);
        }
      }
    },
}, {
    init: function() {
      this.objects = [];
      this.queues = [];
      this.deferred = new $.Deferred();
      this.triggered = false;
      this.completed = false;

      return this;
    }

  , enqueue: function (obj, force) {
      if (!obj) {
        return;
      }
      if (this.triggered) {
        return null;
      }
      if (obj.push) {
        can.each(obj, function (o) {
          this.enqueue(o, force);
        }, this);
        return this;
      }

      this.objects.push(obj);
      if (force || !obj.selfLink) {
        queue = this.constructor.refresh_queue_manager.enqueue(obj, force);
        if (this.queues.indexOf(queue) === -1)
          this.queues.push(queue);
      }
      return this;
    }

  , trigger: function (delay) {
      var self = this
        , deferreds = []
        ;

      if (!delay) {
        delay = 150;
      }

      this.triggered = true;
      can.each(this.queues, function(queue) {
        deferreds.push(queue.trigger_with_debounce(delay, self.constructor.refresh_queue_manager));
      });

      if (deferreds.length) {
        $.when.apply($, deferreds).then(function () {
          self.deferred.resolve(can.map(self.objects, function (obj) {
            return obj.reify();
          }));
        }, function () {
          self.deferred.reject.apply(self.deferred, arguments);
        });
      } else {
        return this.deferred.resolve(this.objects);
      }

      return this.deferred;
    }
});

})(window.can, window.can.$);
<commit_msg>Referesh queue hangs request if binding doesn't exist<commit_after>/*!
    Copyright (C) 2013 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: brad@reciprocitylabs.com
    Maintained By: brad@reciprocitylabs.com
*/

(function(can, $) {
/*  RefreshQueue
 *
 *  enqueue(obj, force=false) -> queue or null
 *  trigger() -> Deferred
 */

can.Construct("ModelRefreshQueue", {
}, {
    init: function(model) {
      this.model = model;
      this.ids = [];
      this.deferred = new $.Deferred();
      this.triggered = false;
      this.completed = false;
      this.updated_at = Date.now();
    }

  , enqueue: function(id) {
      if (this.triggered) {
        return null;
      }
      if (this.ids.indexOf(id) === -1) {
        this.ids.push(id);
        this.updated_at = Date.now();
      }
      return this;
    }

  , trigger: function () {
      var self = this;
      if (!this.triggered) {
        this.triggered = true;
        if (this.ids.length) {
          this.model.findAll({ id__in: this.ids.join(",") }).then(function () {
            self.completed = true;
            self.deferred.resolve();
          }, function () {
            self.deferred.reject.apply(self.deferred, arguments);
          });
        } else {
          this.completed = true;
          this.deferred.resolve();
        }
      }
      return this.deferred;
    }

  , trigger_with_debounce: function(delay, manager) {
      var ms_to_wait = (delay || 0) + this.updated_at - Date.now();

      if (!this.triggered) {
        if (ms_to_wait < 0 && (!manager || manager.triggered_queues().length < 6)) {
          this.trigger();
        } else {
          setTimeout(this.proxy('trigger_with_debounce', delay, manager), ms_to_wait);
        }
      }

      return this.deferred;
    }
});

can.Construct("RefreshQueueManager", {
    model_bases: {
      // This won't work until Relatable/Documentable/etc mixins can handle
      // queries with multiple `type` values.
      //  Regulation: 'Directive'
      //, Contract: 'Directive'
      //, Policy: 'Directive'
      //, Standard: 'Directive'
      //, System: 'SystemOrProcess'
      //, Process: 'SystemOrProcess'
    }
}, {
    init: function() {
      this.null_queue = new ModelRefreshQueue(null);
      this.queues = [];
    }

  , triggered_queues: function() {
      return can.map(this.queues, function(queue) {
        if (queue.triggered)
          return queue;
      });
    }

  , enqueue: function(obj, force) {
      var self = this
        , model = obj.constructor
        , model_name = model.shortName
        , found_queue = null
        , id = obj.id
        ;

      if (!obj.selfLink) {
        if (obj instanceof can.Model) {
          model_name = obj.constructor.shortName;
        } else if (obj.type) {
          // FIXME: obj.kind is to catch invalid stubs coming from Directives
          model_name = obj.type || obj.kind;
        }
      }
      model = CMS.Models[model_name];

      if (this.constructor.model_bases[model_name]) {
        model_name = this.constructor.model_bases[model_name];
        model = CMS.Models[model_name];
      }

      if (!force)
        // Check if the ID is already contained in another queue
        can.each(this.queues, function(queue) {
          if (!found_queue
              && queue.model === model && queue.ids.indexOf(id) > -1)
            found_queue = queue;
        });

      if (!found_queue) {
        can.each(this.queues, function(queue) {
          if (!found_queue && queue.model === model
              && !queue.triggered && queue.ids.length < 150) {
            found_queue = queue.enqueue(id);
            return false;
          }
        });
        if (!found_queue) {
          found_queue = new ModelRefreshQueue(model);
          this.queues.push(found_queue)
          found_queue.enqueue(id);
          found_queue.deferred.done(function() {
            var index = self.queues.indexOf(found_queue);
            if (index > -1)
              self.queues.splice(index, 1);
          });
        }
      }

      return found_queue;
    }
});

can.Construct("RefreshQueue", {
    refresh_queue_manager: new RefreshQueueManager(),
    refresh_all: function(instance, props, force) {
      var dfd = new $.Deferred();

      _refresh_all(instance, props, dfd);
      return dfd;

      // Helper function called recursively for each property
      function _refresh_all(instance, props, dfd) {
        var prop = props[0];
        var next_props = props.slice(1);
        var next = instance[prop];
        var refresh_queue = new RefreshQueue();
        var dfds = [];
        var deferred;
        var hasBinding;

        if (next) {
          refresh_queue.enqueue(next, force);
          deferred = refresh_queue.trigger();
        } else if (instance.get_binding) {
          next = instance.get_binding(prop);
          hasBinding = instance.has_binding(prop);

          if (hasBinding && next) {
            deferred = next.refresh_instances(force);
          }
        }
        if (deferred) {
          deferred.then(function(refreshed_items) {
            if (next_props.length) {
              can.each(refreshed_items, function(item) {
                var d = new $.Deferred();
                _refresh_all(item, next_props, d);
                dfds.push(d);
              });
              // Resolve the original deferred only when all list deferreds
              //   have been resolved
              $.when.apply($, dfds).then(function(items) {
                dfd.resolve(items);
              }, function() {
                dfd.reject.apply(this, arguments);
              });
              return;
            }
            // All items were refreshed, resolve the deferred
            if (next.push || next.list) {
              // Last refreshed property was a list
              dfd.resolve(refreshed_items);
            }
            // Last refreshed property was a single instance, return it as such
            dfd.resolve(refreshed_items[0]);
          }, function() {
            dfd.reject.apply(this, arguments);
          });
        } else {
          console.warn("refresh_all failed at", prop);
        }
      }
    },
}, {
    init: function() {
      this.objects = [];
      this.queues = [];
      this.deferred = new $.Deferred();
      this.triggered = false;
      this.completed = false;

      return this;
    }

  , enqueue: function (obj, force) {
      if (!obj) {
        return;
      }
      if (this.triggered) {
        return null;
      }
      if (obj.push) {
        can.each(obj, function (o) {
          this.enqueue(o, force);
        }, this);
        return this;
      }

      this.objects.push(obj);
      if (force || !obj.selfLink) {
        queue = this.constructor.refresh_queue_manager.enqueue(obj, force);
        if (this.queues.indexOf(queue) === -1)
          this.queues.push(queue);
      }
      return this;
    }

  , trigger: function (delay) {
      var self = this
        , deferreds = []
        ;

      if (!delay) {
        delay = 150;
      }

      this.triggered = true;
      can.each(this.queues, function(queue) {
        deferreds.push(queue.trigger_with_debounce(delay, self.constructor.refresh_queue_manager));
      });

      if (deferreds.length) {
        $.when.apply($, deferreds).then(function () {
          self.deferred.resolve(can.map(self.objects, function (obj) {
            return obj.reify();
          }));
        }, function () {
          self.deferred.reject.apply(self.deferred, arguments);
        });
      } else {
        return this.deferred.resolve(this.objects);
      }

      return this.deferred;
    }
});

})(window.can, window.can.$);
<|endoftext|>


<commit_before>var JsBarcode = require('jsbarcode');

var VueBarcode = {
   render: function (createElement) {
    return createElement('div', [
      createElement(this.elementTag, {
        style: { display: this.valid ? undefined : 'none' },
        'class': ['vue-barcode-element']
      }),
      createElement('div', {
        style: { display: this.valid ? 'none' : undefined }
      }, this.$slots.default),
    ]);
  },
  props: {
    value: [String, Number],
    format: [String],
    width: [String, Number],
    height: [String, Number],
    displayValue: {
      type:  [String, Boolean],
      default: true
    },
    text: [String, Number],
    fontOptions : [String],
    font: [String],
    textAlign: [String],
    textPosition: [String],
    textMargin: [String, Number],
    fontSize: [String, Number],
    background: [String],
    lineColor: [String],
    margin: [String, Number],
    marginTop: [String, Number],
    marginBottom: [String, Number],
    marginLeft: [String, Number],
    marginRight: [String, Number],
    flat: [Boolean],
    elementTag: {
      type: String,
      default: 'svg',
      validator: function (value) {
          return ['canvas', 'svg', 'img'].indexOf(value) !== -1
      }
    }
  },
  mounted: function(){
    this.$watch('$props', render, { deep: true, immediate: true });
    render.call(this);
  },
  data: function(){
    return {valid: true};
  }
};

function render(){
  var that = this;

  var settings = {
    format: this.format,
    width: this.width,
    height: this.height,
    displayValue: this.displayValue,
    text: this.text,
    fontOptions: this.fontOptions,
    font: this.font,
    textAlign: this.textAlign,
    textPosition: this.textPosition,
    textMargin: this.textMargin,
    fontSize: this.fontSize,
    background: this.background,
    lineColor: this.lineColor,
    margin: this.margin,
    marginTop: this.marginTop,
    marginBottom: this.marginBottom,
    marginLeft: this.marginLeft,
    marginRight: this.marginRight,
    flat: this.flat,
    valid: function (valid) {
      that.valid = valid;
    },
    elementTag: this.elementTag
  };

  removeUndefinedProps(settings);

  JsBarcode(this.$el.querySelector('.vue-barcode-element'), String(this.value), settings);
}

function removeUndefinedProps(obj) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop) && obj[prop] === undefined) {
      delete obj[prop];
    }
  }
}

module.exports = VueBarcode;
<commit_msg>Added ean128 option https://github.com/lindell/JsBarcode/wiki/CODE128#ean128-option-for-code128<commit_after>var JsBarcode = require('jsbarcode');

var VueBarcode = {
   render: function (createElement) {
    return createElement('div', [
      createElement(this.elementTag, {
        style: { display: this.valid ? undefined : 'none' },
        'class': ['vue-barcode-element']
      }),
      createElement('div', {
        style: { display: this.valid ? 'none' : undefined }
      }, this.$slots.default),
    ]);
  },
  props: {
    value: [String, Number],
    format: [String],
    width: [String, Number],
    height: [String, Number],
    displayValue: {
      type:  [String, Boolean],
      default: true
    },
    text: [String, Number],
    fontOptions : [String],
    font: [String],
    textAlign: [String],
    textPosition: [String],
    textMargin: [String, Number],
    fontSize: [String, Number],
    background: [String],
    lineColor: [String],
    margin: [String, Number],
    marginTop: [String, Number],
    marginBottom: [String, Number],
    marginLeft: [String, Number],
    marginRight: [String, Number],
    flat: [Boolean],
    ean128: [String, Boolean],
    elementTag: {
      type: String,
      default: 'svg',
      validator: function (value) {
          return ['canvas', 'svg', 'img'].indexOf(value) !== -1
      }
    }
  },
  mounted: function(){
    this.$watch('$props', render, { deep: true, immediate: true });
    render.call(this);
  },
  data: function(){
    return {valid: true};
  }
};

function render(){
  var that = this;

  var settings = {
    format: this.format,
    width: this.width,
    height: this.height,
    displayValue: this.displayValue,
    text: this.text,
    fontOptions: this.fontOptions,
    font: this.font,
    textAlign: this.textAlign,
    textPosition: this.textPosition,
    textMargin: this.textMargin,
    fontSize: this.fontSize,
    background: this.background,
    lineColor: this.lineColor,
    margin: this.margin,
    marginTop: this.marginTop,
    marginBottom: this.marginBottom,
    marginLeft: this.marginLeft,
    marginRight: this.marginRight,
    flat: this.flat,
    ean128: this.ean128,
    valid: function (valid) {
      that.valid = valid;
    },
    elementTag: this.elementTag
  };

  removeUndefinedProps(settings);

  JsBarcode(this.$el.querySelector('.vue-barcode-element'), String(this.value), settings);
}

function removeUndefinedProps(obj) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop) && obj[prop] === undefined) {
      delete obj[prop];
    }
  }
}

module.exports = VueBarcode;
<|endoftext|>


<commit_before>import { getScrollTarget, getScrollPosition } from '../../utils/scroll'
import { cssTransform } from '../../utils/dom'
import { QIcon } from '../icon'
import TouchPan from '../../directives/touch-pan'

export default {
  name: 'q-pull-to-refresh',
  directives: {
    TouchPan
  },
  props: {
    handler: {
      type: Function,
      required: true
    },
    distance: {
      type: Number,
      default: 35
    },
    pullMessage: String,
    releaseMessage: String,
    refreshMessage: String,
    refreshIcon: {
      type: String,
      default: 'refresh'
    },
    inline: Boolean,
    disable: Boolean
  },
  data () {
    let height = 65

    return {
      state: 'pull',
      pullPosition: -height,
      height: height,
      animating: false,
      pulling: false,
      scrolling: false
    }
  },
  computed: {
    message () {
      switch (this.state) {
        case 'pulled':
          return this.releaseMessage || this.$q.i18n.label.pullToRefresh.release
        case 'refreshing':
          return this.refreshMessage || this.$q.i18n.label.pullToRefresh.refresh
        case 'pull':
        default:
          return this.pullMessage || this.$q.i18n.label.pullToRefresh.pull
      }
    },
    style () {
      return cssTransform(`translateY(${this.pullPosition}px)`)
    }
  },
  methods: {
    __pull (event) {
      if (this.disable) {
        return
      }

      if (event.isFinal) {
        this.scrolling = false
        this.pulling = false
        if (this.scrolling) {
          return
        }
        if (this.state === 'pulled') {
          this.state = 'refreshing'
          this.__animateTo(0)
          this.trigger()
        }
        else if (this.state === 'pull') {
          this.__animateTo(-this.height)
        }
        return
      }
      if (this.animating || this.scrolling || this.state === 'refreshing') {
        return true
      }

      let top = getScrollPosition(this.scrollContainer)
      if (top !== 0 || (top === 0 && event.direction !== 'down')) {
        this.scrolling = true
        if (this.pulling) {
          this.pulling = false
          this.state = 'pull'
          this.__animateTo(-this.height)
        }
        return true
      }

      event.evt.preventDefault()
      this.pulling = true
      this.pullPosition = -this.height + Math.max(0, Math.pow(event.distance.y, 0.85))
      this.state = this.pullPosition > this.distance ? 'pulled' : 'pull'
    },
    __animateTo (target, done, previousCall) {
      if (!previousCall && this.animationId) {
        cancelAnimationFrame(this.animating)
      }

      this.pullPosition -= (this.pullPosition - target) / 7

      if (this.pullPosition - target > 1) {
        this.animating = window.requestAnimationFrame(() => {
          this.__animateTo(target, done, true)
        })
      }
      else {
        this.animating = window.requestAnimationFrame(() => {
          this.pullPosition = target
          this.animating = false
          done && done()
        })
      }
    },
    trigger () {
      this.handler(() => {
        this.__animateTo(-this.height, () => {
          this.state = 'pull'
        })
      })
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.scrollContainer = this.inline ? this.$el.parentNode : getScrollTarget(this.$el)
    })
  },
  render (h) {
    return h('div', { staticClass: 'pull-to-refresh' }, [
      h('div', {
        staticClass: 'pull-to-refresh-container',
        style: this.style,
        directives: [{
          name: 'touch-pan',
          modifiers: {
            vertical: true,
            scroll: true
          },
          value: this.__pull
        }]
      }, [
        h('div', { staticClass: 'pull-to-refresh-message row flex-center' }, [
          h(QIcon, {
            'class': { 'rotate-180': this.state === 'pulled' },
            props: { name: 'arrow_downward' },
            directives: [{
              name: 'show',
              value: this.state !== 'refreshing'
            }]
          }),
          h(QIcon, {
            staticClass: 'animate-spin',
            props: { name: this.refreshIcon },
            directives: [{
              name: 'show',
              value: this.state === 'refreshing'
            }]
          }),
          ` ${this.message}`
        ]),
        this.$slots.default
      ])
    ])
  }
}
<commit_msg>Fix i18n for QPullToRefresh (#1229)<commit_after>import { getScrollTarget, getScrollPosition } from '../../utils/scroll'
import { cssTransform } from '../../utils/dom'
import { QIcon } from '../icon'
import TouchPan from '../../directives/touch-pan'

export default {
  name: 'q-pull-to-refresh',
  directives: {
    TouchPan
  },
  props: {
    handler: {
      type: Function,
      required: true
    },
    distance: {
      type: Number,
      default: 35
    },
    pullMessage: String,
    releaseMessage: String,
    refreshMessage: String,
    refreshIcon: {
      type: String,
      default: 'refresh'
    },
    inline: Boolean,
    disable: Boolean
  },
  data () {
    let height = 65

    return {
      state: 'pull',
      pullPosition: -height,
      height: height,
      animating: false,
      pulling: false,
      scrolling: false
    }
  },
  computed: {
    message () {
      switch (this.state) {
        case 'pulled':
          return this.releaseMessage || this.$q.i18n.pullToRefresh.release
        case 'refreshing':
          return this.refreshMessage || this.$q.i18n.pullToRefresh.refresh
        case 'pull':
        default:
          return this.pullMessage || this.$q.i18n.pullToRefresh.pull
      }
    },
    style () {
      return cssTransform(`translateY(${this.pullPosition}px)`)
    }
  },
  methods: {
    __pull (event) {
      if (this.disable) {
        return
      }

      if (event.isFinal) {
        this.scrolling = false
        this.pulling = false
        if (this.scrolling) {
          return
        }
        if (this.state === 'pulled') {
          this.state = 'refreshing'
          this.__animateTo(0)
          this.trigger()
        }
        else if (this.state === 'pull') {
          this.__animateTo(-this.height)
        }
        return
      }
      if (this.animating || this.scrolling || this.state === 'refreshing') {
        return true
      }

      let top = getScrollPosition(this.scrollContainer)
      if (top !== 0 || (top === 0 && event.direction !== 'down')) {
        this.scrolling = true
        if (this.pulling) {
          this.pulling = false
          this.state = 'pull'
          this.__animateTo(-this.height)
        }
        return true
      }

      event.evt.preventDefault()
      this.pulling = true
      this.pullPosition = -this.height + Math.max(0, Math.pow(event.distance.y, 0.85))
      this.state = this.pullPosition > this.distance ? 'pulled' : 'pull'
    },
    __animateTo (target, done, previousCall) {
      if (!previousCall && this.animationId) {
        cancelAnimationFrame(this.animating)
      }

      this.pullPosition -= (this.pullPosition - target) / 7

      if (this.pullPosition - target > 1) {
        this.animating = window.requestAnimationFrame(() => {
          this.__animateTo(target, done, true)
        })
      }
      else {
        this.animating = window.requestAnimationFrame(() => {
          this.pullPosition = target
          this.animating = false
          done && done()
        })
      }
    },
    trigger () {
      this.handler(() => {
        this.__animateTo(-this.height, () => {
          this.state = 'pull'
        })
      })
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.scrollContainer = this.inline ? this.$el.parentNode : getScrollTarget(this.$el)
    })
  },
  render (h) {
    return h('div', { staticClass: 'pull-to-refresh' }, [
      h('div', {
        staticClass: 'pull-to-refresh-container',
        style: this.style,
        directives: [{
          name: 'touch-pan',
          modifiers: {
            vertical: true,
            scroll: true
          },
          value: this.__pull
        }]
      }, [
        h('div', { staticClass: 'pull-to-refresh-message row flex-center' }, [
          h(QIcon, {
            'class': { 'rotate-180': this.state === 'pulled' },
            props: { name: 'arrow_downward' },
            directives: [{
              name: 'show',
              value: this.state !== 'refreshing'
            }]
          }),
          h(QIcon, {
            staticClass: 'animate-spin',
            props: { name: this.refreshIcon },
            directives: [{
              name: 'show',
              value: this.state === 'refreshing'
            }]
          }),
          ` ${this.message}`
        ]),
        this.$slots.default
      ])
    ])
  }
}
<|endoftext|>


<commit_before>// For these tests we use a fake DBus daemon to verify powerMonitor module
// interaction with the system bus. This requires python-dbusmock installed and
// running (with the DBUS_SYSTEM_BUS_ADDRESS environment variable set).
// script/test.py will take care of spawning the fake DBus daemon and setting
// DBUS_SYSTEM_BUS_ADDRESS when python-dbusmock is installed.
//
// See https://pypi.python.org/pypi/python-dbusmock for more information about
// python-dbusmock.
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const dbus = require('dbus-native')
const Promise = require('bluebird')

const {expect} = chai
chai.use(dirtyChai)

const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRESS

describe('powerMonitor', () => {
  let logindMock, dbusMockPowerMonitor, getCalls, emitSignal, reset

  if (!skip) {
    before(async () => {
      const systemBus = dbus.systemBus()
      const loginService = systemBus.getService('org.freedesktop.login1')
      const getInterface = Promise.promisify(loginService.getInterface, {context: loginService})
      logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock')
      getCalls = Promise.promisify(logindMock.GetCalls, {context: logindMock})
      emitSignal = Promise.promisify(logindMock.EmitSignal, {context: logindMock})
      reset = Promise.promisify(logindMock.Reset, {context: logindMock})
    })

    after(async () => {
      await reset()
    })
  }

  (skip ? describe.skip : describe)('when powerMonitor module is loaded with dbus mock', () => {
    function onceMethodCalled (done) {
      function cb () {
        logindMock.removeListener('MethodCalled', cb)
      }
      done()
      return cb
    }

    before(done => {
      logindMock.on('MethodCalled', onceMethodCalled(done))
      // lazy load powerMonitor after we listen to MethodCalled mock signal
      dbusMockPowerMonitor = require('electron').remote.powerMonitor
    })

    it('should call Inhibit to delay suspend', async () => {
      const calls = await getCalls()
      expect(calls).to.be.an('array').that.has.lengthOf(1)
      expect(calls[0].slice(1)).to.deep.equal([
        'Inhibit', [
          [[{type: 's', child: []}], ['sleep']],
          [[{type: 's', child: []}], ['electron']],
          [[{type: 's', child: []}], ['Application cleanup before suspend']],
          [[{type: 's', child: []}], ['delay']]
        ]
      ])
    })

    describe('when PrepareForSleep(true) signal is sent by logind', () => {
      it('should emit "suspend" event', (done) => {
        dbusMockPowerMonitor.once('suspend', () => done())
        emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
          'b', [['b', true]])
      })

      describe('when PrepareForSleep(false) signal is sent by logind', () => {
        it('should emit "resume" event', done => {
          dbusMockPowerMonitor.once('resume', () => done())
          emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
            'b', [['b', false]])
        })

        it('should have called Inhibit again', async () => {
          const calls = await getCalls()
          expect(calls).to.be.an('array').that.has.lengthOf(2)
          expect(calls[1].slice(1)).to.deep.equal([
            'Inhibit', [
              [[{type: 's', child: []}], ['sleep']],
              [[{type: 's', child: []}], ['electron']],
              [[{type: 's', child: []}], ['Application cleanup before suspend']],
              [[{type: 's', child: []}], ['delay']]
            ]
          ])
        })
      })
    })

    describe('when a listener is added to shutdown event', () => {
      before(async () => {
        const calls = await getCalls()
        expect(calls).to.be.an('array').that.has.lengthOf(2)
        dbusMockPowerMonitor.once('shutdown', () => { })
      })

      it('should call Inhibit to delay shutdown', async () => {
        const calls = await getCalls()
        expect(calls).to.be.an('array').that.has.lengthOf(3)
        expect(calls[2].slice(1)).to.deep.equal([
          'Inhibit', [
            [[{type: 's', child: []}], ['shutdown']],
            [[{type: 's', child: []}], ['electron']],
            [[{type: 's', child: []}], ['Ensure a clean shutdown']],
            [[{type: 's', child: []}], ['delay']]
          ]
        ])
      })

      describe('when PrepareForShutdown(true) signal is sent by logind', () => {
        it('should emit "shutdown" event', done => {
          dbusMockPowerMonitor.once('shutdown', () => { done() })
          emitSignal('org.freedesktop.login1.Manager', 'PrepareForShutdown',
            'b', [['b', true]])
        })
      })
    })
  })

  describe('when powerMonitor module is loaded', () => {
    let powerMonitor
    before(() => {
      powerMonitor = require('electron').remote.powerMonitor
    })

    describe('powerMonitor.querySystemIdleState', () => {
      it('notify current system idle state', done => {
        powerMonitor.querySystemIdleState(1, idleState => {
          expect(idleState).to.equal('idle')
          done()
        })
      })

      it('does not accept non positive integer threshold', () => {
        expect(() => {
          powerMonitor.querySystemIdleState(-1, (idleState) => {})
        }).to.throw()

        expect(() => {
          powerMonitor.querySystemIdleState(NaN, (idleState) => {})
        }).to.throw()

        expect(() => {
          powerMonitor.querySystemIdleState('a', (idleState) => {})
        }).to.throw()
      })
    })

    describe('powerMonitor.querySystemIdleTime', () => {
      it('notify current system idle time', done => {
        powerMonitor.querySystemIdleTime(idleTime => {
          expect(idleTime).to.be.at.least(0)
          done()
        })
      })
    })
  })
})
<commit_msg>Revert "fix: re-enable power monitor test (#14458)" (#14506)<commit_after>// For these tests we use a fake DBus daemon to verify powerMonitor module
// interaction with the system bus. This requires python-dbusmock installed and
// running (with the DBUS_SYSTEM_BUS_ADDRESS environment variable set).
// script/test.py will take care of spawning the fake DBus daemon and setting
// DBUS_SYSTEM_BUS_ADDRESS when python-dbusmock is installed.
//
// See https://pypi.python.org/pypi/python-dbusmock for more information about
// python-dbusmock.
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const dbus = require('dbus-native')
const Promise = require('bluebird')

const {expect} = chai
chai.use(dirtyChai)

const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRESS

// TODO(alexeykuzmin): [Ch66] Crashes on Linux ia32. Fix it and enable back.
xdescribe('powerMonitor', () => {
  let logindMock, dbusMockPowerMonitor, getCalls, emitSignal, reset

  if (!skip) {
    before(async () => {
      const systemBus = dbus.systemBus()
      const loginService = systemBus.getService('org.freedesktop.login1')
      const getInterface = Promise.promisify(loginService.getInterface, {context: loginService})
      logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock')
      getCalls = Promise.promisify(logindMock.GetCalls, {context: logindMock})
      emitSignal = Promise.promisify(logindMock.EmitSignal, {context: logindMock})
      reset = Promise.promisify(logindMock.Reset, {context: logindMock})
    })

    after(reset)
  }

  (skip ? describe.skip : describe)('when powerMonitor module is loaded with dbus mock', () => {
    function onceMethodCalled (done) {
      function cb () {
        logindMock.removeListener('MethodCalled', cb)
      }
      done()
      return cb
    }

    before(done => {
      logindMock.on('MethodCalled', onceMethodCalled(done))
      // lazy load powerMonitor after we listen to MethodCalled mock signal
      dbusMockPowerMonitor = require('electron').remote.powerMonitor
    })

    it('should call Inhibit to delay suspend', async () => {
      const calls = await getCalls()
      expect(calls).to.be.an('array').that.has.lengthOf(1)
      expect(calls[0].slice(1)).to.deep.equal([
        'Inhibit', [
          [[{type: 's', child: []}], ['sleep']],
          [[{type: 's', child: []}], ['electron']],
          [[{type: 's', child: []}], ['Application cleanup before suspend']],
          [[{type: 's', child: []}], ['delay']]
        ]
      ])
    })

    describe('when PrepareForSleep(true) signal is sent by logind', () => {
      it('should emit "suspend" event', (done) => {
        dbusMockPowerMonitor.once('suspend', () => done())
        emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
          'b', [['b', true]])
      })

      describe('when PrepareForSleep(false) signal is sent by logind', () => {
        it('should emit "resume" event', done => {
          dbusMockPowerMonitor.once('resume', () => done())
          emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
            'b', [['b', false]])
        })

        it('should have called Inhibit again', async () => {
          const calls = await getCalls()
          expect(calls).to.be.an('array').that.has.lengthOf(2)
          expect(calls[1].slice(1)).to.deep.equal([
            'Inhibit', [
              [[{type: 's', child: []}], ['sleep']],
              [[{type: 's', child: []}], ['electron']],
              [[{type: 's', child: []}], ['Application cleanup before suspend']],
              [[{type: 's', child: []}], ['delay']]
            ]
          ])
        })
      })
    })

    describe('when a listener is added to shutdown event', () => {
      before(async () => {
        const calls = await getCalls()
        expect(calls).to.be.an('array').that.has.lengthOf(2)
        dbusMockPowerMonitor.once('shutdown', () => { })
      })

      it('should call Inhibit to delay shutdown', async () => {
        const calls = await getCalls()
        expect(calls).to.be.an('array').that.has.lengthOf(3)
        expect(calls[2].slice(1)).to.deep.equal([
          'Inhibit', [
            [[{type: 's', child: []}], ['shutdown']],
            [[{type: 's', child: []}], ['electron']],
            [[{type: 's', child: []}], ['Ensure a clean shutdown']],
            [[{type: 's', child: []}], ['delay']]
          ]
        ])
      })

      describe('when PrepareForShutdown(true) signal is sent by logind', () => {
        it('should emit "shutdown" event', done => {
          dbusMockPowerMonitor.once('shutdown', () => { done() })
          emitSignal('org.freedesktop.login1.Manager', 'PrepareForShutdown',
            'b', [['b', true]])
        })
      })
    })
  })

  describe('when powerMonitor module is loaded', () => {
    let powerMonitor
    before(() => {
      powerMonitor = require('electron').remote.powerMonitor
    })

    describe('powerMonitor.querySystemIdleState', () => {
      it('notify current system idle state', done => {
        powerMonitor.querySystemIdleState(1, idleState => {
          expect(idleState).to.be.true()
          done()
        })
      })

      it('does not accept non positive integer threshold', () => {
        expect(() => {
          powerMonitor.querySystemIdleState(-1, (idleState) => {})
        }).to.throw()

        expect(() => {
          powerMonitor.querySystemIdleState(NaN, (idleState) => {})
        }).to.throw()

        expect(() => {
          powerMonitor.querySystemIdleState('a', (idleState) => {})
        }).to.throw()
      })
    })

    describe('powerMonitor.querySystemIdleTime', () => {
      it('notify current system idle time', done => {
        powerMonitor.querySystemIdleTime(idleTime => {
          expect(idleTime).to.be.at.least(0)
          done()
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>/*global require: false, module: false, console: false */
'use strict';

var _ = require('../underscore_ext');
var _s = require('underscore.string');
var {parse} = require('./searchParser');

var includes = (target, str) => {
	return str.toLowerCase().indexOf(target.toLowerCase()) !== -1;
};

// Return indices of arr for which fn is true. fn is passed the value and index.
// XXX also appears in models/km. Could move to underscore_ext.
var filterIndices = (arr, fn) => _.range(arr.length).filter(i => fn(arr[i], i));

// XXX ugh. Need a tail call.
function filterSampleIds(cohortSamples, cmp, str) {
	var i = 0, res = [];
	cohortSamples.forEach(samples => {
		samples.forEach(s => {
			if (cmp(str, s)) {
				res.push(i);
			}
			++i;
		});
	});
	return res;
}

function searchSampleIds(cohortSamples, str) {
	return filterSampleIds(cohortSamples, includes, str);
}

function searchSampleIdsExact(cohortSamples, str) {
	return filterSampleIds(cohortSamples, (x, y) => x === y, str);
}

var searchCoded = _.curry((cmp, search, data) => {
	var {req: {values: [field]}, codes} = data,
		filter = search === 'null' ?
			v => v === null :
			v => _.has(codes, v) && cmp(search, codes[v]);
	return filterIndices(field, filter);
});

var tol = 0.001;
var near = _.curry((x, y) => Math.abs(x - y) < tol);

var searchFloat = _.curry((cmp, search, data) => {
	var {req: {values}} = data,
		searchVal = search === 'null' ? null : parseFloat(search);
	if (isNaN(searchVal) || values.length > 1) {
		// don't try to search strings against floats, and don't try to
		// search sub-columns.
		return [];
	}
	return filterIndices(_.map(values[0], cmp(searchVal)), _.identity);
});

var searchMutation = _.curry((cmp, search, data) => {
	var {req: {rows}} = data,
		matchingRows = _.filter(rows, row => _.any(row, v => cmp(search, _.isString(v) ? v : String(v))));

	return _.uniq(_.pluck(matchingRows, 'sample'));
});

var searchMethod = {
	coded: searchCoded(includes),
	float: searchFloat(near),
	mutation: searchMutation(includes),
	samples: searchSampleIds
};

var searchExactMethod = {
	coded: searchCoded((target, value) => value === target),
	float: searchFloat(_.curry((x, y) => x === y)), // XXX does this make sense?
	mutation: searchMutation((target, value) => value === target),
	samples: searchSampleIdsExact
};

var empty = () => [];

var searchLt = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y < x))
};

var searchLe = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y <= x))
};

var searchGt = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y > x))
};

var searchGe = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y >= x))
};

var m = (methods, exp, defaultMethod) => {
	let [type, ...args] = exp,
		method = methods[type];
	return method ? method(...args) : defaultMethod(exp);
};

function searchAll(columns, methods, data, search, cohortSamples) {
	return _.union(..._.map(columns, (c, key) => methods[c.valueType](search, data[key])),
				   methods['samples'](cohortSamples, search));
}

function invert(matches, cohortSamples) {
	var total = _.sum(_.pluck(cohortSamples, 'length'));
	return _.difference(_.range(total), matches);
}

function evalFieldExp(expression, column, data, cohortSamples) {
	if (!column) {
		return [];
	}
	return m({
		value: search => searchMethod[column.valueType](search, data),
		'quoted-value': search => searchExactMethod[column.valueType](search, data),
		ne: exp => invert(evalFieldExp(exp, column, data, cohortSamples), cohortSamples),
		lt: search => searchLt[column.valueType](search, data),
		gt: search => searchGt[column.valueType](search, data),
		le: search => searchLe[column.valueType](search, data),
		ge: search => searchGe[column.valueType](search, data)
	}, expression);
}

// XXX should rename ne to not, since we've implemented it that way.
// Unary ! or NOT can be implemented using the same operation.
function evalexp(expression, columns, data, fieldMap, cohortSamples) {
	return m({
		value: search => searchAll(columns, searchMethod, data, search, cohortSamples),
		'quoted-value': search => searchAll(columns, searchExactMethod, data, search, cohortSamples),
		ne: exp => invert(evalexp(exp, columns, data, fieldMap, cohortSamples), cohortSamples),
		and: (...exprs) => _.intersection(...exprs.map(e => evalexp(e, columns, data, fieldMap, cohortSamples))),
		or: (...exprs) => _.union(...exprs.map(e => evalexp(e, columns, data, fieldMap, cohortSamples))),
		group: exp => evalexp(exp, columns, data, fieldMap),
		field: (field, exp) => evalFieldExp(exp, columns[fieldMap[field]], data[fieldMap[field]], cohortSamples)
	}, expression);
}

function createFieldIds(len) {
	const A = 'A'.charCodeAt(0);
	return _.range(len).map(i => String.fromCharCode(i + A));
}

function createFieldMap(columnOrder) {
	return _.object(createFieldIds(columnOrder.length), columnOrder);
}

function searchSamples(search, columns, columnOrder, data, cohortSamples) { //eslint-disable-line no-unused-vars
	if (!_.get(search, 'length')) {
		return null;
	}
	let fieldMap = createFieldMap(columnOrder);
	try {
		var exp = parse(_s.trim(search));
		return evalexp(exp, columns, data, fieldMap, cohortSamples);
	} catch(e) {
		console.log('parsing error', e);
		return [];
	}
}

function treeToString(tree) {
	return m({
		value: value => value,
		'quoted-value': value => `"${value}"`,
		and: (...factors) => _.map(factors, treeToString).join(' '),
		or: (...terms) => _.map(terms, treeToString).join(' OR '),
		field: (field, value) => `${field}:${treeToString(value)}`,
		ne: term => `!=${treeToString(term)}`,
		lt: value => `<${value}`,
		gt: value => `>${value}`,
		le: value => `<=${value}`,
		ge: value => `>=${value}`
	}, tree);
}

function remapTreeFields(tree, mapping) {
	return m({
		and: (...factors) => ['and', ..._.map(factors, t => remapTreeFields(t, mapping))],
		or: (...terms) => ['or', ..._.map(terms, t => remapTreeFields(t, mapping))],
		field: (field, value) => ['field', _.get(mapping, field, 'XXX'), value]
	}, tree, _.identity);
}

// Remap field ids in search expressions. For example,
//
// oldOrder: [uuid0, uuid1, ...]
// newOrder: [uuid1, uuid0, ...]
// exp: "A:foo B:bar"
// out: "A:bar B:foo"
function remapFields(oldOrder, order, exp) {
	if (!_.get(exp, 'length')) {
		return null;
	}
	var fieldIds = createFieldIds(order.length),
		oldFieldMap = _.invert(createFieldMap(oldOrder)),
		newOrder = _.map(order, uuid => oldFieldMap[uuid]),
		mapping = _.object(newOrder, fieldIds),
		tree = parse(_s.trim(exp));
	return treeToString(remapTreeFields(tree, mapping));
}

module.exports = {
	searchSamples,
	treeToString,
	remapFields,
	parse
};
<commit_msg>Fix search for 'null' on mutation & float.<commit_after>/*global require: false, module: false, console: false */
'use strict';

var _ = require('../underscore_ext');
var _s = require('underscore.string');
var {parse} = require('./searchParser');

var includes = (target, str) => {
	return str.toLowerCase().indexOf(target.toLowerCase()) !== -1;
};

function invert(matches, allSamples) {
	return _.difference(allSamples, matches);
}

// Return indices of arr for which fn is true. fn is passed the value and index.
// XXX also appears in models/km. Could move to underscore_ext.
var filterIndices = (arr, fn) => _.range(arr.length).filter(i => fn(arr[i], i));

// XXX ugh. Need a tail call.
function filterSampleIds(cohortSamples, cmp, str) {
	var i = 0, res = [];
	cohortSamples.forEach(samples => {
		samples.forEach(s => {
			if (cmp(str, s)) {
				res.push(i);
			}
			++i;
		});
	});
	return res;
}

function searchSampleIds(cohortSamples, str) {
	return filterSampleIds(cohortSamples, includes, str);
}

function searchSampleIdsExact(cohortSamples, str) {
	return filterSampleIds(cohortSamples, (x, y) => x === y, str);
}

var searchCoded = _.curry((cmp, ctx, search, data) => {
	var {req: {values: [field]}, codes} = data,
		filter = search === 'null' ?
			v => v === null :
			v => _.has(codes, v) && cmp(search, codes[v]);
	return filterIndices(field, filter);
});

var tol = 0.01;
var near = _.curry((x, y) => (x === null || y === null) ? y === x : Math.abs(x - y) < tol);

var searchFloat = _.curry((cmp, ctx, search, data) => {
	var {req: {values}} = data,
		searchVal = search === 'null' ? null : parseFloat(search);
	if (isNaN(searchVal) || values.length > 1) {
		// don't try to search strings against floats, and don't try to
		// search sub-columns.
		return [];
	}
	return filterIndices(_.map(values[0], cmp(searchVal)), _.identity);
});

var searchMutation = _.curry((cmp, {allSamples}, search, data) => {
	var {req: {rows, samplesInResp}} = data;

	if (search === 'null') {
		return invert(samplesInResp, allSamples);
	}

	let matchingRows = _.filter(rows, row => _.any(row, v => cmp(search, _.isString(v) ? v : String(v))));
	return _.uniq(_.pluck(matchingRows, 'sample'));
});

var searchMethod = {
	coded: searchCoded(includes),
	float: searchFloat(near),
	mutation: searchMutation(includes),
	samples: searchSampleIds
};

var searchExactMethod = {
	coded: searchCoded((target, value) => value === target),
	float: searchFloat(_.curry((x, y) => x === y)),
	mutation: searchMutation((target, value) => value === target),
	samples: searchSampleIdsExact
};

var empty = () => [];

var searchLt = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y < x))
};

var searchLe = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y <= x))
};

var searchGt = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y > x))
};

var searchGe = {
	coded: empty,
	mutation: empty,
	float: searchFloat(_.curry((x, y) => x !== null && y !== null && y >= x))
};

var m = (methods, exp, defaultMethod) => {
	let [type, ...args] = exp,
		method = methods[type];
	return method ? method(...args) : defaultMethod(exp);
};

function searchAll(ctx, methods, search) {
	let {cohortSamples, columns, data} = ctx;
	return _.union(..._.map(columns, (c, key) => methods[c.valueType](ctx, search, data[key])),
				   methods['samples'](cohortSamples, search));
}

function evalFieldExp(ctx, expression, column, data) {
	if (!column) {
		return [];
	}
	return m({
		value: search => searchMethod[column.valueType](ctx, search, data),
		'quoted-value': search => searchExactMethod[column.valueType](ctx, search, data),
		ne: exp => invert(evalFieldExp(ctx, exp, column, data), ctx.allSamples),
		lt: search => searchLt[column.valueType](ctx, search, data),
		gt: search => searchGt[column.valueType](ctx, search, data),
		le: search => searchLe[column.valueType](ctx, search, data),
		ge: search => searchGe[column.valueType](ctx, search, data)
	}, expression);
}

// XXX should rename ne to not, since we've implemented it that way.
// Unary ! or NOT can be implemented using the same operation.
function evalexp(ctx, expression) {
	return m({
		value: search => searchAll(ctx, searchMethod, search),
		'quoted-value': search => searchAll(ctx, searchExactMethod, search),
		ne: exp => invert(evalexp(ctx, exp), ctx.allSamples),
		and: (...exprs) => _.intersection(...exprs.map(e => evalexp(ctx, e))),
		or: (...exprs) => _.union(...exprs.map(e => evalexp(ctx, e))),
		group: exp => evalexp(ctx, exp),
		field: (field, exp) => evalFieldExp(ctx, exp, ctx.columns[ctx.fieldMap[field]], ctx.data[ctx.fieldMap[field]])
	}, expression);
}

function createFieldIds(len) {
	const A = 'A'.charCodeAt(0);
	return _.range(len).map(i => String.fromCharCode(i + A));
}

function createFieldMap(columnOrder) {
	return _.object(createFieldIds(columnOrder.length), columnOrder);
}

function searchSamples(search, columns, columnOrder, data, cohortSamples) { //eslint-disable-line no-unused-vars
	if (!_.get(search, 'length')) {
		return null;
	}
	let fieldMap = createFieldMap(columnOrder),
		allSamples = _.range(_.sum(_.pluck(cohortSamples, 'length')));
	try {
		var exp = parse(_s.trim(search));
		return evalexp({columns, data, fieldMap, cohortSamples, allSamples}, exp);
	} catch(e) {
		console.log('parsing error', e);
		return [];
	}
}

function treeToString(tree) {
	return m({
		value: value => value,
		'quoted-value': value => `"${value}"`,
		and: (...factors) => _.map(factors, treeToString).join(' '),
		or: (...terms) => _.map(terms, treeToString).join(' OR '),
		field: (field, value) => `${field}:${treeToString(value)}`,
		ne: term => `!=${treeToString(term)}`,
		lt: value => `<${value}`,
		gt: value => `>${value}`,
		le: value => `<=${value}`,
		ge: value => `>=${value}`
	}, tree);
}

function remapTreeFields(tree, mapping) {
	return m({
		and: (...factors) => ['and', ..._.map(factors, t => remapTreeFields(t, mapping))],
		or: (...terms) => ['or', ..._.map(terms, t => remapTreeFields(t, mapping))],
		field: (field, value) => ['field', _.get(mapping, field, 'XXX'), value]
	}, tree, _.identity);
}

// Remap field ids in search expressions. For example,
//
// oldOrder: [uuid0, uuid1, ...]
// newOrder: [uuid1, uuid0, ...]
// exp: "A:foo B:bar"
// out: "A:bar B:foo"
function remapFields(oldOrder, order, exp) {
	if (!_.get(exp, 'length')) {
		return null;
	}
	var fieldIds = createFieldIds(order.length),
		oldFieldMap = _.invert(createFieldMap(oldOrder)),
		newOrder = _.map(order, uuid => oldFieldMap[uuid]),
		mapping = _.object(newOrder, fieldIds),
		tree = parse(_s.trim(exp));
	return treeToString(remapTreeFields(tree, mapping));
}

module.exports = {
	searchSamples,
	treeToString,
	remapFields,
	parse
};
<|endoftext|>


<commit_before>import { chain } from 'lodash';
import { getVisible, addBasePath } from 'ui/chrome';

export function pollingNotifications($timeout, $http, NotificationCenter, Notifier) {
  if (!getVisible()) {
    return;
  }

  const { config } = NotificationCenter;
  const notify = new Notifier();
  $timeout(function pullNotifications() {
    return $http.get(addBasePath('/api/notification_center/notification'), {
      params: {
        from: config.get('lastPulledAt'),
        size: config.get('maxSize')
      }
    })
    .then(({ data }) => {
      const notifications = data || [];
      const lastPulledAt = chain(notifications)
      .forEach(notification => {
        notify[notification.type || 'info'](notification.content);
      })
      .maxBy('timestamp')
      .get('timestamp')
      .value();

      if (lastPulledAt) {
        config.set('lastPulledAt', lastPulledAt);
        config.save();
      }
    })
    .then(() => $timeout(pullNotifications, config.get('pollingInterval')));
  }, config.get('pollingInterval'));
};<commit_msg>Add system_api_header to polling_notifications.<commit_after>import { chain } from 'lodash';
import { getVisible, addBasePath } from 'ui/chrome';
import { addSystemApiHeader } from 'ui/system_api';

export function pollingNotifications($timeout, $http, NotificationCenter, Notifier) {
  if (!getVisible()) {
    return;
  }

  const { config } = NotificationCenter;
  const notify = new Notifier();
  $timeout(function pullNotifications() {
    return $http.get(addBasePath('/api/notification_center/notification'), {
      headers: addSystemApiHeader({}),
      params: {
        from: config.get('lastPulledAt'),
        size: config.get('maxSize')
      }
    })
    .then(({ data }) => {
      const notifications = data || [];
      const lastPulledAt = chain(notifications)
      .forEach(notification => {
        notify[notification.type || 'info'](notification.content);
      })
      .maxBy('timestamp')
      .get('timestamp')
      .value();

      if (lastPulledAt) {
        config.set('lastPulledAt', lastPulledAt);
        config.save();
      }
    })
    .then(() => $timeout(pullNotifications, config.get('pollingInterval')));
  }, config.get('pollingInterval'));
};<|endoftext|>


<commit_before>var chai = require('chai');
var expect = require('chai').expect;
var sinon = require('sinon');

chai.should();
chai.use(require('sinon-chai'));

var Plugin = require('../lib/plugin');
var HOOKS = require('../lib/hooks');
var configureLoaderPath = require('../lib/configurator/loader').LOADER_PATH;
var InMemoryCompiler = require('../lib/modules/MemoryCompiler');
var mergeConfig = require('webpack-config-merger');
var Source = require('../lib/data/Source');

function inMemoryCompiler(config) {
  var defaultConfig = {
    context: '/',

    output: {
      filename: '[name].js',
      path: '/build'
    }
  };

  var compiler = new InMemoryCompiler(
    mergeConfig(defaultConfig, config || {}),
    true,
    true
  );

  return compiler;
}

describe('Plugin', function () {

  describe('static props', function () {
    Plugin.should.have.property('HOOKS').and.be.a('object').and.eql(HOOKS);
  });

  describe('::extract()', function () {

    it('should exist', function () {
      Plugin.extract.should.exist.and.be.a('function');
    });

    it('should return path to configure loader', function () {
      Plugin.extract().should.contain(configureLoaderPath);
    });

    it('should return path with query params when options are presented', function () {
      var params = {qwe: 123};
      Plugin.extract({qwe: 123}).should.contain(JSON.stringify(params));
    });

  });

  describe('constructor', function () {

    it('should set initial config', function () {
      var plugin = new Plugin();
      plugin.should.have.property('config').and.be.an('object');
      plugin.should.have.property('extractors').and.be.an('object');
      plugin.config.should.have.property('loaders').and.be.an('array');
    });

  });

  describe('getConfig()', function () {

    it('should exist', function () {
      new Plugin().getConfig.should.exist;
    });

    it('should return initial config if no argument specified', function () {
      var plugin = new Plugin();
      plugin.getConfig().should.eql(plugin.config.initial);
    });

    describe('when plugin instantiated', function () {
      var plugin;
      var compiler;

      beforeEach(function () {
        plugin = new Plugin();
        compiler = inMemoryCompiler({
          module: {
            loaders: [
              {test: /\.qwe$/, loader: Plugin.extract({foo: 'bar'})}
            ]
          },
          plugins: [plugin]
        });
      });

      it('should return config provided in loaders', function (done) {
        compiler.run().then(function () {
          var config = plugin.getConfig('/tralala/test.qwe');
          config.should.have.property('foo');
          config.foo.should.be.eql('bar');
          done();
        });
      });

      it('should return initial config value if no param specified via loaders found', function (done) {
        compiler.run().then(function () {
          var config = plugin.getConfig('/tralala');
          config.should.be.eql(plugin.config.initial);
          done();
        });
      });

    });

  });

  describe('registerExtractor()', function () {

    it('should throw if invalid extractor structure provided', function() {
      var extractor = {
        apply: function (compiler) {
          var extractor = this;

          compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
            plugin.registerExtractor(extractor);
          })
        }
      };

      expect(function() {
        inMemoryCompiler({plugins: [ new Plugin(), extractor ]})
      }).to.throw();
    });

    it('should register extractor and throw when it already registered', function (done) {
      var extractorName = 'tralala';
      var extractor = {
        getName: function () { return extractorName },
        extract: function () {},
        apply: function (compiler) {
          var extractor = this;

          compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
            plugin.registerExtractor(extractor);
          })
        }
      };

      var plugin = new Plugin();

      inMemoryCompiler({plugins: [plugin, extractor]}).run().then(function () {
        plugin.extractors.should.have.property(extractorName).and.be.eql(extractor);

        expect(function() {
          plugin.registerExtractor({
            getName: function() { return 'tralala' },
            extract: function() {},
            apply: function() {
              var extractor = this;

              compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
                plugin.registerExtractor(extractor);
              })
            }
          });
        }).to.throw();

        done();
      });

    });

  });

  describe('save()', function () {

    it('should save source', function() {
      var source = new Source({path: '/qwe', absolutePath: '/qwe', content: 'qwe'});
      var plugin = new Plugin();

      plugin.save(source);
      plugin.sources.should.include(source).and.to.have.lengthOf(1);

      plugin.save(source);
      plugin.sources.should.include(source).and.to.have.lengthOf(1);
    })

  });

  describe('readFile()', function () {

    it('should use the same filesystem with compiler', function() {
      var file = {
        path: '/test.txt',
        content: 'qwe'
      };

      var plugin = new Plugin();
      var compiler = inMemoryCompiler({plugins: [plugin]});

      compiler.inputFileSystem.writeFileSync(file.path, file.content, 'utf-8');
      plugin.readFile(file.path).then(function(content) {
        content.toString().should.eql(file.content);
      });
    })

  });

  describe('addEntry()', function () {

    it('should add entry point to compiler', function() {

    });

  });

  describe('apply()', function () {

    it('should fill `config.loaders`', function() {
      var plugin = new Plugin();
      var loader = {
        test: /\.qwe$/,
        include: /qwe/,
        exclude: /tralala/,
        loader: Plugin.extract({option: 1}),
        customProp: 123
      };

      inMemoryCompiler({
        module: { loaders: [loader] },
        plugins: [plugin]
      }).run().then(function() {
        plugin.config.loaders.should.be.an('array').and.have.lengthOf(1);
      });
    });

    it('should invoke plugins at HOOKS.CONFIGURE hook in sync mode', function() {
      var spiedPluginBody = sinon.spy();
      var cfgPlugin = {
        apply: function(compiler) {
          compiler.plugin(HOOKS.CONFIGURE, spiedPluginBody);
        }
      };

      inMemoryCompiler({ plugins: [new Plugin, cfgPlugin] }).run().then(function() {
        spiedPluginBody.should.have.been.calledOnce;
      });
    });

    it('should do nothing if no files to process', function() {
      var spiedPluginBody = sinon.spy();
      var sourcesCreatedPlugin = {
        apply: function(compiler) {
          compiler.plugin('compilation', function(compilation) {
            compilation.plugin(HOOKS.SOURCES_CREATED, spiedPluginBody);
          });
        }
      };

      inMemoryCompiler({ plugins: [new Plugin, sourcesCreatedPlugin] }).run().then(function() {
        spiedPluginBody.should.have.callCount(0);
      })
    });
  });
});<commit_msg>Add plugin test<commit_after>var chai = require('chai');
var expect = require('chai').expect;
var sinon = require('sinon');

chai.should();
chai.use(require('sinon-chai'));

var Plugin = require('../lib/plugin');
var HOOKS = require('../lib/hooks');
var configureLoaderPath = require('../lib/configurator/loader').LOADER_PATH;
var InMemoryCompiler = require('../lib/modules/MemoryCompiler');
var mergeConfig = require('webpack-config-merger');
var Source = require('../lib/data/Source');

function inMemoryCompiler(config) {
  var defaultConfig = {
    context: '/',

    output: {
      filename: '[name].js',
      path: '/build'
    }
  };

  var compiler = new InMemoryCompiler(
    mergeConfig(defaultConfig, config || {}),
    true,
    true
  );

  return compiler;
}

describe('Plugin', function () {

  describe('static props', function () {
    Plugin.should.have.property('HOOKS').and.be.a('object').and.eql(HOOKS);
  });

  describe('::extract()', function () {

    it('should exist', function () {
      Plugin.extract.should.exist.and.be.a('function');
    });

    it('should return path to configure loader', function () {
      Plugin.extract().should.contain(configureLoaderPath);
    });

    it('should return path with query params when options are presented', function () {
      var params = {qwe: 123};
      Plugin.extract({qwe: 123}).should.contain(JSON.stringify(params));
    });

  });

  describe('constructor', function () {

    it('should set initial config', function () {
      var plugin = new Plugin();
      plugin.should.have.property('config').and.be.an('object');
      plugin.should.have.property('extractors').and.be.an('object');
      plugin.config.should.have.property('loaders').and.be.an('array');
    });

  });

  describe('getConfig()', function () {

    it('should exist', function () {
      new Plugin().getConfig.should.exist;
    });

    it('should return initial config if no argument specified', function () {
      var plugin = new Plugin();
      plugin.getConfig().should.eql(plugin.config.initial);
    });

    describe('when plugin instantiated', function () {
      var plugin;
      var compiler;

      beforeEach(function () {
        plugin = new Plugin();
        compiler = inMemoryCompiler({
          module: {
            loaders: [
              {test: /\.qwe$/, loader: Plugin.extract({foo: 'bar'})}
            ]
          },
          plugins: [plugin]
        });
      });

      it('should return config provided in loaders', function (done) {
        compiler.run().then(function () {
          var config = plugin.getConfig('/tralala/test.qwe');
          config.should.have.property('foo');
          config.foo.should.be.eql('bar');
          done();
        });
      });

      it('should return initial config value if no param specified via loaders found', function (done) {
        compiler.run().then(function () {
          var config = plugin.getConfig('/tralala');
          config.should.be.eql(plugin.config.initial);
          done();
        });
      });

    });

  });

  describe('registerExtractor()', function () {

    it('should throw if invalid extractor structure provided', function() {
      var extractor = {
        apply: function (compiler) {
          var extractor = this;

          compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
            plugin.registerExtractor(extractor);
          })
        }
      };

      expect(function() {
        inMemoryCompiler({plugins: [ new Plugin(), extractor ]})
      }).to.throw();
    });

    it('should register extractor and throw when it already registered', function (done) {
      var extractorName = 'tralala';
      var extractor = {
        getName: function () { return extractorName },
        extract: function () {},
        apply: function (compiler) {
          var extractor = this;

          compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
            plugin.registerExtractor(extractor);
          })
        }
      };

      var plugin = new Plugin();

      inMemoryCompiler({plugins: [plugin, extractor]}).run().then(function () {
        plugin.extractors.should.have.property(extractorName).and.be.eql(extractor);

        expect(function() {
          plugin.registerExtractor({
            getName: function() { return 'tralala' },
            extract: function() {},
            apply: function() {
              var extractor = this;

              compiler.plugin(HOOKS.CONFIGURE, function (plugin) {
                plugin.registerExtractor(extractor);
              })
            }
          });
        }).to.throw();

        done();
      });

    });

  });

  describe('save()', function () {

    it('should save source', function() {
      var source = new Source({path: '/qwe', absolutePath: '/qwe', content: 'qwe'});
      var plugin = new Plugin();

      plugin.save(source);
      plugin.sources.should.include(source).and.to.have.lengthOf(1);

      plugin.save(source);
      plugin.sources.should.include(source).and.to.have.lengthOf(1);
    })

  });

  describe('readFile()', function () {

    it('should use the same filesystem with compiler', function() {
      var file = {
        path: '/test.txt',
        content: 'qwe'
      };

      var plugin = new Plugin();
      var compiler = inMemoryCompiler({plugins: [plugin]});

      compiler.inputFileSystem.writeFileSync(file.path, file.content, 'utf-8');
      plugin.readFile(file.path).then(function(content) {
        content.toString().should.eql(file.content);
      });
    })

  });

  describe('addEntry()', function () {

    it('should add entry point to compiler', function() {

    });

  });

  describe('apply()', function () {

    it('should fill `config.loaders`', function() {
      var plugin = new Plugin();
      var loader = {
        test: /\.qwe$/,
        include: /qwe/,
        exclude: /tralala/,
        loader: Plugin.extract({option: 1}),
        customProp: 123
      };

      inMemoryCompiler({
        module: { loaders: [loader] },
        plugins: [plugin]
      }).run().then(function() {
        plugin.config.loaders.should.be.an('array').and.have.lengthOf(1);
      });
    });

    it('should invoke plugins at CONFIGURE hook in sync mode', function() {
      var spiedPluginBody = sinon.spy();
      var plugin = {
        apply: function(compiler) {
          compiler.plugin(HOOKS.CONFIGURE, spiedPluginBody);
        }
      };

      inMemoryCompiler({ plugins: [new Plugin, plugin] }).run().then(function() {
        spiedPluginBody.should.have.been.calledOnce;
      });
    });

    it('should do nothing if no files to process', function() {
      var spiedPluginBody = sinon.spy();
      var plugin = {
        apply: function(compiler) {
          compiler.plugin('compilation', function(compilation) {
            compilation.plugin(HOOKS.SOURCES_CREATED, spiedPluginBody);
          });
        }
      };

      inMemoryCompiler({ plugins: [new Plugin, plugin] }).run().then(function() {
        spiedPluginBody.should.have.not.been.called;
      });
    });

    it('should handle SOURCES_CREATED hook', function() {
      var spiedPluginBody = sinon.spy();

      var plugin = {
        apply: function(compiler) {
          compiler.plugin('compilation', function(compilation) {
            compilation.plugin(HOOKS.SOURCES_CREATED, spiedPluginBody);
          });
        }
      };

      var compiler = inMemoryCompiler({
        entry: './qwe',
        module: {
          loaders: [
            {
              test: /\.js$/,
              loader: Plugin.extract()
            }
          ]
        },
        plugins: [new Plugin, plugin]
      });

      compiler.inputFileSystem.writeFileSync('/qwe.js', 'qwe', 'utf-8');

      compiler.run().then(function() {
        spiedPluginBody.should.have.callCount(1);
      });
    });

  });
});<|endoftext|>