<commit_before>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

(function()
{
  var globalId = 0;
  var modelToJson = function(model) {
    return model.toJSON();
  };

  /**
   * Collections are ordered sets of models. You can bind individual "change" events
   * to be notified when any model in the collection has been modified,
   * listen for "add" and "remove" events, fetch the collection from the
   * server, etc.
   */
  core.Class("core.mvc.Collection",
  {
    include: [core.property.MGeneric, core.event.MEvent],
    implement : [core.mvc.IModel],

    /**
     * Prefill the collection with @models {core.mvc.Model[]}.
     */
    construct: function(models)
    {
      this.__models = [];
      this.__length = 0;

      if (models != null) {
        this.append(models);
      }

      this.id = "collection:" + (globalId++);
    },

    events :
    {
      "change" : core.event.Notification
    },

    properties :
    {
      /**
       * Model to use for creating new entries.
       */
      model :
      {
        type: core.mvc.Model,
        nullable : true
      },

      /**
       * Base URL to construct URLs with to load/save data from/to the server.
       */
      url :
      {
        type: "String",
        nullable : true
      }
    },

    members :
    {
      // Interface implementation
      getId : function() {
        return this.id;
      },

      // Interface implementation
      toJSON : function() {
        return this.__models.map(modelToJson);
      },

      /**
       * Imports an array of @models {core.mvc.Model[]} into the collection.
       */
      append : function(models)
      {
        var db = this.__models;
        var addEvent = core.mvc.event.AddModel.obtain(null);

        for (var i=0, l=models.length, model; i<l; i++)
        {
          model = models[i];
          db.push(model);
          this.length++;

          addEvent.model = model;
          this.dispatchEvent(addEvent);
        }

        core.mvc.event.AddModel.release(addEvent);
      },

      /**
       * Clears the collection so that all models are removed from it.
       */
      clear : function()
      {
        var db = this.__models;
        var length = db.length;

        if (db.length == 0) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(null);

        for (var i=length, model; i>=0; i--)
        {
          model = models[i];

          removeEvent.model = model;
          this.dispatchEvent(removeEvent);

          // Pop out last model
          db.length--;

          // Update internal length, too
          this.length--;
        }

        core.mvc.event.RemoveModel.release(removeEvent);
      },

      /**
       * Combined call to replace all existing data with new list of @models {core.mvc.Model}.
       */
      reset : function(models)
      {
        this.clear();
        this.append(models);
      },

      /** {core.mvc.Model} Returns the model at the given @index {Integer}. */
      at : function(index) {
        return this.__models[index] || null;
      },

      /** {core.mvc.Model} Removes and returns the last model of the collection. */
      pop : function()
      {
        var removedModel = this.__models.pop();
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);
      },

      /**
       * Pushes a new @model {core.mvc.Model} to the end of the collection.
       */
      push : function(model)
      {
        this.__models.push(model);
        var addEvent = core.mvc.event.AddModel.obtain(model);
        this.dispatchEvent(addEvent);
        core.mvc.event.RemoveModel.release(addEvent);
      },

      /** {core.mvc.Model} Removes and returns the first model of the collection. */
      shift : function()
      {
        var removedModel = this.__models.shift();
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);

        return removedModel;
      },

      /**
       * Pushes a new @model {core.mvc.Model} to the beginning of the collection.
       */
      unshift: function(model)
      {
        this.__models.unshift(model);
        var addEvent = core.mvc.event.AddModel.obtain(model);
        this.dispatchEvent(addEvent);
        core.mvc.event.RemoveModel.release(addEvent);
      },

      /** {core.mvc.Model} Removes and returns the given @model {core.mvc.Model} of the collection. */
      remove : function(model)
      {
        /** #require(ext.sugar.Array) */
        var removedModel = this.__models.remove(model);
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);

        return removedModel;
      },

      /**
       * {core.mvc.Collection} Returns a new collection filtered by the given filter @method {Function}.
       */
      filter : function(method) {
        return this.__models.filter(method);
      },

      /**
       * {Array} Creates a new array with the results of calling a provided
       * @callback {Function} on every model in this collection. It's possible
       * to define the execution @context {Object?} of every @callback call.
       * The context defaults to the global object. The parameters with which the
       * callback is executed are: `value`, `key`, `array`.
       */
      map : function(callback, context) {
        return this.__models.map(callback, context);
      },

      /**
       * Pluck a @property {String} from each model in the collection.
       * Equivalent to calling {#map}, and returning a single property from the iterator.
       */
      pluck : function(property)
      {
        return this.__models.map(function() {
          return this.get(property);
        });
      },

      /**
       * {core.mvc.Model} Returns the model with the given @id {String}.
       */
      get : function(id)
      {
        var models = this.__models;
        for (var i=0, l=models.length; i<l; i++)
        {
          if (models[i].getId() == id) {
            return models[i];
          }
        }

        return null;
      },

      /**
       * {Map[]} The method is called by whenever a collection's models are returned
       * by the server, in `fetch`. The function is passed the raw @response {Object} object,
       * and should return the array of model attributes to be added to the collection.
       *
       * The default implementation is a no-op, simply passing through the JSON response.
       * Override this if you need to work with a preexisting API, or better namespace
       * your responses. Note that afterwards, if your model class already has a parse
       * function, it will be run against each fetched model.
       */
      parse : function(response) {
        return response;
      },

      /**
       * Fetch the default set of models for this collection from the server, resetting the
       * collection when they arrive. The options hash takes success and error callbacks which
       * will be passed (collection, response, options) and (collection, xhr, options)
       * as arguments, respectively. When the model data returns from the server,
       * the collection will reset. Delegates to `sync` under the covers for
       * custom persistence strategies and returns a XHR. The server handler
       * for fetch requests should return a JSON array of models.
       */
      fetch : function() {
        // TODO
      },

      /**
       * {core.mvc.Model} Creates a new model with the given
       * @properties {Map?} and appends it to the collection.
       */
      create : function(properties)
      {
        var modelClass = this.getModel();

        if (!modelClass) {
          throw new Error("create() requires a model being assigned to work!");
        }

        // Prefer pooling when available
        var model = modelClass.obtain ? modelClass.obtain(properties) : new modelClass(properties);

        // Auto-append
        this.push(model);

        return model;
      }

    }
  });
})();<commit_msg>Allow non-models in collection. Added getLength(). Fixed length operation. Fixed append. Allow multi args push(). Minor fixes<commit_after>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

(function()
{
  var globalId = 0;
  var modelToJson = function(model) {
    return model.toJSON ? model.toJSON() : model;
  };

  /**
   * Collections are ordered sets of models. You can bind individual "change" events
   * to be notified when any model in the collection has been modified,
   * listen for "add" and "remove" events, fetch the collection from the
   * server, etc.
   */
  core.Class("core.mvc.Collection",
  {
    include: [core.property.MGeneric, core.event.MEvent],
    implement : [core.mvc.IModel],

    /**
     * Prefill the collection with @models {core.mvc.Model[]}.
     */
    construct: function(models)
    {
      // Do not directly use given models to have a internal
      // "protected" copy of the original data and using
      // the real append() method instead.
      this.__models = [];

      // Inject given models
      if (models != null) {
        this.append(models);
      }

      this.id = "collection:" + (globalId++);
    },

    events :
    {
      "change" : core.event.Notification
    },

    properties :
    {
      /**
       * Model to use for creating new entries.
       */
      model :
      {
        type: core.mvc.Model,
        nullable : true
      },

      /**
       * Base URL to construct URLs with to load/save data from/to the server.
       */
      url :
      {
        type: "String",
        nullable : true
      }
    },

    members :
    {
      // Interface implementation
      getId : function() {
        return this.id;
      },

      // Interface implementation
      toJSON : function() {
        return this.__models.map(modelToJson);
      },

      /**
       * {Integer} Returns the length of the collection.
       */
      getLength : function() {
        return this.__models.length;
      },

      /**
       * Imports an array of @models {core.mvc.Model[]} into the collection.
       */
      append : function(models)
      {
        var db = this.__models;
        var addEvent = core.mvc.event.AddModel.obtain(null);

        for (var i=0, l=models.length, model; i<l; i++)
        {
          model = models[i];
          db.push(model);

          addEvent.model = model;
          this.dispatchEvent(addEvent);
        }

        core.mvc.event.AddModel.release(addEvent);
      },

      /**
       * Clears the collection so that all models are removed from it.
       */
      clear : function()
      {
        var db = this.__models;
        var length = db.length;

        if (db.length == 0) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(null);

        for (var i=length, model; i>0; i--)
        {
          model = db[i];

          // Pop out last model
          db.length--;

          // Inform others
          removeEvent.model = model;
          this.dispatchEvent(removeEvent);
        }

        core.mvc.event.RemoveModel.release(removeEvent);
      },

      /**
       * Combined call to replace all existing data with new list of @models {core.mvc.Model}.
       */
      reset : function(models)
      {
        this.clear();
        this.append(models);
      },

      /** {core.mvc.Model} Returns the model at the given @index {Integer}. */
      at : function(index) {
        return this.__models[index] || null;
      },

      /** {core.mvc.Model} Removes and returns the last model of the collection. */
      pop : function()
      {
        var removedModel = this.__models.pop();
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);

        return removedModel;
      },

      /**
       * Pushes a new (or multiple) @model {core.mvc.Model...} to the end of the collection.
       */
      push : function(model)
      {
        for (var i=0, l=arguments.length; i<l; i++)
        {
          model = arguments[i];
          this.__models.push(model);
          var addEvent = core.mvc.event.AddModel.obtain(model);
          this.dispatchEvent(addEvent);
          core.mvc.event.AddModel.release(addEvent);
        }
      },

      /** {core.mvc.Model} Removes and returns the first model of the collection. */
      shift : function()
      {
        var removedModel = this.__models.shift();
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);

        return removedModel;
      },

      /**
       * Pushes a new @model {core.mvc.Model} to the beginning of the collection.
       */
      unshift: function(model)
      {
        this.__models.unshift(model);
        var addEvent = core.mvc.event.AddModel.obtain(model);
        this.dispatchEvent(addEvent);
        core.mvc.event.RemoveModel.release(addEvent);
      },

      /** {core.mvc.Model} Removes and returns the given @model {core.mvc.Model} of the collection. */
      remove : function(model)
      {
        /** #require(ext.sugar.Array) */
        var removedModel = this.__models.remove(model);
        if (!removedModel) {
          return;
        }

        var removeEvent = core.mvc.event.RemoveModel.obtain(removedModel);
        this.dispatchEvent(removeEvent);
        core.mvc.event.RemoveModel.release(removeEvent);

        return removedModel;
      },

      /**
       * {core.mvc.Collection} Returns a new collection filtered by the given filter @method {Function}.
       */
      filter : function(method) {
        return this.__models.filter(method);
      },

      /**
       * {Array} Creates a new array with the results of calling a provided
       * @callback {Function} on every model in this collection. It's possible
       * to define the execution @context {Object?} of every @callback call.
       * The context defaults to the global object. The parameters with which the
       * callback is executed are: `value`, `key`, `array`.
       */
      map : function(callback, context) {
        return this.__models.map(callback, context);
      },

      /**
       * Pluck a @property {String} from each model in the collection.
       * Equivalent to calling {#map}, and returning a single property from the iterator.
       */
      pluck : function(property)
      {
        return this.__models.map(function() {
          return this.get(property);
        });
      },

      /**
       * {core.mvc.Model} Returns the model with the given @id {String}.
       */
      get : function(id)
      {
        var models = this.__models;
        for (var i=0, l=models.length; i<l; i++)
        {
          if (models[i].getId() == id) {
            return models[i];
          }
        }

        return null;
      },

      /**
       * {Map[]} The method is called by whenever a collection's models are returned
       * by the server, in `fetch`. The function is passed the raw @response {Object} object,
       * and should return the array of model attributes to be added to the collection.
       *
       * The default implementation is a no-op, simply passing through the JSON response.
       * Override this if you need to work with a preexisting API, or better namespace
       * your responses. Note that afterwards, if your model class already has a parse
       * function, it will be run against each fetched model.
       */
      parse : function(response) {
        return response;
      },

      /**
       * Fetch the default set of models for this collection from the server, resetting the
       * collection when they arrive. The options hash takes success and error callbacks which
       * will be passed (collection, response, options) and (collection, xhr, options)
       * as arguments, respectively. When the model data returns from the server,
       * the collection will reset. Delegates to `sync` under the covers for
       * custom persistence strategies and returns a XHR. The server handler
       * for fetch requests should return a JSON array of models.
       */
      fetch : function() {
        // TODO
      },

      /**
       * {core.mvc.Model} Creates a new model with the given
       * @properties {Map?} and appends it to the collection.
       */
      create : function(properties)
      {
        var modelClass = this.getModel();

        if (!modelClass) {
          throw new Error("create() requires a model being assigned to work!");
        }

        // Prefer pooling when available
        var model = modelClass.obtain ? modelClass.obtain(properties) : new modelClass(properties);

        // Auto-append
        this.push(model);

        return model;
      }

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


<commit_before>/**
 * Basic helpers related the tracking of changes in content, and flattening a
 * content tree (including distributed content).
 */

(function() {

/*
 * Given a array of nodes, return a new array with any content elements expanded
 * to the nodes distributed to that content element. This rule is applied
 * recursively.
 *
 * If includeTextNodes is true, text nodes will be included, as in the
 * standard childNodes property; by default, this skips text nodes, like the
 * standard children property.
 */
function expandContentElements(nodes, includeTextNodes) {
  var expanded = Array.prototype.map.call(nodes, function(node) {
    // We want to see if the node is an instanceof HTMLContentElement, but
    // that class won't exist if the browser that doesn't support native
    // Shadow DOM and if the Shadow DOM polyfill hasn't been loaded. Instead,
    // we do a simplistic check to see if the tag name is "content".
    if (node.localName && node.localName === "content") {
      // content element; use its distributed nodes instead.

      // HACK: In Polymer 0.8 rc 7, a call to getDistributedNodes() can fail if
      // made too early. Until that can be fixed, do a pre-check to make sure
      // the call will succeed.
      if (node._distributedNodes || node.getDistributedNodes) {
        var distributedNodes = Polymer.dom(node).getDistributedNodes();
        return expandContentElements(distributedNodes, includeTextNodes);
      } else {
        return [];
      }

    } else if (node instanceof HTMLElement) {
      // Plain element; use as is.
      return [node];
    } else if (node instanceof Text && includeTextNodes) {
      // Text node.
      return [node];
    } else {
      // Comment, processing instruction, etc.; skip.
      return [];
    }
  }.bind(this));
  var flattened = Array.prototype.concat.apply([], expanded);
  return flattened;
}

// Invoke an element's contentChanged handler -- but first, see if the elements'
// new content includes a content element, in which case we'll need to monitor
// the component's host for changes, too.
function handleContentChanged(mutations) {

  // TODO: Decide whether we want to treat attribute changes as content changes.
  // Special case: attribute mutations on the element itself aren't considered
  // changes to content, so we ignore them. (Attribute changes are only
  // treated as content changes if they apply to children.)
  // if (mutations) {
  //   var selfAttributeMutations = mutations.filter(function(mutation) {
  //     return mutation.target === this && mutation.type === 'attributes';
  //   }.bind(this));
  //   if (selfAttributeMutations.length === mutations.length) {
  //     // All mutations were only modifications to this element's own attributes.
  //     return;
  //   }
  // }

  // Filter out mutations which occurred in Shady DOM.
  if (mutations) {
    var mutationsInLightDom = mutations.filter(function(mutation) {
      return isLightDomMutation(mutation, this);
    }.bind(this));
    if (mutationsInLightDom.length === 0) {
      // All mutations were only modifications in Shady DOM.
      return;
    }
  }

  observeHostIfContentElementPresent(this);

  // Invoke the element's own handler.
  this.contentChanged();
}

// Return true if the node is a light DOM child of the indicated component.
function isLightDomDescendant(node, component) {
  var parent = Polymer.dom(node).parentNode;
  if (parent === component) {
    // The parent itself is in the component's light DOM.
    return true;
  } else if (parent instanceof HTMLElement) {
    // Walk up to see if the parent is in component's light DOM.
    return isLightDomDescendant(parent, component);
  } else {
    return false;
  }
}

// Return true if the given mutation only affects the component's light DOM
// (and not Shady DOM).
function isLightDomMutation(mutation, component) {
  // See if at least one added node was in light DOM.
  return Array.prototype.some.call(mutation.addedNodes, function(addedNode) {
    return isLightDomDescendant(addedNode, component);
  });
}

// Wire up an observer for (light DOM) content change events on the given
// node.
function observeContentMutations(node, handler) {
  node._contentChangeObserver = new MutationObserver(handler);
  node._contentChangeObserver.observe(node, {
    // attributes: true,
    characterData: true,
    childList: true,
    subtree: true
  });
}

// If the indicated node contains a content element, the changes in the *host*
// element's light DOM content will cause nodes to be redistributed to this
// node. In order to notified of such content changes, we wire up a mutation
// observed on the host. We also recursively call this function on the host: it
// may very well contain a content element itself, meaning that we'll have to
// observe content changes on *its* host, and so on...
function observeHostIfContentElementPresent(node) {
  var contentElement = Polymer.dom(node).querySelector('content');
  if (contentElement) {
    // The element's new content contains at least one content element, so we
    // also need to observe our host element, too.
    // TODO: If this element's content changes again, disconnect any
    // outstanding observers of that old content.
    var host = Basic.ContentHelpers.getHost(node);
    if (host) {
      observeContentMutations(host, function() {
        node.contentChanged();
      });
      observeHostIfContentElementPresent(host);
    }
  }
}

window.Basic = window.Basic || {};

/*
 * A collection of content helpers that can be used by any element.
 */
window.Basic.ContentHelpers = {

  /*
   * Returns an in-order collection of children, expanding any content nodes.
   * Like the standard children property, this skips text nodes.
   *
   * TODO: This walks the whole content tree every time the list is requested.
   * It'd be nice to cache the answer and invalidate it only when content
   * actually changes.
   */
  flattenChildren: function(node) {
    var children = Polymer.dom(node).children;
    return expandContentElements(children, false);
  },

  /*
   * Returns an in-order collection of child nodes, expanding any content nodes.
   * Like the standard childNodes property, this includes text nodes.
   */
  flattenChildNodes: function(node) {
    var childNodes = Polymer.dom(node).childNodes;
    return expandContentElements(childNodes, true);
  },

  /*
   * Removes nodes distributed to <content></content>
   */
  removeLightDomNodes: function(node) {
    var content = Polymer.dom(node.root).querySelector('content');
    var distributedNodes = Polymer.dom(content).getDistributedNodes();
    if (!distributedNodes) {
      return;
    }
    for (var i = 0; i < distributedNodes.length; i++) {
      Polymer.dom(node).removeChild(distributedNodes[i]);
    }
  },

  /*
   * Returns the concatenated text content of all child nodes, expanding any
   * content nodes.
   */
  flattenTextContent: function(node) {
    var flattenedChildNodes = Basic.ContentHelpers.flattenChildNodes(node);
    var strings = flattenedChildNodes.map(function(child) {
      return child.textContent;
    });
    return strings.join('');
  },

  /**
   * Return the host element for this element. If this element is not hosted by
   * another element (it's not attached to anything, or is hosted by the
   * top-level document), this returns null.
   *
   * This getter is used by Basic.ContentHelpers itself, but is also useful
   * generally.
   */
  getHost: function(node) {
    var parent = Polymer.dom(node).parentNode;
    while (parent) {
      // REVIEW: Is there an official Shady DOM way to get the host?
      // var host = Polymer.dom(parent)._hostForNode(parent);
      var host = parent.host;
      if (host) {
        return host;
      }
      parent = Polymer.dom(parent).parentNode;
    }
    return null;
  },

  /**
   * Wire up a MutationObserver that will invoke an element's contentChanged
   * handler whenever the element's content changes.
   *
   * In many cases, a component performs processing of its own children: it
   * performs layout operations on them, it modifies them. There is currently
   * (March 2015) no concise, standard means of doing this. Moreover, handling
   * reprojected Shadow DOM content is non-trivial.
   *
   * This approach abstracts that complexity behind a "contentChanged" handler.
   * This will be invoked whenever the component's content changes, including
   * reprojected content. Additionally, if a component currently has content,
   * the contentChanged handler will be immediately invoked.
   *
   * If the optional observeChanges parameter is false, this function will
   * disconnect any existing observer.
   *
   * This method is typically invoked by a component's attached handler, and
   * the invoked with observeChanges = false in the detached handler.
   *
   * @method observeContentChanges
   */
  observeContentChanges: function(node, observeChanges) {
    if (observeChanges || observeChanges == null) {
      // Start observing
      observeContentMutations(node, handleContentChanged.bind(node));
      if (Polymer.dom(node).childNodes.length > 0) {
        // Consider any initial content of a new element to be "changed" content.
        handleContentChanged.call(node);
      }
    } else if (node._contentChangeObserver) {
      // Stop observing
      node._contentChangeObserver.disconnect();
      node._contentChangeObserver = null;
    }
  }

};

/*
 * A Polymer element behavior that automatically wires up content observation,
 * and simplifies access to the various content-flattening helper functions.
 */
window.Basic.ContentChanged = {

  attached: function() {
    Basic.ContentHelpers.observeContentChanges(this);
  },

  detached: function() {
    Basic.ContentHelpers.observeContentChanges(this, false);
  },

  get flattenChildren() {
    return Basic.ContentHelpers.flattenChildren(this);
  },

  get flattenChildNodes() {
    return Basic.ContentHelpers.flattenChildNodes(this);
  },

  get flattenTextContent() {
    return Basic.ContentHelpers.flattenTextContent(this);
  }

};

})();
<commit_msg>Add comment about limitations of detecting content changes.<commit_after>/**
 * Basic helpers related the tracking of changes in content, and flattening a
 * content tree (including distributed content).
 *
 */

(function() {

/*
 * Given a array of nodes, return a new array with any content elements expanded
 * to the nodes distributed to that content element. This rule is applied
 * recursively.
 *
 * If includeTextNodes is true, text nodes will be included, as in the
 * standard childNodes property; by default, this skips text nodes, like the
 * standard children property.
 */
function expandContentElements(nodes, includeTextNodes) {
  var expanded = Array.prototype.map.call(nodes, function(node) {
    // We want to see if the node is an instanceof HTMLContentElement, but
    // that class won't exist if the browser that doesn't support native
    // Shadow DOM and if the Shadow DOM polyfill hasn't been loaded. Instead,
    // we do a simplistic check to see if the tag name is "content".
    if (node.localName && node.localName === "content") {
      // content element; use its distributed nodes instead.

      // HACK: In Polymer 0.8 rc 7, a call to getDistributedNodes() can fail if
      // made too early. Until that can be fixed, do a pre-check to make sure
      // the call will succeed.
      if (node._distributedNodes || node.getDistributedNodes) {
        var distributedNodes = Polymer.dom(node).getDistributedNodes();
        return expandContentElements(distributedNodes, includeTextNodes);
      } else {
        return [];
      }

    } else if (node instanceof HTMLElement) {
      // Plain element; use as is.
      return [node];
    } else if (node instanceof Text && includeTextNodes) {
      // Text node.
      return [node];
    } else {
      // Comment, processing instruction, etc.; skip.
      return [];
    }
  }.bind(this));
  var flattened = Array.prototype.concat.apply([], expanded);
  return flattened;
}

// Invoke an element's contentChanged handler -- but first, see if the elements'
// new content includes a content element, in which case we'll need to monitor
// the component's host for changes, too.
function handleContentChanged(mutations) {

  // TODO: Decide whether we want to treat attribute changes as content changes.
  // Special case: attribute mutations on the element itself aren't considered
  // changes to content, so we ignore them. (Attribute changes are only
  // treated as content changes if they apply to children.)
  // if (mutations) {
  //   var selfAttributeMutations = mutations.filter(function(mutation) {
  //     return mutation.target === this && mutation.type === 'attributes';
  //   }.bind(this));
  //   if (selfAttributeMutations.length === mutations.length) {
  //     // All mutations were only modifications to this element's own attributes.
  //     return;
  //   }
  // }

  // Filter out mutations which occurred in Shady DOM.
  if (mutations) {
    var mutationsInLightDom = mutations.filter(function(mutation) {
      return isLightDomMutation(mutation, this);
    }.bind(this));
    if (mutationsInLightDom.length === 0) {
      // All mutations were only modifications in Shady DOM.
      return;
    }
  }

  observeHostIfContentElementPresent(this);

  // Invoke the element's own handler.
  this.contentChanged();
}

// Return true if the node is a light DOM child of the indicated component.
function isLightDomDescendant(node, component) {
  var parent = Polymer.dom(node).parentNode;
  if (parent === component) {
    // The parent itself is in the component's light DOM.
    return true;
  } else if (parent instanceof HTMLElement) {
    // Walk up to see if the parent is in component's light DOM.
    return isLightDomDescendant(parent, component);
  } else {
    return false;
  }
}

// Return true if the given mutation only affects the component's light DOM
// (and not Shady DOM).
function isLightDomMutation(mutation, component) {
  // See if at least one added node was in light DOM.
  return Array.prototype.some.call(mutation.addedNodes, function(addedNode) {
    return isLightDomDescendant(addedNode, component);
  });
}

// Wire up an observer for (light DOM) content change events on the given
// node.
function observeContentMutations(node, handler) {
  node._contentChangeObserver = new MutationObserver(handler);
  node._contentChangeObserver.observe(node, {
    // attributes: true,
    characterData: true,
    childList: true,
    subtree: true
  });
}

// If the indicated node contains a content element, the changes in the *host*
// element's light DOM content will cause nodes to be redistributed to this
// node. In order to notified of such content changes, we wire up a mutation
// observed on the host. We also recursively call this function on the host: it
// may very well contain a content element itself, meaning that we'll have to
// observe content changes on *its* host, and so on...
function observeHostIfContentElementPresent(node) {
  var contentElement = Polymer.dom(node).querySelector('content');
  if (contentElement) {
    // The element's new content contains at least one content element, so we
    // also need to observe our host element, too.
    // TODO: If this element's content changes again, disconnect any
    // outstanding observers of that old content.
    var host = Basic.ContentHelpers.getHost(node);
    if (host) {
      observeContentMutations(host, function() {
        node.contentChanged();
      });
      observeHostIfContentElementPresent(host);
    }
  }
}

window.Basic = window.Basic || {};

/*
 * A collection of content helpers that can be used by any element.
 */
window.Basic.ContentHelpers = {

  /*
   * Returns an in-order collection of children, expanding any content nodes.
   * Like the standard children property, this skips text nodes.
   *
   * TODO: This walks the whole content tree every time the list is requested.
   * It'd be nice to cache the answer and invalidate it only when content
   * actually changes.
   */
  flattenChildren: function(node) {
    var children = Polymer.dom(node).children;
    return expandContentElements(children, false);
  },

  /*
   * Returns an in-order collection of child nodes, expanding any content nodes.
   * Like the standard childNodes property, this includes text nodes.
   */
  flattenChildNodes: function(node) {
    var childNodes = Polymer.dom(node).childNodes;
    return expandContentElements(childNodes, true);
  },

  /*
   * Removes nodes distributed to <content></content>
   */
  removeLightDomNodes: function(node) {
    var content = Polymer.dom(node.root).querySelector('content');
    var distributedNodes = Polymer.dom(content).getDistributedNodes();
    if (!distributedNodes) {
      return;
    }
    for (var i = 0; i < distributedNodes.length; i++) {
      Polymer.dom(node).removeChild(distributedNodes[i]);
    }
  },

  /*
   * Returns the concatenated text content of all child nodes, expanding any
   * content nodes.
   */
  flattenTextContent: function(node) {
    var flattenedChildNodes = Basic.ContentHelpers.flattenChildNodes(node);
    var strings = flattenedChildNodes.map(function(child) {
      return child.textContent;
    });
    return strings.join('');
  },

  /**
   * Return the host element for this element. If this element is not hosted by
   * another element (it's not attached to anything, or is hosted by the
   * top-level document), this returns null.
   *
   * This getter is used by Basic.ContentHelpers itself, but is also useful
   * generally.
   */
  getHost: function(node) {
    var parent = Polymer.dom(node).parentNode;
    while (parent) {
      // REVIEW: Is there an official Shady DOM way to get the host?
      // var host = Polymer.dom(parent)._hostForNode(parent);
      var host = parent.host;
      if (host) {
        return host;
      }
      parent = Polymer.dom(parent).parentNode;
    }
    return null;
  },

  /**
   * Wire up a MutationObserver that will invoke an element's contentChanged
   * handler whenever the element's content changes.
   *
   * In many cases, a component performs processing of its own children: it
   * performs layout operations on them, it modifies them. There is currently
   * (March 2015) no concise, standard means of doing this. Moreover, handling
   * reprojected Shadow DOM content is non-trivial.
   *
   * This approach abstracts that complexity behind a "contentChanged" handler.
   * This will be invoked whenever the component's content changes, including
   * reprojected content. Additionally, if a component currently has content,
   * the contentChanged handler will be immediately invoked.
   *
   * If the optional observeChanges parameter is false, this function will
   * disconnect any existing observer.
   *
   * This method is typically invoked by a component's attached handler, and
   * the invoked with observeChanges = false in the detached handler.
   *
   * NOTE: This content observation system currently works with native Shadow
   * DOM, but only partially supports Polymer's Shady DOM. Specifically, when
   * using Shady DOM, a component will detect nodes *added* to it (including
   * when a node is replacing another node), but the component will not
   * correctly detect when nodes are only *removed* from it.
   *
   * @method observeContentChanges
   */
  observeContentChanges: function(node, observeChanges) {
    if (observeChanges || observeChanges == null) {
      // Start observing
      observeContentMutations(node, handleContentChanged.bind(node));
      if (Polymer.dom(node).childNodes.length > 0) {
        // Consider any initial content of a new element to be "changed" content.
        handleContentChanged.call(node);
      }
    } else if (node._contentChangeObserver) {
      // Stop observing
      node._contentChangeObserver.disconnect();
      node._contentChangeObserver = null;
    }
  }

};

/*
 * A Polymer element behavior that automatically wires up content observation,
 * and simplifies access to the various content-flattening helper functions.
 */
window.Basic.ContentChanged = {

  attached: function() {
    Basic.ContentHelpers.observeContentChanges(this);
  },

  detached: function() {
    Basic.ContentHelpers.observeContentChanges(this, false);
  },

  get flattenChildren() {
    return Basic.ContentHelpers.flattenChildren(this);
  },

  get flattenChildNodes() {
    return Basic.ContentHelpers.flattenChildNodes(this);
  },

  get flattenTextContent() {
    return Basic.ContentHelpers.flattenTextContent(this);
  }

};

})();
<|endoftext|>


<commit_before>var buildGraph = require('./buildGraph');

describe('Graph builder', () => {
  it('constructs a graph with 2 nodes and 1 edge', () => {
    const nodes = ['Alice', 'Bob'];
    const expectedGraph = [{ v1: 'Alice', v2: 'Bob' }];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
  it('constructs a graph with 4 nodes and 6 edges', () => {
    const nodes = ['Alice', 'Bob', 'Cathy', 'Dan'];
    const expectedGraph = [
      { v1: 'Alice', v2: 'Bob' },
      { v1: 'Alice', v2: 'Cathy' },
      { v1: 'Alice', v2: 'Dan' },
      { v1: 'Bob', v2: 'Cathy' },
      { v1: 'Bob', v2: 'Dan' },
      { v1: 'Cathy', v2: 'Dan' }
    ];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
  it('constructs a graph with 6 nodes and 15 edges', () => {
    const nodes = ['Alice', 'Bob', 'Cathy', 'Dan', 'Ellen', 'Frank'];
    const expectedGraph = [
      { v1: 'Alice', v2: 'Bob' },
      { v1: 'Alice', v2: 'Cathy' },
      { v1: 'Alice', v2: 'Dan' },
      { v1: 'Alice', v2: 'Ellen' },
      { v1: 'Alice', v2: 'Frank' },
      { v1: 'Bob', v2: 'Cathy' },
      { v1: 'Bob', v2: 'Dan' },
      { v1: 'Bob', v2: 'Ellen' },
      { v1: 'Bob', v2: 'Frank' },
      { v1: 'Cathy', v2: 'Dan' },
      { v1: 'Cathy', v2: 'Ellen' },
      { v1: 'Cathy', v2: 'Frank' },
      { v1: 'Dan', v2: 'Ellen' },
      { v1: 'Dan', v2: 'Frank' },
      { v1: 'Ellen', v2: 'Frank' }
    ];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
});
<commit_msg>clean up<commit_after>var buildGraph = require('./buildGraph');

describe('Graph builder', () => {
  it('constructs a complete graph with 2 nodes and 1 edge', () => {
    const nodes = ['Alice', 'Bob'];
    const expectedGraph = [{ v1: 'Alice', v2: 'Bob' }];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
  it('constructs a complete graph with 4 nodes and 6 edges', () => {
    const nodes = ['Alice', 'Bob', 'Cathy', 'Dan'];
    const expectedGraph = [
      { v1: 'Alice', v2: 'Bob' },
      { v1: 'Alice', v2: 'Cathy' },
      { v1: 'Alice', v2: 'Dan' },
      { v1: 'Bob', v2: 'Cathy' },
      { v1: 'Bob', v2: 'Dan' },
      { v1: 'Cathy', v2: 'Dan' }
    ];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
  it('constructs a complete graph with 6 nodes and 15 edges', () => {
    const nodes = ['Alice', 'Bob', 'Cathy', 'Dan', 'Ellen', 'Frank'];
    const expectedGraph = [
      { v1: 'Alice', v2: 'Bob' },
      { v1: 'Alice', v2: 'Cathy' },
      { v1: 'Alice', v2: 'Dan' },
      { v1: 'Alice', v2: 'Ellen' },
      { v1: 'Alice', v2: 'Frank' },
      { v1: 'Bob', v2: 'Cathy' },
      { v1: 'Bob', v2: 'Dan' },
      { v1: 'Bob', v2: 'Ellen' },
      { v1: 'Bob', v2: 'Frank' },
      { v1: 'Cathy', v2: 'Dan' },
      { v1: 'Cathy', v2: 'Ellen' },
      { v1: 'Cathy', v2: 'Frank' },
      { v1: 'Dan', v2: 'Ellen' },
      { v1: 'Dan', v2: 'Frank' },
      { v1: 'Ellen', v2: 'Frank' }
    ];
    expect(buildGraph(nodes)).toEqual(expectedGraph);
  });
});
<|endoftext|>


<commit_before>/**
 * This file provides the mock "data" received
 * by your visualization code when you develop
 * locally.
 *
 */
export const message = {
  tables: {
    DEFAULT: [
      {
        dimID: ['25-54'],
        metricID: [128863172],
      },
      {
        dimID: ['0-14'],
        metricID: [61175933],
      },
      {
        dimID: ['65+'],
        metricID: [51055052],
      },
      {
        dimID: ['15-24'],
        metricID: [43351778],
      },
      {
        dimID: ['55-64'],
        metricID: [42179856],
      },
    ],
  },
  fields: {
    dimID: [
      {
        id: 'qt_odsbu27hzb',
        name: 'Age Group',
        type: 'TEXT',
        concept: 'DIMENSION',
      },
    ],
    metricID: [
      {
        id: 'qt_7vz3u27hzb',
        name: 'Population',
        type: 'NUMBER',
        concept: 'METRIC',
      },
    ],
  },
  style: {},
  theme: {
    themeFillColor: {
      color: '#fff',
      opacity: 1,
    },
    themeFontColor: {
      color: '#616161',
      opacity: 1,
    },
    themeFontFamily: 'Roboto',
    themeAccentFillColor: {
      color: '#4285F4',
      opacity: 1,
    },
    themeAccentFontColor: {
      color: '#EFEFEF',
      opacity: 1,
    },
    themeAccentFontFamily: 'Roboto',
    themeSeriesColor: [
      {
        color: '#4285F4',
        opacity: 1,
      },
      {
        color: '#DB4437',
        opacity: 1,
      },
      {
        color: '#F4B400',
        opacity: 1,
      },
      {
        color: '#0F9D58',
        opacity: 1,
      },
      {
        color: '#AB47BC',
        opacity: 1,
      },
      {
        color: '#00ACC1',
        opacity: 1,
      },
      {
        color: '#FF7043',
        opacity: 1,
      },
      {
        color: '#9E9D24',
        opacity: 1,
      },
      {
        color: '#5C6BC0',
        opacity: 1,
      },
      {
        color: '#F06292',
        opacity: 1,
      },
      {
        color: '#00796b',
        opacity: 1,
      },
      {
        color: '#c2185b',
        opacity: 1,
      },
      {
        color: '#7e57c2',
        opacity: 1,
      },
      {
        color: '#03a9f4',
        opacity: 1,
      },
      {
        color: '#8bc34a',
        opacity: 1,
      },
      {
        color: '#fdd835',
        opacity: 1,
      },
      {
        color: '#fb8c00',
        opacity: 1,
      },
      {
        color: '#8d6e63',
        opacity: 1,
      },
      {
        color: '#9e9e9e',
        opacity: 1,
      },
      {
        color: '#607d8b',
        opacity: 1,
      },
    ],
    themeIncreaseColor: {
      color: '#388e3c',
      opacity: 1,
    },
    themeDecreaseColor: {
      color: '#f44336',
      opacity: 1,
    },
    themeGridColor: {
      color: '#e9e9e9',
      opacity: 1,
    },
  },
  interactions: {},
};
<commit_msg>reverted localMessage<commit_after>/**
 * This file provides the mock "data" received
 * by your visualization code when you develop
 * locally.
 *
 */
export const message = {
  tables: {
    DEFAULT: [
      {
        dimID: ['25-54'],
        metricID: [128863172],
      },
      {
        dimID: ['0-14'],
        metricID: [61175933],
      },
      {
        dimID: ['65+'],
        metricID: [51055052],
      },
      {
        dimID: ['15-24'],
        metricID: [43351778],
      },
      {
        dimID: ['55-64'],
        metricID: [42179856],
      },
    ],
  },
  fields: {
    dimID: [
      {
        id: 'qt_nzqx6a0xvb',
        name: 'Age Group',
        type: 'TEXT',
        concept: 'DIMENSION',
      },
    ],
    metricID: [
      {
        id: 'qt_8isx6a0xvb',
        name: 'Population',
        type: 'NUMBER',
        concept: 'METRIC',
      },
    ],
  },
  style: {},
};
<|endoftext|>


<commit_before>define(["react", "dist/experiment_design"], function(React, ExperimentalDesign){
    return function(callback){
        var CaptureParticipants = React.createClass({
            handleChange: function(event){
                this.setState({value: parseInt(event.target.value)});
            },
            submit: function(){
               var val = this.state.value;
               if(val){
                    window.log.meta.participantNumber = val;
                    window.log.meta.experimentDesign = ExperimentalDesign(val);
                    React.unmountComponentAtNode(document.getElementById('content'));
                    callback();
               } else {
                    alert("Participant numbers are only integers. Please enter a valid integer");
               }
            },
            render: function(){
                return

                                <legend>Welcome</legend>
                                Thank you for participating in our experiment! Before you begin, could you please read the informed consent form and instruction manual in front of you. After you've filled in the consent form, please enter your participant number below.

                                Once you've submitted, you will be taken to the experiment page.

                                <legend>Participant Information</legend>

                                      Particpant Number

                                              <input id="participant_input" name="participant_input" type="text" placeholder="12345" className="form-control input-md" min="0" step="1" data-bind="value:replyNumber" required="" onChange={this.handleChange} />
                                                The participant number given to you, attached to your formal consent forms.

                              <button id="submit-participant-number" className="btn  btn-default" onClick={this.submit}>Submit</button>

            }
        });

        React.render(
            <CaptureParticipants />,
            document.getElementById("content")
        );
    };
});
<commit_msg>Changed wording on participant number capture<commit_after>define(["react", "dist/experiment_design"], function(React, ExperimentalDesign){
    return function(callback){
        var CaptureParticipants = React.createClass({
            handleChange: function(event){
                this.setState({value: parseInt(event.target.value)});
            },
            submit: function(){
               var val = this.state.value;
               if(val){
                    window.log.meta.participantNumber = val;
                    window.log.meta.experimentDesign = ExperimentalDesign(val);
                    React.unmountComponentAtNode(document.getElementById('content'));
                    callback();
               } else {
                    alert("Participant numbers are only integers. Please enter a valid integer");
               }
            },
            render: function(){
                return

                                <legend>Welcome</legend>
                                Thank you for participating in our experiment! Before you begin, could you please enter your participant number below. Once you've submitted, you will be given instructions for how to use the interface.

                                <legend>Participant Information</legend>

                                      Particpant Number

                                              <input id="participant_input" name="participant_input" type="text" placeholder="12345" className="form-control input-md" min="0" step="1" data-bind="value:replyNumber" required="" onChange={this.handleChange} />
                                                The participant number given to you, attached to your formal consent forms.

                              <button id="submit-participant-number" className="btn  btn-default" onClick={this.submit}>Submit</button>

            }
        });

        React.render(
            <CaptureParticipants />,
            document.getElementById("content")
        );
    };
});
<|endoftext|>


<commit_before>/*
 * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
 *
 * Copyright 2013 - 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

_dynamicInclude($includeFolder);
_dynamicInclude("../../common/showroom_common.js");
_dynamicInclude("../../common/login_credentials.js");

var testCase = new TestCase(60, 70);

var $username = "bearbeiter_link";

try {
    var browser = new Application(BROWSER_NAME);
    browser.focus();
    maximize(browser, "browser_maximize");

    load();
	testCase.endOfStep("Load");

    login(3);
	testCase.endOfStep("Login");

	checkLink("Hauptseite");
	checkLink("Neuer Vorgang");
	checkLink("Neuer Kontakt | Produkt");

	env.sleep(1);

} catch (e) {
    testCase.handleException(e);
}
finally {
    end();
}

function checkLink($link){

	_highlight(_link($link));
	env.sleep(1);
	_click(_link($link));
	env.sleep(1);
}

function checkItem($item){

	_click(_link("/.*choice.html#/"));
	_highlight(_listItem($item));
	env.sleep(1);
	_click(_listItem($item));
	env.sleep(1);
	_highlight(_heading4(0));
	env.sleep(1);

}<commit_msg>break test<commit_after>/*
 * Sakuli - Testing and Monitoring-Tool for Websites and common UIs.
 *
 * Copyright 2013 - 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

_dynamicInclude($includeFolder);
_dynamicInclude("../../common/showroom_common.js");
_dynamicInclude("../../common/login_credentials.js");

var testCase = new TestCase(60, 70);

var $username = "bearbeiter_link";

try {
    var browser = new Application(BROWSER_NAME);
    browser.focus();
    maximize(browser, "browser_maximize");

    load();
	testCase.endOfStep("Load");

    login(3);
	testCase.endOfStep("Login");

	checkLink("NOT_VALID_ELEMENT");
	checkLink("Neuer Vorgang");
	checkLink("Neuer Kontakt | Produkt");

	env.sleep(1);

} catch (e) {
    testCase.handleException(e);
}
finally {
    end();
}

function checkLink($link){

	_highlight(_link($link));
	env.sleep(1);
	_click(_link($link));
	env.sleep(1);
}

function checkItem($item){

	_click(_link("/.*choice.html#/"));
	_highlight(_listItem($item));
	env.sleep(1);
	_click(_listItem($item));
	env.sleep(1);
	_highlight(_heading4(0));
	env.sleep(1);

}<|endoftext|>


<commit_before>var definitions = require('./definitions');

var random = {
    // returns a single random number based on a range
    number: function (range) {
        return Math.floor(Math.random() * range);
    },

    // takes an array and returns the array randomly sorted
    array_rand: function (array) {
        var r = Math.floor(Math.random() * array.length);
        return array[r];
    },

    city_prefix: function () {
        return this.array_rand(definitions.city_prefix());
    },

    city_suffix: function () {
        return this.array_rand(definitions.city_suffix());
    },

    street_suffix: function () {
        return this.array_rand(definitions.street_suffix());
    },

    br_state: function () {
        return this.array_rand(definitions.br_state());
    },

    br_state_abbr: function () {
        return this.array_rand(definitions.br_state_abbr());
    },

    us_state: function () {
        return this.array_rand(definitions.us_state());
    },

    us_state_abbr: function () {
        return this.array_rand(definitions.us_state_abbr());
    },

    uk_county: function () {
        return this.array_rand(definitions.uk_county());
    },

    uk_country: function () {
        return this.array_rand(definitions.uk_country());
    },

    first_name: function () {
        return this.array_rand(definitions.first_name());
    },

    last_name: function () {
        return this.array_rand(definitions.last_name());
    },

    name_prefix: function () {
        return this.array_rand(definitions.name_prefix());
    },

    name_suffix: function () {
        return this.array_rand(definitions.name_suffix());
    },

    catch_phrase_adjective: function () {
        return this.array_rand(definitions.catch_phrase_adjective());
    },

    catch_phrase_descriptor: function () {
        return this.array_rand(definitions.catch_phrase_descriptor());
    },

    catch_phrase_noun: function () {
        return this.array_rand(definitions.catch_phrase_noun());
    },

    bs_adjective: function () {
        return this.array_rand(definitions.bs_adjective());
    },

    bs_buzz: function () {
        return this.array_rand(definitions.bs_buzz());
    },

    bs_noun: function () {
        return this.array_rand(definitions.bs_noun());
    }
};

/**
 * Generate methods that return random elements from definition arrays.
 */
var method_names = [
    // Address
    'city_prefix',
    'city_suffix',
    'street_suffix',
    'br_state',
    'br_state_abbr',
    'us_state',
    'us_state_abbr',
    'uk_county',
    'uk_country',

    // Name
    'first_name',
    'last_name',
    'name_prefix',
    'name_suffix',

    // Company
    'catch_phrase_adjective',
    'catch_phrase_descriptor',
    'catch_phrase_noun',
    'bs_adjective',
    'bs_buzz',
    'bs_noun'
];

/*
method_names.forEach(function (method_name) {
    random[method_name] = function () {
        return random.array_rand(definitions[method_name]());
    };
});

method_names.forEach(function (method_name) {
    var str = method_name + ': function () {\n' +
        '    return random.array_rand(definitions.' + method_name + '());\n' +
        '},\n';
    console.log(str);
});

*/

module.exports = random;
<commit_msg>cleanup old code<commit_after>var definitions = require('./definitions');

var random = {
    // returns a single random number based on a range
    number: function (range) {
        return Math.floor(Math.random() * range);
    },

    // takes an array and returns the array randomly sorted
    array_rand: function (array) {
        var r = Math.floor(Math.random() * array.length);
        return array[r];
    },

    city_prefix: function () {
        return this.array_rand(definitions.city_prefix());
    },

    city_suffix: function () {
        return this.array_rand(definitions.city_suffix());
    },

    street_suffix: function () {
        return this.array_rand(definitions.street_suffix());
    },

    br_state: function () {
        return this.array_rand(definitions.br_state());
    },

    br_state_abbr: function () {
        return this.array_rand(definitions.br_state_abbr());
    },

    us_state: function () {
        return this.array_rand(definitions.us_state());
    },

    us_state_abbr: function () {
        return this.array_rand(definitions.us_state_abbr());
    },

    uk_county: function () {
        return this.array_rand(definitions.uk_county());
    },

    uk_country: function () {
        return this.array_rand(definitions.uk_country());
    },

    first_name: function () {
        return this.array_rand(definitions.first_name());
    },

    last_name: function () {
        return this.array_rand(definitions.last_name());
    },

    name_prefix: function () {
        return this.array_rand(definitions.name_prefix());
    },

    name_suffix: function () {
        return this.array_rand(definitions.name_suffix());
    },

    catch_phrase_adjective: function () {
        return this.array_rand(definitions.catch_phrase_adjective());
    },

    catch_phrase_descriptor: function () {
        return this.array_rand(definitions.catch_phrase_descriptor());
    },

    catch_phrase_noun: function () {
        return this.array_rand(definitions.catch_phrase_noun());
    },

    bs_adjective: function () {
        return this.array_rand(definitions.bs_adjective());
    },

    bs_buzz: function () {
        return this.array_rand(definitions.bs_buzz());
    },

    bs_noun: function () {
        return this.array_rand(definitions.bs_noun());
    }
};

module.exports = random;
<|endoftext|>


<commit_before>'use strict';

let run = 'run(-script)?';
let scriptName = '\\w+|:|\\*|-';
let arg = `${scriptName}| --`;
let reserved = '(re)?start|stop|test|publish|(un)?install|version';
let reservedTest = '(te?s)?t';
let redrunArg = '--?\\w+';
let redrunEnd = ';|&&?|\\|\\|?|>|<| #';

module.exports.enter        = RegExp(`redrun\\s|(npm (${run}|${reserved}|${reservedTest}))`);
module.exports.arg          = RegExp(`npm ${run} (${arg})+`, 'g');
module.exports.name         = RegExp(`(${scriptName})+`);
module.exports.reserved     = RegExp(`npm (${reserved})`);
module.exports.reservedTest = RegExp(`^npm ${reservedTest}$`);
module.exports.redrun       = RegExp(`redrun(\\s|${redrunArg}|${scriptName}|${redrunEnd})+`);
module.exports.redrunEnd    = RegExp(`\\s+(${redrunEnd}).*`);
module.exports.cli          = cli();

function cli() {
    const version = 'v(ersion)?';
    const help = 'h(elp)?';
    const loud = 'l(oud)?';
    const series = 's(eries)?';
    const parallel = 'p(arallel)?';
    const calm = 'c(alm)?';
    const parallelCalm = 'P|parallel(C|-c)alm';
    const seriesCalm = 'S|series(C|-c)alm';

    const args = [
        version,
        help,
        loud,
        calm,
        series,
        seriesCalm,
        parallel,
        parallelCalm,
    ].join('|');

    const regexp = RegExp(`^(_|\\$0|${args})$`);

    return regexp;
}
<commit_msg>chore(regexp) rm ","<commit_after>'use strict';

let run = 'run(-script)?';
let scriptName = '\\w+|:|\\*|-';
let arg = `${scriptName}| --`;
let reserved = '(re)?start|stop|test|publish|(un)?install|version';
let reservedTest = '(te?s)?t';
let redrunArg = '--?\\w+';
let redrunEnd = ';|&&?|\\|\\|?|>|<| #';

module.exports.enter        = RegExp(`redrun\\s|(npm (${run}|${reserved}|${reservedTest}))`);
module.exports.arg          = RegExp(`npm ${run} (${arg})+`, 'g');
module.exports.name         = RegExp(`(${scriptName})+`);
module.exports.reserved     = RegExp(`npm (${reserved})`);
module.exports.reservedTest = RegExp(`^npm ${reservedTest}$`);
module.exports.redrun       = RegExp(`redrun(\\s|${redrunArg}|${scriptName}|${redrunEnd})+`);
module.exports.redrunEnd    = RegExp(`\\s+(${redrunEnd}).*`);
module.exports.cli          = cli();

function cli() {
    const version = 'v(ersion)?';
    const help = 'h(elp)?';
    const loud = 'l(oud)?';
    const series = 's(eries)?';
    const parallel = 'p(arallel)?';
    const calm = 'c(alm)?';
    const parallelCalm = 'P|parallel(C|-c)alm';
    const seriesCalm = 'S|series(C|-c)alm';

    const args = [
        version,
        help,
        loud,
        calm,
        series,
        seriesCalm,
        parallel,
        parallelCalm
    ].join('|');

    const regexp = RegExp(`^(_|\\$0|${args})$`);

    return regexp;
}
<|endoftext|>


<commit_before>
var through2        = require('through2')
var syntaxError     = require('syntax-error')
var transform       = require('./transform')
var concatStream    = require('concat-stream')
var Stream          = require('stream')
var resolve         = require('path').resolve
var getCssyConfig   = require('./utils').getCssyConfig

module.exports = function(b, config) {

  if('[object Object]' != Object.prototype.toString.call(config)) {
    config = getCssyConfig(); // Default is the cwd package.json
  }

  if(!config.basedir) {
    config.basedir = process.cwd();
  }

  var match     = config.match || ['\\.(css|sass|scss|less|styl)$', 'i'];

  if(!(match instanceof RegExp)) {
    match = RegExp.apply(null, Array.isArray(match) ? match : [match])
  }

  match = RegExp.apply(null, match)

  b.transform({global: true}, function(filename) {

    if(!match.test(filename))    return through2();

    var code      = '';
    return through2(
      function (chunk, encoding, next) {
        code += chunk.toString();
        next()
      },
      function (done) {
        var self     = this;

        if(/^module\.exports\s=/.test(code) || !syntaxError(code, filename)) {
          self.push(code);
          done();
        } else {
          var readable = through2()
          readable.pipe(transform(filename, config))
          .pipe(concatStream(function(result) {
            self.push(result)
            done();
          }))
          readable.push(code)
          readable.end()
        }
      }
    )
  })

}
<commit_msg>fix: Typo in remedy regex<commit_after>
var through2        = require('through2')
var syntaxError     = require('syntax-error')
var transform       = require('./transform')
var concatStream    = require('concat-stream')
var Stream          = require('stream')
var resolve         = require('path').resolve
var getCssyConfig   = require('./utils').getCssyConfig

module.exports = function(b, config) {

  if('[object Object]' != Object.prototype.toString.call(config)) {
    config = getCssyConfig(); // Default is the cwd package.json
  }

  if(!config.basedir) {
    config.basedir = process.cwd();
  }

  var match     = config.match || ['\\.(css|sass|scss|less|styl)$', 'i'];

  if(!(match instanceof RegExp)) {
    match = RegExp.apply(null, Array.isArray(match) ? match : [match])
  }

  match = RegExp.apply(null, match)

  b.transform({global: true}, function(filename) {

    if(!match.test(filename))    return through2();

    var code      = '';
    return through2(
      function (chunk, encoding, next) {
        code += chunk.toString();
        next()
      },
      function (done) {
        var self     = this;

        if(/^module\.exports\s?=/.test(code) || !syntaxError(code, filename)) {
          self.push(code);
          done();
        } else {
          var readable = through2()
          readable.pipe(transform(filename, config))
          .pipe(concatStream(function(result) {
            self.push(result)
            done();
          }))
          readable.push(code)
          readable.end()
        }
      }
    )
  })

}
<|endoftext|>


<commit_before>'use strict'

var htmlParser = require('htmlparser2')
var select = require('css-select')
var serialize = require('dom-serializer')

// These symbols are not re-usable.
var renderFnKey = Symbol('renderFn')
var insertionPathKey = Symbol('insertionPath')
var insertionOrderKey = Symbol('insertionOrder')
var matchedNodeKey = Symbol('matchedNode')
var hasDefinitionKey = Symbol('hasDefinition')
var parentBindingsKey = Symbol('parentBindings')

var handlerOptions = { withDomLvl1: true }

// Entities are a performance killer! However, this is potentially unsafe.
var serializeOptions = { decodeEntities: false }

var escapeMap = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"',
  '\'': '''
}

var elementExtension = {
  addEventListener: { value: noop },
  removeEventListener: { value: noop },
  setAttribute: { value: function (key, value) {
    this.attribs[key] = value
  } },
  innerHTML: {
    get: function () {
      return this.children[0].data
    },
    set: function (html) {
      this.children = [ { data: html } ]
      return html
    },
    configurable: true
  },
  classList: {
    get: function () {
      var self = this

      return {
        add: function (cls) {
          if (!('class' in self.attribs)) self.attribs['class'] = cls
          else self.attribs['class'] += ' ' + cls
        },
        remove: function (cls) {
          self.attribs['class'] = self.attribs['class'].replace(cls, '')
        }
      }
    },
    configurable: true
  }
}

var attributeAliases = {
  checked: 'checked',
  className: 'class',
  placeholder: 'placeholder',
  value: 'value',
  required: 'required',
  disabled: 'disabled'
}

var booleanAttributes = [
  'checked', 'required', 'disabled'
]

var key

for (key in attributeAliases)
  extendElement(attributeAliases[key], ~booleanAttributes.indexOf(key))

module.exports = render

/**
 * Internal function for rendering strings. The benchmark to beat is plain old
 * string concatenation and for loops. Although this won't be faster, it should
 * work with more or less the same functionality as the DOM version.
 *
 * Only a tiny subset of the actual DOM is implemented here, so it may not work
 * for all front-end functionality.
 *
 * @param {Object} obj
 * @param {Object} def
 * @param {String} [html]
 * @return {String}
 */
function render (obj, def, html) {
  var i, nodes, handler, parser, element

  // Generating the render function is processing intensive. Skip if possible.
  if (renderFnKey in def) return def[renderFnKey](obj)

  // Callback API looks weird. This is actually synchronous, not asynchronous.
  handler = new htmlParser.DomHandler(function (error, result) {
    if (error) throw error
    nodes = result
  }, handlerOptions)

  parser = new htmlParser.Parser(handler)
  parser.write(html)
  parser.end()

  for (i = nodes.length; i--;)
    if (nodes[i].type === 'tag') {
      element = nodes[i]
      break
    }

  if (!element) throw new Error('No element found!')

  Object.defineProperties(Object.getPrototypeOf(element), elementExtension)

  processDefinition(def, nodes)
  def[renderFnKey] = makeRender(def, nodes)

  return def[renderFnKey](obj)
}

function processDefinition (def, nodes) {
  var insertionIndices = []
  var i, key, branch, selector, matches, match

  for (key in def) {
    branch = def[key]

    if (typeof branch === 'function') {
      if (!(parentBindingsKey in def)) def[parentBindingsKey] = []
      def[parentBindingsKey].push(key)
      continue
    }

    if (!Array.isArray(branch)) def[key] = branch = [ branch ]
    selector = branch[0]

    if (typeof selector !== 'string') throw new Error('Selector on "' +
      key + '" must be a string.')

    matches = select(selector, nodes)

    if (!matches.length) throw new Error('Matching node not found on "' +
      key + '".')

    match = matches[0]
    locateMatch(branch, nodes, match, [])
    insertionIndices.push(branch[insertionPathKey][0])
    branch[matchedNodeKey] = match
    removeMatches(nodes, matches)

    if (typeof branch[1] === 'object') {
      branch[hasDefinitionKey] = true
      processDefinition(branch[1], match.children)
    }
  }

  def[insertionOrderKey] = Object.keys(def)
    .filter(function (key) {
      return insertionPathKey in def[key]
    })
    .sort(function (a, b) {
      return def[a][insertionPathKey][0] - def[b][insertionPathKey][0]
    })

  // Optimization: serialize static nodes in advance.
  for (i = nodes.length; i--;)
    if (!~insertionIndices.indexOf(i))
      nodes[i] = { data: serialize(nodes[i], serializeOptions) }
}

function locateMatch (branch, nodes, match, path) {
  var i, j, node

  for (i = 0, j = nodes.length; i < j; i++) {
    node = nodes[i]

    if (match === node) {
      path.push(i)
      branch[insertionPathKey] = path
      return true
    }

    if (node.children.length) {
      path.push(i)
      if (!locateMatch(branch, node.children, match, path)) path.pop()
    }
  }

  return false
}

function removeMatches (nodes, matches) {
  var i, node

  for (i = nodes.length; i--;) {
    node = nodes[i]
    if (~matches.indexOf(node)) {
      nodes.splice(i, 1)
      continue
    }
    if (node.children.length)
      removeMatches(node.children, matches)
  }
}

function makeRender (def, nodes) {
  return function (obj) {
    return serialize(fillTemplate(obj, def, nodes, obj), serializeOptions)
  }
}

function fillTemplate (obj, def, nodes, root) {
  var clonedNodes = []
  var path = {
    root: root,
    target: obj
  }
  var key, value, branch, node, change, returnValue, isArray
  var nestedDef, parentKey
  var i, j, k, l

  for (i = 0, j = nodes.length; i < j; i++)
    clonedNodes.push(cloneNode(nodes[i]))

  for (i = def[insertionOrderKey].length; i--;) {
    key = def[insertionOrderKey][i]
    path.key = key

    value = obj[key]
    if (value === null || value === void 0) continue

    isArray = Array.isArray(value)
    if (!isArray) value = [ value ]

    branch = def[key]

    for (j = value.length; j--;) {
      node = cloneNode(branch[matchedNodeKey])

      if (hasDefinitionKey in branch) {
        nestedDef = branch[1]
        if (parentBindingsKey in nestedDef)
          for (k = 0, l = nestedDef[parentBindingsKey].length; k < l; k++) {
            parentKey = nestedDef[parentBindingsKey][k]
            path.key = parentKey
            nestedDef[parentKey](node, value[j][parentKey], null, path)
          }

        node.children = fillTemplate(value[j], nestedDef, node.children, root)
        change = branch[2]
      }
      else change = branch[1]

      // Apply change function.
      if (change) {
        if (isArray) path.index = j
        else delete path.index
        returnValue = change(node, value[j], null, path)
      }
      else returnValue = value[j]

      if (!(hasDefinitionKey in branch) && returnValue !== void 0)
        if (node.name === 'input' ||
          node.name === 'textarea' ||
          node.name === 'progress')
          if (node.attribs.type === 'checkbox' ||
            node.attribs.type === 'radio') {
            if (returnValue) node.attribs.checked = true
          }
          else node.attribs.value = htmlEscape(returnValue)
        else node.children = [ { data: htmlEscape(returnValue) } ]

      insertNode(node, clonedNodes, branch[insertionPathKey])
    }
  }

  return clonedNodes
}

function insertNode (node, nodes, path) {
  var i, j, currentNodes = nodes

  for (i = 0, j = path.length - 1; i < j; i++)
    currentNodes = nodes[i].children

  currentNodes.splice(path[j], 0, node)
}

function cloneNode (node) {
  var i, j, key, clonedNode

  if ('data' in node) return node

  clonedNode = Object.create(Object.getPrototypeOf(node))
  clonedNode.type = node.type
  clonedNode.name = node.name
  clonedNode.children = []
  clonedNode.attribs = {}

  if ('children' in node)
    for (i = 0, j = node.children.length; i < j; i++)
      clonedNode.children.push(cloneNode(node.children[i]))

  for (key in node.attribs)
    clonedNode.attribs[key] = node.attribs[key]

  return clonedNode
}

function htmlEscape (html) {
  return html.replace(/[&<>"']/g, replacer)
}

function replacer (char) {
  return escapeMap[char]
}

function extendElement (attribute, isBoolean) {
  elementExtension[key] = {
    get: function () {
      return this.attribs[attribute]
    },
    set: function (value) {
      if (isBoolean && !value) delete this.attribs[attribute]
      else this.attribs[attribute] = value
      return value
    },
    configurable: true
  }
}

function noop () {}
<commit_msg>pick nested binding in render function<commit_after>'use strict'

var htmlParser = require('htmlparser2')
var select = require('css-select')
var serialize = require('dom-serializer')

// These symbols are not re-usable.
var renderFnKey = Symbol('renderFn')
var insertionPathKey = Symbol('insertionPath')
var insertionOrderKey = Symbol('insertionOrder')
var matchedNodeKey = Symbol('matchedNode')
var hasDefinitionKey = Symbol('hasDefinition')
var parentBindingsKey = Symbol('parentBindings')

var handlerOptions = { withDomLvl1: true }

// Entities are a performance killer! However, this is potentially unsafe.
var serializeOptions = { decodeEntities: false }

var escapeMap = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"',
  '\'': '''
}

var elementExtension = {
  addEventListener: { value: noop },
  removeEventListener: { value: noop },
  setAttribute: { value: function (key, value) {
    this.attribs[key] = value
  } },
  innerHTML: {
    get: function () {
      return this.children[0].data
    },
    set: function (html) {
      this.children = [ { data: html } ]
      return html
    },
    configurable: true
  },
  classList: {
    get: function () {
      var self = this

      return {
        add: function (cls) {
          if (!('class' in self.attribs)) self.attribs['class'] = cls
          else self.attribs['class'] += ' ' + cls
        },
        remove: function (cls) {
          self.attribs['class'] = self.attribs['class'].replace(cls, '')
        }
      }
    },
    configurable: true
  }
}

var attributeAliases = {
  checked: 'checked',
  className: 'class',
  placeholder: 'placeholder',
  value: 'value',
  required: 'required',
  disabled: 'disabled'
}

var booleanAttributes = [
  'checked', 'required', 'disabled'
]

var key

for (key in attributeAliases)
  extendElement(attributeAliases[key], ~booleanAttributes.indexOf(key))

module.exports = render

/**
 * Internal function for rendering strings. The benchmark to beat is plain old
 * string concatenation and for loops. Although this won't be faster, it should
 * work with more or less the same functionality as the DOM version.
 *
 * Only a tiny subset of the actual DOM is implemented here, so it may not work
 * for all front-end functionality.
 *
 * @param {Object} obj
 * @param {Object} def
 * @param {String} [html]
 * @return {String}
 */
function render (obj, def, html) {
  var i, nodes, handler, parser, element

  // If given bindings with a root node, pick only the binding keys.
  if (Array.isArray(def)) def = def[1]

  // Generating the render function is processing intensive. Skip if possible.
  if (renderFnKey in def) return def[renderFnKey](obj)

  // Callback API looks weird. This is actually synchronous, not asynchronous.
  handler = new htmlParser.DomHandler(function (error, result) {
    if (error) throw error
    nodes = result
  }, handlerOptions)

  parser = new htmlParser.Parser(handler)
  parser.write(html)
  parser.end()

  for (i = nodes.length; i--;)
    if (nodes[i].type === 'tag') {
      element = nodes[i]
      break
    }

  if (!element) throw new Error('No element found!')

  Object.defineProperties(Object.getPrototypeOf(element), elementExtension)

  processDefinition(def, nodes)
  def[renderFnKey] = makeRender(def, nodes)

  return def[renderFnKey](obj)
}

function processDefinition (def, nodes) {
  var insertionIndices = []
  var i, key, branch, selector, matches, match

  for (key in def) {
    branch = def[key]

    if (typeof branch === 'function') {
      if (!(parentBindingsKey in def)) def[parentBindingsKey] = []
      def[parentBindingsKey].push(key)
      continue
    }

    if (!Array.isArray(branch)) def[key] = branch = [ branch ]
    selector = branch[0]

    if (typeof selector !== 'string') throw new Error('Selector on "' +
      key + '" must be a string.')

    matches = select(selector, nodes)

    if (!matches.length) throw new Error('Matching node not found on "' +
      key + '".')

    match = matches[0]
    locateMatch(branch, nodes, match, [])
    insertionIndices.push(branch[insertionPathKey][0])
    branch[matchedNodeKey] = match
    removeMatches(nodes, matches)

    if (typeof branch[1] === 'object') {
      branch[hasDefinitionKey] = true
      processDefinition(branch[1], match.children)
    }
  }

  def[insertionOrderKey] = Object.keys(def)
    .filter(function (key) {
      return insertionPathKey in def[key]
    })
    .sort(function (a, b) {
      return def[a][insertionPathKey][0] - def[b][insertionPathKey][0]
    })

  // Optimization: serialize static nodes in advance.
  for (i = nodes.length; i--;)
    if (!~insertionIndices.indexOf(i))
      nodes[i] = { data: serialize(nodes[i], serializeOptions) }
}

function locateMatch (branch, nodes, match, path) {
  var i, j, node

  for (i = 0, j = nodes.length; i < j; i++) {
    node = nodes[i]

    if (match === node) {
      path.push(i)
      branch[insertionPathKey] = path
      return true
    }

    if (node.children.length) {
      path.push(i)
      if (!locateMatch(branch, node.children, match, path)) path.pop()
    }
  }

  return false
}

function removeMatches (nodes, matches) {
  var i, node

  for (i = nodes.length; i--;) {
    node = nodes[i]
    if (~matches.indexOf(node)) {
      nodes.splice(i, 1)
      continue
    }
    if (node.children.length)
      removeMatches(node.children, matches)
  }
}

function makeRender (def, nodes) {
  return function (obj) {
    return serialize(fillTemplate(obj, def, nodes, obj), serializeOptions)
  }
}

function fillTemplate (obj, def, nodes, root) {
  var clonedNodes = []
  var path = {
    root: root,
    target: obj
  }
  var key, value, branch, node, change, returnValue, isArray
  var nestedDef, parentKey
  var i, j, k, l

  for (i = 0, j = nodes.length; i < j; i++)
    clonedNodes.push(cloneNode(nodes[i]))

  for (i = def[insertionOrderKey].length; i--;) {
    key = def[insertionOrderKey][i]
    path.key = key

    value = obj[key]
    if (value === null || value === void 0) continue

    isArray = Array.isArray(value)
    if (!isArray) value = [ value ]

    branch = def[key]

    for (j = value.length; j--;) {
      node = cloneNode(branch[matchedNodeKey])

      if (hasDefinitionKey in branch) {
        nestedDef = branch[1]
        if (parentBindingsKey in nestedDef)
          for (k = 0, l = nestedDef[parentBindingsKey].length; k < l; k++) {
            parentKey = nestedDef[parentBindingsKey][k]
            path.key = parentKey
            nestedDef[parentKey](node, value[j][parentKey], null, path)
          }

        node.children = fillTemplate(value[j], nestedDef, node.children, root)
        change = branch[2]
      }
      else change = branch[1]

      // Apply change function.
      if (change) {
        if (isArray) path.index = j
        else delete path.index
        returnValue = change(node, value[j], null, path)
      }
      else returnValue = value[j]

      if (!(hasDefinitionKey in branch) && returnValue !== void 0)
        if (node.name === 'input' ||
          node.name === 'textarea' ||
          node.name === 'progress')
          if (node.attribs.type === 'checkbox' ||
            node.attribs.type === 'radio') {
            if (returnValue) node.attribs.checked = true
          }
          else node.attribs.value = htmlEscape(returnValue)
        else node.children = [ { data: htmlEscape(returnValue) } ]

      insertNode(node, clonedNodes, branch[insertionPathKey])
    }
  }

  return clonedNodes
}

function insertNode (node, nodes, path) {
  var i, j, currentNodes = nodes

  for (i = 0, j = path.length - 1; i < j; i++)
    currentNodes = nodes[i].children

  currentNodes.splice(path[j], 0, node)
}

function cloneNode (node) {
  var i, j, key, clonedNode

  if ('data' in node) return node

  clonedNode = Object.create(Object.getPrototypeOf(node))
  clonedNode.type = node.type
  clonedNode.name = node.name
  clonedNode.children = []
  clonedNode.attribs = {}

  if ('children' in node)
    for (i = 0, j = node.children.length; i < j; i++)
      clonedNode.children.push(cloneNode(node.children[i]))

  for (key in node.attribs)
    clonedNode.attribs[key] = node.attribs[key]

  return clonedNode
}

function htmlEscape (html) {
  return html.replace(/[&<>"']/g, replacer)
}

function replacer (char) {
  return escapeMap[char]
}

function extendElement (attribute, isBoolean) {
  elementExtension[key] = {
    get: function () {
      return this.attribs[attribute]
    },
    set: function (value) {
      if (isBoolean && !value) delete this.attribs[attribute]
      else this.attribs[attribute] = value
      return value
    },
    configurable: true
  }
}

function noop () {}
<|endoftext|>


<commit_before>'use strict';

/* render.js
 * Render CSS from a tree.
 */

const Rule = require('./rule'),
      Property = require('./property'),
      Value = require('./value');

let render = module.exports = function(tree, opts){
  opts = (typeof opts === 'object') ? opts : {};

  if (tree instanceof Rule) return render.rule(tree, opts);
  if (tree instanceof Property) return render.property(tree, opts);
  if (tree instanceof Value) return render.value(tree, opts);

  let final = '';
  tree.forEach((rule, i) => {
    if (!opts.weak && !(rule instanceof Rule))
      throw new Error('a tree may only contain rules at the base');

    final += render(rule, opts) + (i !== rule.length-1 ? ' ' : '');
  });

  return final;
};

render.value = function(value, opts){
  // Numeric value
  if (value.has('numeric')) return value.val + (value.name || '');

  // Keyword value
  else if (value.has('keyword')) return value.val;

  // Function
  else if (value.has('function')) {
    let args = '';
    value.val.forEach((arg, i) => {
      if (typeof arg === 'number' || typeof arg === 'string') arg = new Value(arg);
      if (!opts.weak && !(arg instanceof Value) && !arg.has('function'))
        throw new Error('functions may only contain non-function values');

      args += render(arg, opts) + (i !== value.val.length-1 ? ', ' : '');
    });

    return value.name + '(' + args + ')';
  }

  // Unknown
  else {
    if (!opts.weak)
      throw new Error('encountered unknown value when rendering.');
    else
      return '';
  }
};

render.property = function(prop, opts){
  let vals = '';
  prop.items.forEach((val, i) => {
    if (!opts.weak && !(val instanceof Value))
      throw new Error('properties may only contain Value objects');

    vals += render(val, opts) + (i !== prop.items.length-1 ? ' ' : '');
  });

  return prop.name + ':' + vals;
};

render.rule = function(rule, opts){
  // Regular @-rule
  if (rule.has('at-rule') && rule.has('non-conditional')) {
    let vals = '';
    rule.items.forEach((val, i) => {
      if (!opts.weak && !(val instanceof Value))
        throw new Error('non-conditional @-rules may only contain Values');

      vals += render(val, opts) + (i !== rule.items.length-1 ? ' ' : '');
    });

    return rule.name + ' ' + vals;
  }

  // Conditional @-rule
  else if (rule.has('at-rule') && rule.has('conditional')) {
    let sels = '';
    rule.items.forEach((sel, i) => {
      if (!opts.weak && !(sel instanceof Rule) && !sel.has('selector'))
        throw new Error('conditional @-rules may only contain selector Properties');

      sels += render(sel, opts) + (i !== rule.items.length-1 ? ' ' : '');
    });

    let cond = '';
    if (rule.has('conditional')) {
      rule.condition.forEach((prop, i) => {
        if (typeof prop === 'string') prop = new Value(prop);
        if (!opts.weak && !(prop instanceof Property || prop instanceof Value))
          throw new Error('a conditional at-rule may only contain properties and values');

        cond += prop instanceof Property ? '(' + render(prop, opts) + ')' : render(prop, opts);
        cond += (i !== rule.condition.length-1 ? ' ' : '');
      });
    }

    return rule.name + ' ' + cond + ' {' + sels + '}';
  }

  // Selector
  else if (rule.has('selector')) {
    let props = '';
    rule.items.forEach((prop, i) => {
      if (!opts.weak && !(prop instanceof Property))
        throw new Error('selector Rules may only contain Properties');

      props += render(prop, opts) + (i !== rule.items.length-1 ? '; ' : '');
    });

    return rule.name + ' {' + props + '}';
  }

  // Unknown
  else {
    if (!opts.weak)
      throw new Error('encountered unknown value when rendering.');
    else
      return '';
  }
};
<commit_msg>Add seni-colon to rendering<commit_after>'use strict';

/* render.js
 * Render CSS from a tree.
 */

const Rule = require('./rule'),
      Property = require('./property'),
      Value = require('./value');

let render = module.exports = function(tree, opts){
  opts = (typeof opts === 'object') ? opts : {};

  if (tree instanceof Rule) return render.rule(tree, opts);
  if (tree instanceof Property) return render.property(tree, opts);
  if (tree instanceof Value) return render.value(tree, opts);

  let final = '';
  tree.forEach((rule, i) => {
    if (!opts.weak && !(rule instanceof Rule))
      throw new Error('a tree may only contain rules at the base');

    final += render(rule, opts) + (i !== rule.length-1 ? '; ' : '');
  });

  return final;
};

render.value = function(value, opts){
  // Numeric value
  if (value.has('numeric')) return value.val + (value.name || '');

  // Keyword value
  else if (value.has('keyword')) return value.val;

  // Function
  else if (value.has('function')) {
    let args = '';
    value.val.forEach((arg, i) => {
      if (typeof arg === 'number' || typeof arg === 'string') arg = new Value(arg);
      if (!opts.weak && !(arg instanceof Value) && !arg.has('function'))
        throw new Error('functions may only contain non-function values');

      args += render(arg, opts) + (i !== value.val.length-1 ? ', ' : '');
    });

    return value.name + '(' + args + ')';
  }

  // Unknown
  else {
    if (!opts.weak)
      throw new Error('encountered unknown value when rendering.');
    else
      return '';
  }
};

render.property = function(prop, opts){
  let vals = '';
  prop.items.forEach((val, i) => {
    if (!opts.weak && !(val instanceof Value))
      throw new Error('properties may only contain Value objects');

    vals += render(val, opts) + (i !== prop.items.length-1 ? ' ' : '');
  });

  return prop.name + ':' + vals;
};

render.rule = function(rule, opts){
  // Regular @-rule
  if (rule.has('at-rule') && rule.has('non-conditional')) {
    let vals = '';
    rule.items.forEach((val, i) => {
      if (!opts.weak && !(val instanceof Value))
        throw new Error('non-conditional @-rules may only contain Values');

      vals += render(val, opts) + (i !== rule.items.length-1 ? ' ' : '');
    });

    return rule.name + ' ' + vals;
  }

  // Conditional @-rule
  else if (rule.has('at-rule') && rule.has('conditional')) {
    let sels = '';
    rule.items.forEach((sel, i) => {
      if (!opts.weak && !(sel instanceof Rule) && !sel.has('selector'))
        throw new Error('conditional @-rules may only contain selector Properties');

      sels += render(sel, opts) + (i !== rule.items.length-1 ? ' ' : '');
    });

    let cond = '';
    if (rule.has('conditional')) {
      rule.condition.forEach((prop, i) => {
        if (typeof prop === 'string') prop = new Value(prop);
        if (!opts.weak && !(prop instanceof Property || prop instanceof Value))
          throw new Error('a conditional at-rule may only contain properties and values');

        cond += prop instanceof Property ? '(' + render(prop, opts) + ')' : render(prop, opts);
        cond += (i !== rule.condition.length-1 ? ' ' : '');
      });
    }

    return rule.name + ' ' + cond + ' {' + sels + '}';
  }

  // Selector
  else if (rule.has('selector')) {
    let props = '';
    rule.items.forEach((prop, i) => {
      if (!opts.weak && !(prop instanceof Property))
        throw new Error('selector Rules may only contain Properties');

      props += render(prop, opts) + (i !== rule.items.length-1 ? '; ' : '');
    });

    return rule.name + ' {' + props + '}';
  }

  // Unknown
  else {
    if (!opts.weak)
      throw new Error('encountered unknown value when rendering.');
    else
      return '';
  }
};
<|endoftext|>


<commit_before>Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() { return Meteor.subscribe('posts'); }
});
Router.route('/', {name: 'content'});
Router.route('/posts/:_id', {
name: 'postPage',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/posts/:_id/edit', {
name: 'postEdit',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/submit', {name: 'postSubmit'});
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
} else {
this.next();
}
}
Router.onBeforeAction('dataNotFound', {only: 'postPage'});
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});<commit_msg>roll back to post list<commit_after>Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() { return Meteor.subscribe('posts'); }
});
Router.route('/', {name: 'postsList'});
Router.route('/posts/:_id', {
name: 'postPage',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/posts/:_id/edit', {
name: 'postEdit',
data: function() { return Posts.findOne(this.params._id); }
});
Router.route('/submit', {name: 'postSubmit'});
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
} else {
this.next();
}
}
Router.onBeforeAction('dataNotFound', {only: 'postPage'});
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});<|endoftext|>