<commit_before>/**@
* #Text
* @category Graphics
* @trigger Change - when the text is changed
* @requires Canvas or DOM
* Component to make a text entity.
*
* Note: An entity with the text component is just text! If you want to write text
* inside an image, you need one entity for the text and another entity for the image.
* More tips for writing text inside an image: (1) Use the z-index (from 2D component)
* to ensure that the text is on top of the image, not the other way around; (2)
* use .attach() (from 2D component) to glue the text to the image so they move and
* rotate together.
*/
Crafty.c("Text", {
	_text: "",
	_textFont: {
		"type": "",
		"weight": "",
		"size": "",
		"family": ""
	},
	ready: true,

	init: function () {
		this.requires("2D");

		this.bind("Draw", function (e) {
			var font = this._textFont["type"] + ' ' + this._textFont["weight"] + ' ' +
				this._textFont["size"] + ' ' + this._textFont["family"];

			if (e.type === "DOM") {
				var el = this._element,
					style = el.style;

				style.color = this._textColor;
				style.font = font;
				el.innerHTML = this._text;
			} else if (e.type === "canvas") {
				var context = e.ctx,
                    metrics = null;

				context.save();

				context.fillStyle = this._textColor || "rgb(0,0,0)";
				context.font = font;

				context.translate(this.x, this.y + this.h);
				context.fillText(this._text, 0, 0);

				metrics = context.measureText(this._text);
				this._w = metrics.width;

				context.restore();
			}
		});
	},

	/**@
    * #.text
    * @comp Text
    * @sign public this .text(String text)
    * @sign public this .text(Function textgenerator)
    * @param text - String of text that will be inserted into the DOM or Canvas element.
    *
    * This method will update the text inside the entity.
    * If you use DOM, to modify the font, use the `.css` method inherited from the DOM component.
    *
    * If you need to reference attributes on the entity itself you can pass a function instead of a string.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
    *
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 })
    *     .text(function () { return "My position is " + this._x });
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 })
    *     .text(function () { return "My position is " + this._x });
    * ~~~
    */
	text: function (text) {
		if (!(typeof text !== "undefined" && text !== null)) return this._text;
		if (typeof(text) == "function")
			this._text = text.call(this);
		else
			this._text = text;
		this.trigger("Change");
		return this;
	},

	/**@
    * #.textColor
    * @comp Text
    * @sign public this .textColor(String color, Number strength)
    * @param color - The color in hexadecimal
    * @param strength - Level of opacity
    *
    * Modify the text color and level of opacity.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!")
    *   .textColor('#FF0000');
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text('Look at me!!')
    *   .textColor('#FF0000', 0.6);
    * ~~~
    * @see Crafty.toRGB
    */
	textColor: function (color, strength) {
		this._strength = strength;
		this._textColor = Crafty.toRGB(color, this._strength);
		this.trigger("Change");
		return this;
	},

	/**@
    * #.textFont
    * @comp Text
    * @triggers Change
    * @sign public this .textFont(String key, * value)
    * @param key - Property of the entity to modify
    * @param value - Value to set the property to
    *
    * @sign public this .textFont(Object map)
    * @param map - Object where the key is the property to modify and the value as the property value
    *
    * Use this method to set font property of the text entity.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").textFont({ type: 'italic', family: 'Arial' });
    * Crafty.e("2D, Canvas, Text").textFont({ size: '20px', weight: 'bold' });
    *
    * Crafty.e("2D, Canvas, Text").textFont("type", "italic");
    * Crafty.e("2D, Canvas, Text").textFont("type"); // italic
    * ~~~
    */
	textFont: function (key, value) {
		if (arguments.length === 1) {
			//if just the key, return the value
			if (typeof key === "string") {
				return this._textFont[key];
			}

			if (typeof key === "object") {
				for (propertyKey in key) {
					this._textFont[propertyKey] = key[propertyKey];
				}
			}
		} else {
			this._textFont[key] = value;
		}

		this.trigger("Change");
		return this;
	}
});
<commit_msg>Provide default family and size for text<commit_after>/**@
* #Text
* @category Graphics
* @trigger Change - when the text is changed
* @requires Canvas or DOM
* Component to make a text entity.
*
* Note: An entity with the text component is just text! If you want to write text
* inside an image, you need one entity for the text and another entity for the image.
* More tips for writing text inside an image: (1) Use the z-index (from 2D component)
* to ensure that the text is on top of the image, not the other way around; (2)
* use .attach() (from 2D component) to glue the text to the image so they move and
* rotate together.
*/
Crafty.c("Text", {
	_text: "",
	_textFont: {
		"type": "",
		"weight": "",
		"size": "",
		"family": ""
	},
	defaultSize: "10px",
	defaultFamily: "sans-serif",
	ready: true,

	init: function () {
		this.requires("2D");

		this.bind("Draw", function (e) {
			var font = this._textFont["type"] + ' ' + this._textFont["weight"] + ' '
			 	+ (this._textFont["size"] || this.defaultSize) + ' '
				+ (this._textFont["family"] || this.defaultFamily);

			if (e.type === "DOM") {
				var el = this._element,
					style = el.style;

				style.color = this._textColor;
				style.font = font;
				el.innerHTML = this._text;
			} else if (e.type === "canvas") {
				var context = e.ctx,
                    metrics = null;

				context.save();

				context.fillStyle = this._textColor || "rgb(0,0,0)";
				context.font = font;

				context.translate(this.x, this.y + this.h);
				context.fillText(this._text, 0, 0);

				metrics = context.measureText(this._text);
				this._w = metrics.width;

				context.restore();
			}
		});
	},

	/**@
    * #.text
    * @comp Text
    * @sign public this .text(String text)
    * @sign public this .text(Function textgenerator)
    * @param text - String of text that will be inserted into the DOM or Canvas element.
    *
    * This method will update the text inside the entity.
    * If you use DOM, to modify the font, use the `.css` method inherited from the DOM component.
    *
    * If you need to reference attributes on the entity itself you can pass a function instead of a string.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
    *
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 })
    *     .text(function () { return "My position is " + this._x });
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text("Look at me!!");
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 })
    *     .text(function () { return "My position is " + this._x });
    * ~~~
    */
	text: function (text) {
		if (!(typeof text !== "undefined" && text !== null)) return this._text;
		if (typeof(text) == "function")
			this._text = text.call(this);
		else
			this._text = text;
		this.trigger("Change");
		return this;
	},

	/**@
    * #.textColor
    * @comp Text
    * @sign public this .textColor(String color, Number strength)
    * @param color - The color in hexadecimal
    * @param strength - Level of opacity
    *
    * Modify the text color and level of opacity.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").attr({ x: 100, y: 100 }).text("Look at me!!")
    *   .textColor('#FF0000');
    *
    * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text('Look at me!!')
    *   .textColor('#FF0000', 0.6);
    * ~~~
    * @see Crafty.toRGB
    */
	textColor: function (color, strength) {
		this._strength = strength;
		this._textColor = Crafty.toRGB(color, this._strength);
		this.trigger("Change");
		return this;
	},

	/**@
    * #.textFont
    * @comp Text
    * @triggers Change
    * @sign public this .textFont(String key, * value)
    * @param key - Property of the entity to modify
    * @param value - Value to set the property to
    *
    * @sign public this .textFont(Object map)
    * @param map - Object where the key is the property to modify and the value as the property value
    *
    * Use this method to set font property of the text entity.
    *
    * @example
    * ~~~
    * Crafty.e("2D, DOM, Text").textFont({ type: 'italic', family: 'Arial' });
    * Crafty.e("2D, Canvas, Text").textFont({ size: '20px', weight: 'bold' });
    *
    * Crafty.e("2D, Canvas, Text").textFont("type", "italic");
    * Crafty.e("2D, Canvas, Text").textFont("type"); // italic
    * ~~~
    */
	textFont: function (key, value) {
		if (arguments.length === 1) {
			//if just the key, return the value
			if (typeof key === "string") {
				return this._textFont[key];
			}

			if (typeof key === "object") {
				for (propertyKey in key) {
					this._textFont[propertyKey] = key[propertyKey];
				}
			}
		} else {
			this._textFont[key] = value;
		}

		this.trigger("Change");
		return this;
	}
});
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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.

(function(shared, scope, testing) {
  var originalRequestAnimationFrame = window.requestAnimationFrame;
  var rafCallbacks = [];
  var rafId = 0;
  window.requestAnimationFrame = function(f) {
    var id = rafId++;
    if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) {
      originalRequestAnimationFrame(processRafCallbacks);
    }
    rafCallbacks.push([id, f]);
    return id;
  };

  window.cancelAnimationFrame = function(id) {
    rafCallbacks.forEach(function(entry) {
      if (entry[0] == id) {
        entry[1] = function() {};
      }
    });
  };

  function processRafCallbacks(t) {
    var processing = rafCallbacks;
    rafCallbacks = [];
    tick(t);
    processing.forEach(function(entry) { entry[1](t); });
    if (needsRetick)
      tick(t);
    applyPendingEffects();
  }

  function comparePlayers(leftPlayer, rightPlayer) {
    return leftPlayer._sequenceNumber - rightPlayer._sequenceNumber;
  }

  function InternalTimeline() {
    this._animations = [];
    // Android 4.3 browser has window.performance, but not window.performance.now
    this.currentTime = window.performance && performance.now ? performance.now() : 0;
  };

  InternalTimeline.prototype = {
    _play: function(source) {
      source._timing = shared.normalizeTimingInput(source.timing);
      var player = new scope.Player(source);
      player._idle = false;
      player._timeline = this;
      this._animations.push(player);
      scope.restart();
      scope.invalidateEffects();
      return player;
    }
  };

  var ticking = false;
  var hasRestartedThisFrame = false;

  scope.restart = function() {
    if (!ticking) {
      ticking = true;
      requestAnimationFrame(function() {});
      hasRestartedThisFrame = true;
    }
    return hasRestartedThisFrame;
  };

  var needsRetick = false;
  scope.invalidateEffects = function() {
    needsRetick = true;
  };

  var pendingEffects = [];
  function applyPendingEffects() {
    pendingEffects.forEach(function(f) { f(); });
    pendingEffects.length = 0;
  }

  var originalGetComputedStyle = window.getComputedStyle;
  Object.defineProperty(window, 'getComputedStyle', {
    configurable: true,
    enumerable: true,
    value: function() {
      if (needsRetick) tick(timeline.currentTime);
      applyPendingEffects();
      return originalGetComputedStyle.apply(this, arguments);
    },
  });

  function tick(t) {
    hasRestartedThisFrame = false;
    var timeline = scope.timeline;
    timeline.currentTime = t;
    timeline._animations.sort(comparePlayers);
    ticking = false;
    var updatingPlayers = timeline._animations;
    timeline._animations = [];

    var newPendingClears = [];
    var newPendingEffects = [];
    updatingPlayers = updatingPlayers.filter(function(player) {
      player._inTimeline = player._tick(t);

      if (!player._inEffect)
        newPendingClears.push(player._source);
      else
        newPendingEffects.push(player._source);

      if (!player.finished && !player.paused && !player._idle)
        ticking = true;

      return player._inTimeline;
    });

    // FIXME: Should remove dupliactes from pendingEffects.
    pendingEffects.push.apply(pendingEffects, newPendingClears);
    pendingEffects.push.apply(pendingEffects, newPendingEffects);

    timeline._animations.push.apply(timeline._animations, updatingPlayers);
    needsRetick = false;

    if (ticking)
      requestAnimationFrame(function() {});
  };

  if (WEB_ANIMATIONS_TESTING) {
    testing.tick = processRafCallbacks;
    testing.isTicking = function() { return ticking; };
    testing.setTicking = function(newVal) { ticking = newVal; };
  }

  var timeline = new InternalTimeline();
  scope.timeline = timeline;

})(webAnimationsShared, webAnimations1, webAnimationsTesting);
<commit_msg>Rename for tick.js<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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.

(function(shared, scope, testing) {
  var originalRequestAnimationFrame = window.requestAnimationFrame;
  var rafCallbacks = [];
  var rafId = 0;
  window.requestAnimationFrame = function(f) {
    var id = rafId++;
    if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) {
      originalRequestAnimationFrame(processRafCallbacks);
    }
    rafCallbacks.push([id, f]);
    return id;
  };

  window.cancelAnimationFrame = function(id) {
    rafCallbacks.forEach(function(entry) {
      if (entry[0] == id) {
        entry[1] = function() {};
      }
    });
  };

  function processRafCallbacks(t) {
    var processing = rafCallbacks;
    rafCallbacks = [];
    tick(t);
    processing.forEach(function(entry) { entry[1](t); });
    if (needsRetick)
      tick(t);
    applyPendingEffects();
  }

  function compareAnimations(leftAnimation, rightAnimation) {
    return leftAnimation._sequenceNumber - rightAnimation._sequenceNumber;
  }

  function InternalTimeline() {
    this._animations = [];
    // Android 4.3 browser has window.performance, but not window.performance.now
    this.currentTime = window.performance && performance.now ? performance.now() : 0;
  };

  InternalTimeline.prototype = {
    _play: function(source) {
      source._timing = shared.normalizeTimingInput(source.timing);
      var animation = new scope.Animation(source);
      animation._idle = false;
      animation._timeline = this;
      this._animations.push(animation);
      scope.restart();
      scope.invalidateEffects();
      return animation;
    }
  };

  var ticking = false;
  var hasRestartedThisFrame = false;

  scope.restart = function() {
    if (!ticking) {
      ticking = true;
      requestAnimationFrame(function() {});
      hasRestartedThisFrame = true;
    }
    return hasRestartedThisFrame;
  };

  var needsRetick = false;
  scope.invalidateEffects = function() {
    needsRetick = true;
  };

  var pendingEffects = [];
  function applyPendingEffects() {
    pendingEffects.forEach(function(f) { f(); });
    pendingEffects.length = 0;
  }

  var originalGetComputedStyle = window.getComputedStyle;
  Object.defineProperty(window, 'getComputedStyle', {
    configurable: true,
    enumerable: true,
    value: function() {
      if (needsRetick) tick(timeline.currentTime);
      applyPendingEffects();
      return originalGetComputedStyle.apply(this, arguments);
    },
  });

  function tick(t) {
    hasRestartedThisFrame = false;
    var timeline = scope.timeline;
    timeline.currentTime = t;
    timeline._animations.sort(compareAnimations);
    ticking = false;
    var updatingAnimations = timeline._animations;
    timeline._animations = [];

    var newPendingClears = [];
    var newPendingEffects = [];
    updatingAnimations = updatingAnimations.filter(function(animation) {
      animation._inTimeline = animation._tick(t);

      if (!animation._inEffect)
        newPendingClears.push(animation._source);
      else
        newPendingEffects.push(animation._source);

      if (!animation.finished && !animation.paused && !animation._idle)
        ticking = true;

      return animation._inTimeline;
    });

    // FIXME: Should remove dupliactes from pendingEffects.
    pendingEffects.push.apply(pendingEffects, newPendingClears);
    pendingEffects.push.apply(pendingEffects, newPendingEffects);

    timeline._animations.push.apply(timeline._animations, updatingAnimations);
    needsRetick = false;

    if (ticking)
      requestAnimationFrame(function() {});
  };

  if (WEB_ANIMATIONS_TESTING) {
    testing.tick = processRafCallbacks;
    testing.isTicking = function() { return ticking; };
    testing.setTicking = function(newVal) { ticking = newVal; };
  }

  var timeline = new InternalTimeline();
  scope.timeline = timeline;

})(webAnimationsShared, webAnimations1, webAnimationsTesting);
<|endoftext|>


<commit_before>// tile.js
//
// The thing a player and mobs walk on top of. Tend not to move.
// Holy flyweight pattern, Batman! Each individual tile is not
// instantiated per tile in the map; each tile type is created once
// and stored in the map as a reference to the same object each time.

(function(global, $) {

  var Tile = function() {
    this.draw = function(x, y, ctx) {
      // this version doesn't do anything
    };
  };

  // Tiles are square with this length on a side.
  Tile.tilesize = 32;

  // Given an x,y in worldspace returns an {x,y} position in tilespace.
  Tile.totilepos = function(x, y) {
    return {
      x: Math.floor(x/Tile.tilesize),
      y: Math.floor(y/Tile.tilesize)
    };
  };

  // Default tile (meant to be used by ref by everybody) that fills the
  // map at first.
  Tile.Air = new Tile();

  // The other really common tile.
  Tile.Dirt = new Tile();
  Tile.Dirt.draw = function(x, y, ctx) {
    var oldFillStyle = ctx.fillStyle;
    ctx.fillStyle = "rgb(102,51,0)";
    ctx.fillRect(x, y, Tile.tilesize, Tile.tilesize + 1);
    ctx.fillStyle = oldFillStyle;
  }

  // The common tile, but on the surface with grass
  Tile.DirtWithGrass = new Tile();
  Tile.DirtWithGrass.draw = function(x, y, ctx) {
    Tile.Dirt.draw(x, y, ctx);
    var oldFillStyle = ctx.fillStyle;
    ctx.fillStyle = "rgb(0, 232, 16)";
    ctx.fillRect(x, y, Tile.tilesize, Tile.tilesize * 0.2);
    ctx.fillStyle = oldFillStyle;
  }

  global.Tile = Tile;

})(window, jQuery)
<commit_msg>making single quotes because I am a crazy person<commit_after>// tile.js
//
// The thing a player and mobs walk on top of. Tend not to move.
// Holy flyweight pattern, Batman! Each individual tile is not
// instantiated per tile in the map; each tile type is created once
// and stored in the map as a reference to the same object each time.

(function(global, $) {

  var Tile = function() {
    this.draw = function(x, y, ctx) {
      // this version doesn't do anything
    };
  };

  // Tiles are square with this length on a side.
  Tile.tilesize = 32;

  // Given an x,y in worldspace returns an {x,y} position in tilespace.
  Tile.totilepos = function(x, y) {
    return {
      x: Math.floor(x/Tile.tilesize),
      y: Math.floor(y/Tile.tilesize)
    };
  };

  // Default tile (meant to be used by ref by everybody) that fills the
  // map at first.
  Tile.Air = new Tile();

  // The other really common tile.
  Tile.Dirt = new Tile();
  Tile.Dirt.draw = function(x, y, ctx) {
    var oldFillStyle = ctx.fillStyle;
    ctx.fillStyle = 'rgb(102,51,0)';
    ctx.fillRect(x, y, Tile.tilesize, Tile.tilesize + 1);
    ctx.fillStyle = oldFillStyle;
  }

  // The common tile, but on the surface with grass
  Tile.DirtWithGrass = new Tile();
  Tile.DirtWithGrass.draw = function(x, y, ctx) {
    Tile.Dirt.draw(x, y, ctx);
    var oldFillStyle = ctx.fillStyle;
    ctx.fillStyle = "rgb(0, 232, 16)";
    ctx.fillRect(x, y, Tile.tilesize, Tile.tilesize * 0.2);
    ctx.fillStyle = oldFillStyle;
  }

  global.Tile = Tile;

})(window, jQuery)
<|endoftext|>


<commit_before>import _ from 'lodash/fp'
import { findIndexed } from './conversion'
import { push, dotEncoder, slashEncoder } from './array'

export let isTraversable = x => _.isArray(x) || _.isPlainObject(x)
export let traverse = x => isTraversable(x) && !_.isEmpty(x) && x

export let walk = (next = traverse) => (
  pre,
  post = _.noop,
  parents = [],
  parentIndexes = []
) => (tree, index) =>
  pre(tree, index, parents, parentIndexes) ||
  findIndexed(
    walk(next)(pre, post, [tree, ...parents], [index, ...parentIndexes]),
    next(tree, index, parents, parentIndexes) || []
  ) ||
  post(tree, index, parents, parentIndexes)

export let findIndexedAsync = async (f, data) => {
  for (let key in data) {
    if (await f(data[key], key, data)) return data[key]
  }
}

export let walkAsync = (next = traverse) => (
  pre,
  post = _.noop,
  parents = [],
  parentIndexes = []
) => async (tree, index) =>
  (await pre(tree, index, parents, parentIndexes)) ||
  (await findIndexedAsync(
    walkAsync(next)(pre, post, [tree, ...parents], [index, ...parentIndexes]),
    next(tree, index, parents, parentIndexes) || []
  )) ||
  post(tree, index, parents, parentIndexes) //await not needed because its the final return value

export let transformTree = (next = traverse) =>
  _.curry((f, x) => {
    let result = _.cloneDeep(x)
    walk(next)(f)(result)
    return result
  })

export let reduceTree = (next = traverse) =>
  _.curry((f, result, tree) => {
    walk(next)((...x) => {
      result = f(result, ...x)
    })(tree)
    return result
  })

export let treeToArrayBy = (next = traverse) =>
  _.curry((fn, tree) => reduceTree(next)((r, x) => push(fn(x), r), [], tree))
export let treeToArray = (next = traverse) => treeToArrayBy(next)(x => x)

export let leaves = (next = traverse) =>
  _.flow(treeToArray(next), _.reject(next))

export let treeLookup = (next = traverse, buildIteratee = _.identity) =>
  _.curry((path, tree) =>
    _.reduce(
      (tree, path) => _.find(buildIteratee(path), next(tree)),
      tree,
      path
    )
  )

export let keyTreeByWith = (next = traverse) =>
  _.curry((transformer, groupIteratee, x) =>
    _.flow(
      treeToArrayBy(next)(_.iteratee(groupIteratee)),
      _.uniq,
      _.keyBy(_.identity),
      _.mapValues(group =>
        transformTree(next)(node => {
          let matches = _.iteratee(groupIteratee)(node) === group
          transformer(node, matches, group)
        }, x)
      )
    )(x)
  )

// Flat Tree
export let treeKeys = (x, i, xs, is) => [i, ...is]
export let treeValues = (x, i, xs) => [x, ...xs]
export let treePath = (build = treeKeys, encoder = dotEncoder) => (...args) =>
  (encoder.encode || encoder)(build(...args).reverse())
export let propTreePath = prop =>
  treePath(_.flow(treeValues, _.map(prop)), slashEncoder)

export let flattenTree = (next = traverse) => (buildPath = treePath()) =>
  reduceTree(next)(
    (result, node, ...x) => _.set([buildPath(node, ...x)], node, result),
    {}
  )

export let flatLeaves = (next = traverse) => _.reject(next)

export let tree = (next = traverse, buildIteratee = _.identity) => ({
  walk: walk(next),
  walkAsync: walkAsync(next),
  transform: transformTree(next),
  reduce: reduceTree(next),
  toArrayBy: treeToArrayBy(next),
  toArray: treeToArray(next),
  leaves: leaves(next),
  lookup: treeLookup(next, buildIteratee),
  keyByWith: keyTreeByWith(next),
  traverse: next,
  flatten: flattenTree(next),
  flatLeaves: flatLeaves(next),
})
<commit_msg>backport walkAsync to promises<commit_after>import _ from 'lodash/fp'
import { findIndexed } from './conversion'
import { push, dotEncoder, slashEncoder } from './array'

export let isTraversable = x => _.isArray(x) || _.isPlainObject(x)
export let traverse = x => isTraversable(x) && !_.isEmpty(x) && x

export let walk = (next = traverse) => (
  pre,
  post = _.noop,
  parents = [],
  parentIndexes = []
) => (tree, index) =>
  pre(tree, index, parents, parentIndexes) ||
  findIndexed(
    walk(next)(pre, post, [tree, ...parents], [index, ...parentIndexes]),
    next(tree, index, parents, parentIndexes) || []
  ) ||
  post(tree, index, parents, parentIndexes)

export let findIndexedAsync = async (f, data) => {
  for (let key in data) {
    if (await f(data[key], key, data)) return data[key]
  }
}

export let walkAsync = (next = traverse) => (
  pre,
  post = _.noop,
  parents = [],
  parentIndexes = []
) => async (tree, index) =>
  pre(tree, index, parents, parentIndexes)
    .then(
      preResult =>
        preResult ||
        findIndexedAsync(
          walkAsync(next)(
            pre,
            post,
            [tree, ...parents],
            [index, ...parentIndexes]
          ),
          next(tree, index, parents, parentIndexes) || []
        )
    )
    .then(stepResult => stepResult || post(tree, index, parents, parentIndexes))

export let transformTree = (next = traverse) =>
  _.curry((f, x) => {
    let result = _.cloneDeep(x)
    walk(next)(f)(result)
    return result
  })

export let reduceTree = (next = traverse) =>
  _.curry((f, result, tree) => {
    walk(next)((...x) => {
      result = f(result, ...x)
    })(tree)
    return result
  })

export let treeToArrayBy = (next = traverse) =>
  _.curry((fn, tree) => reduceTree(next)((r, x) => push(fn(x), r), [], tree))
export let treeToArray = (next = traverse) => treeToArrayBy(next)(x => x)

export let leaves = (next = traverse) =>
  _.flow(treeToArray(next), _.reject(next))

export let treeLookup = (next = traverse, buildIteratee = _.identity) =>
  _.curry((path, tree) =>
    _.reduce(
      (tree, path) => _.find(buildIteratee(path), next(tree)),
      tree,
      path
    )
  )

export let keyTreeByWith = (next = traverse) =>
  _.curry((transformer, groupIteratee, x) =>
    _.flow(
      treeToArrayBy(next)(_.iteratee(groupIteratee)),
      _.uniq,
      _.keyBy(_.identity),
      _.mapValues(group =>
        transformTree(next)(node => {
          let matches = _.iteratee(groupIteratee)(node) === group
          transformer(node, matches, group)
        }, x)
      )
    )(x)
  )

// Flat Tree
export let treeKeys = (x, i, xs, is) => [i, ...is]
export let treeValues = (x, i, xs) => [x, ...xs]
export let treePath = (build = treeKeys, encoder = dotEncoder) => (...args) =>
  (encoder.encode || encoder)(build(...args).reverse())
export let propTreePath = prop =>
  treePath(_.flow(treeValues, _.map(prop)), slashEncoder)

export let flattenTree = (next = traverse) => (buildPath = treePath()) =>
  reduceTree(next)(
    (result, node, ...x) => _.set([buildPath(node, ...x)], node, result),
    {}
  )

export let flatLeaves = (next = traverse) => _.reject(next)

export let tree = (next = traverse, buildIteratee = _.identity) => ({
  walk: walk(next),
  walkAsync: walkAsync(next),
  transform: transformTree(next),
  reduce: reduceTree(next),
  toArrayBy: treeToArrayBy(next),
  toArray: treeToArray(next),
  leaves: leaves(next),
  lookup: treeLookup(next, buildIteratee),
  keyByWith: keyTreeByWith(next),
  traverse: next,
  flatten: flattenTree(next),
  flatLeaves: flatLeaves(next),
})
<|endoftext|>


<commit_before>c3_chart_internal_fn.setTargetType = function C3_INTERNAL_setTargetType(targetIds, type) {
    var $$ = this, config = $$.config;
    $$.mapToTargetIds(targetIds).forEach(function (id) {
        $$.withoutFadeIn[id] = (type === config.data_types[id]);
        config.data_types[id] = type;
    });
    if (!targetIds) {
        config.data_type = type;
    }
};
c3_chart_internal_fn.hasType = function C3_INTERNAL_hasType(type, targets) {
    var $$ = this, types = $$.config.data_types, has = false;
    targets = targets || $$.data.targets;
    if (targets && targets.length) {
        targets.forEach(function (target) {
            var t = types[target.id];
            if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) {
                has = true;
            }
        });
    } else if (Object.keys(types).length) {
        Object.keys(types).forEach(function (id) {
            if (types[id] === type) {
                has = true;
            }
        });
    } else {
        has = $$.config.data_type === type;
    }
    return has;
};
c3_chart_internal_fn.hasArcType = function C3_INTERNAL_hasArcType(targets) {
    return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
};
c3_chart_internal_fn.isLineType = function C3_INTERNAL_isLineType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return !dataType ||
        ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isStepType = function C3_INTERNAL_isStepType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['step', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isSplineType = function C3_INTERNAL_isSplineType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['spline', 'area-spline'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isAreaType = function C3_INTERNAL_isAreaType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['area', 'area-spline', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isBarType = function C3_INTERNAL_isBarType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'bar';
};
c3_chart_internal_fn.isScatterType = function C3_INTERNAL_isScatterType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'scatter';
};
c3_chart_internal_fn.isPieType = function C3_INTERNAL_isPieType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'pie';
};
c3_chart_internal_fn.isGaugeType = function C3_INTERNAL_isGaugeType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'gauge';
};
c3_chart_internal_fn.isDonutType = function C3_INTERNAL_isDonutType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'donut';
};
c3_chart_internal_fn.isArcType = function C3_INTERNAL_isArcType(d) {
    return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
};
c3_chart_internal_fn.lineData = function C3_INTERNAL_lineData(d, isSub) {
    return this.isLineType(d, isSub) ? [d] : [];
};
c3_chart_internal_fn.arcData = function C3_INTERNAL_arcData(d) {
    return this.isArcType(d.data) ? [d] : [];
};
/* not used
 function scatterData(d) {
 return isScatterType(d) ? d.values : [];
 }
 */
c3_chart_internal_fn.barData = function C3_INTERNAL_barData(d) {
    return this.isBarType(d) ? d.values : [];
};
c3_chart_internal_fn.lineOrScatterData = function C3_INTERNAL_lineOrScatterData(d, isSub) {
    return this.isLineType(d, isSub) || this.isScatterType(d) ? d.values : [];
};
c3_chart_internal_fn.barOrLineData = function C3_INTERNAL_barOrLineData(d, isSub) {
    return this.isBarType(d) || this.isLineType(d, isSub) ? d.values : [];
};
c3_chart_internal_fn.isInterpolationType = function (type) {
    return ['linear', 'linear-closed', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone'].indexOf(type) >= 0;
};
<commit_msg>unify the isSub parameter usage for the various graph types<commit_after>c3_chart_internal_fn.setTargetType = function C3_INTERNAL_setTargetType(targetIds, type) {
    var $$ = this, config = $$.config;
    $$.mapToTargetIds(targetIds).forEach(function (id) {
        $$.withoutFadeIn[id] = (type === config.data_types[id]);
        config.data_types[id] = type;
    });
    if (!targetIds) {
        config.data_type = type;
    }
};
c3_chart_internal_fn.hasType = function C3_INTERNAL_hasType(type, targets) {
    var $$ = this, types = $$.config.data_types, has = false;
    targets = targets || $$.data.targets;
    if (targets && targets.length) {
        targets.forEach(function (target) {
            var t = types[target.id];
            if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) {
                has = true;
            }
        });
    } else if (Object.keys(types).length) {
        Object.keys(types).forEach(function (id) {
            if (types[id] === type) {
                has = true;
            }
        });
    } else {
        has = $$.config.data_type === type;
    }
    return has;
};
c3_chart_internal_fn.hasArcType = function C3_INTERNAL_hasArcType(targets) {
    return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
};
c3_chart_internal_fn.isLineType = function C3_INTERNAL_isLineType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return !dataType ||
        ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isStepType = function C3_INTERNAL_isStepType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['step', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isSplineType = function C3_INTERNAL_isSplineType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['spline', 'area-spline'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isAreaType = function C3_INTERNAL_isAreaType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return ['area', 'area-spline', 'area-step'].indexOf(dataType) >= 0;
};
c3_chart_internal_fn.isBarType = function C3_INTERNAL_isBarType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return dataType === 'bar';
};
c3_chart_internal_fn.isScatterType = function C3_INTERNAL_isScatterType(d, isSub) {
    var config = this.config,
        id = isString(d) ? d : d.id;
    var dataType = (isSub && config.subchart_types ? config.subchart_types[id] : undefined) || config.data_types[id];
    return dataType === 'scatter';
};
c3_chart_internal_fn.isPieType = function C3_INTERNAL_isPieType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'pie';
};
c3_chart_internal_fn.isGaugeType = function C3_INTERNAL_isGaugeType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'gauge';
};
c3_chart_internal_fn.isDonutType = function C3_INTERNAL_isDonutType(d) {
    var id = isString(d) ? d : d.id;
    return this.config.data_types[id] === 'donut';
};
c3_chart_internal_fn.isArcType = function C3_INTERNAL_isArcType(d) {
    return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
};
c3_chart_internal_fn.lineData = function C3_INTERNAL_lineData(d, isSub) {
    return this.isLineType(d, isSub) ? [d] : [];
};
c3_chart_internal_fn.arcData = function C3_INTERNAL_arcData(d) {
    return this.isArcType(d.data) ? [d] : [];
};
/* not used
 function scatterData(d) {
 return isScatterType(d) ? d.values : [];
 }
 */
c3_chart_internal_fn.barData = function C3_INTERNAL_barData(d, isSub) {
    return this.isBarType(d, isSub) ? d.values : [];
};
c3_chart_internal_fn.lineOrScatterData = function C3_INTERNAL_lineOrScatterData(d, isSub) {
    return this.isLineType(d, isSub) || this.isScatterType(d, isSub) ? d.values : [];
};
c3_chart_internal_fn.barOrLineData = function C3_INTERNAL_barOrLineData(d, isSub) {
    return this.isBarType(d, isSub) || this.isLineType(d, isSub) ? d.values : [];
};
c3_chart_internal_fn.isInterpolationType = function (type) {
    return ['linear', 'linear-closed', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone'].indexOf(type) >= 0;
};
<|endoftext|>


<commit_before>"use strict";

var _ = require("./utils");

var Emitter = require("./emitter");
var NeuDC   = require("./dc");
var NeuUnit = require("./unit");

var SelectorParser = require("./selector-parser");
var makeOutlet = require("./ugen-makeOutlet");

function NeuUGen(synth, key, spec, inputs) {
  Emitter.call(this);
  var parsed = SelectorParser.parse(key);

  if (!NeuUGen.registered.hasOwnProperty(parsed.key)) {
    throw new Error("unknown key: " + key);
  }

  this.$synth   = synth;
  this.$context = synth.$context;
  this.$key   = parsed.key;
  this.$class = parsed.class;
  this.$id    = parsed.id;

  var unit = NeuUGen.registered[parsed.key](this, spec, inputs);

  if (!(unit instanceof NeuUnit)) {
    throw new Error("invalid key: " + key);
  }

  var outlet = makeOutlet(this.$context, unit, spec);

  this.$unit   = unit;
  this.$outlet = outlet.outlet;
  this.$offset = outlet.offset;

  Object.defineProperties(this, {
    context: {
      value: _.findAudioContext(this.$context),
      enumerable: true
    },
    outlet: {
      value: _.findAudioNode(this.$outlet),
      enumerable: true
    },
  });

  _.each(unit.$methods, function(method, name) {
    _.definePropertyIfNotExists(this, name, {
      value: method
    });
  }, this);
}
_.inherits(NeuUGen, Emitter);

NeuUGen.$name = "NeuUGen";

NeuUGen.registered = {};

NeuUGen.register = function(name, func) {
  if (!SelectorParser.isValidUGenName(name)) {
    throw new Error("invalid ugen name: " + name);
  }
  if (typeof func !== "function") {
    throw new TypeError("ugen must be a function");
  }
  NeuUGen.registered[name] = func;
};

NeuUGen.build = function(synth, key, spec, inputs) {
  if (typeof key !== "string") {
    spec.value = key;
    key = _.typeOf(key);
  }

  return new NeuUGen(synth, key, spec, inputs);
};

NeuUGen.prototype.add = function(node) {
  return new NeuUGen(this.$synth, "+", {}, [ this, _.defaults(node, 0) ]);
};

NeuUGen.prototype.mul = function(node) {
  return new NeuUGen(this.$synth, "*", {}, [ this, _.defaults(node, 1) ]);
};

NeuUGen.prototype.madd = function(mul, add) {
  return this.mul(_.defaults(mul, 1)).add(_.defaults(add, 0));
};

NeuUGen.prototype._connect = function(to) {
  _.connect({ from: this.$outlet, to: to });
  if (this.$offset !== 0) {
    if (to instanceof window.AudioParam) {
      to.value = this.$offset;
    } else {
      _.connect({ from: createGainDC(this.$context, this.$offset), to: to });
    }
  }
};

function createGainDC(context, offset) {
  var gain = context.createGain();

  gain.gain.value = offset;

  _.connect({ from: new NeuDC(context, 1), to: gain });

  return gain;
}

module.exports = NeuUGen;
<commit_msg>fix NeuUGen: connect output/input index support<commit_after>"use strict";

var _ = require("./utils");

var Emitter = require("./emitter");
var NeuDC   = require("./dc");
var NeuUnit = require("./unit");

var SelectorParser = require("./selector-parser");
var makeOutlet = require("./ugen-makeOutlet");

function NeuUGen(synth, key, spec, inputs) {
  Emitter.call(this);
  var parsed = SelectorParser.parse(key);

  if (!NeuUGen.registered.hasOwnProperty(parsed.key)) {
    throw new Error("unknown key: " + key);
  }

  this.$synth   = synth;
  this.$context = synth.$context;
  this.$key   = parsed.key;
  this.$class = parsed.class;
  this.$id    = parsed.id;

  var unit = NeuUGen.registered[parsed.key](this, spec, inputs);

  if (!(unit instanceof NeuUnit)) {
    throw new Error("invalid key: " + key);
  }

  var outlet = makeOutlet(this.$context, unit, spec);

  this.$unit   = unit;
  this.$outlet = outlet.outlet;
  this.$offset = outlet.offset;

  Object.defineProperties(this, {
    context: {
      value: _.findAudioContext(this.$context),
      enumerable: true
    },
    outlet: {
      value: _.findAudioNode(this.$outlet),
      enumerable: true
    },
  });

  _.each(unit.$methods, function(method, name) {
    _.definePropertyIfNotExists(this, name, {
      value: method
    });
  }, this);
}
_.inherits(NeuUGen, Emitter);

NeuUGen.$name = "NeuUGen";

NeuUGen.registered = {};

NeuUGen.register = function(name, func) {
  if (!SelectorParser.isValidUGenName(name)) {
    throw new Error("invalid ugen name: " + name);
  }
  if (typeof func !== "function") {
    throw new TypeError("ugen must be a function");
  }
  NeuUGen.registered[name] = func;
};

NeuUGen.build = function(synth, key, spec, inputs) {
  if (typeof key !== "string") {
    spec.value = key;
    key = _.typeOf(key);
  }

  return new NeuUGen(synth, key, spec, inputs);
};

NeuUGen.prototype.add = function(node) {
  return new NeuUGen(this.$synth, "+", {}, [ this, _.defaults(node, 0) ]);
};

NeuUGen.prototype.mul = function(node) {
  return new NeuUGen(this.$synth, "*", {}, [ this, _.defaults(node, 1) ]);
};

NeuUGen.prototype.madd = function(mul, add) {
  return this.mul(_.defaults(mul, 1)).add(_.defaults(add, 0));
};

NeuUGen.prototype._connect = function(to, output, input) {
  _.connect({
    from  : this.$outlet,
    to    : to,
    output: output,
    input : input
  });
  if (this.$offset !== 0) {
    if (to instanceof window.AudioParam) {
      to.value = this.$offset;
    } else {
      _.connect({
        from : createGainDC(this.$context, this.$offset),
        to   : to,
        input: input
      });
    }
  }
};

function createGainDC(context, offset) {
  var gain = context.createGain();

  gain.gain.value = offset;

  _.connect({ from: new NeuDC(context, 1), to: gain });

  return gain;
}

module.exports = NeuUGen;
<|endoftext|>


<commit_before>//! UpUp
//! version : 1.0.0
//! author  : Tal Ater @TalAter
//! license : MIT
//! https://github.com/TalAter/UpUp

(function(undefined) {
  'use strict';

  /**
   * # Quick Start
   *
   * The quickest way to get started is to visit the [UpUp homepage](https://www.talater.com/upup/).
   *
   * Next, you'll want to experiment with the [Getting Started with Offline First using UpUp tutorial](https://www.talater.com/upup/getting-started-with-offline-first.html).
   *
   * Once you're ready for a more in-depth look at the UpUp API, read on.
   *
   * # Good to Know
   *
   * ## Service Worker
   *
   * At the heart of UpUp are *service workers* - a new web technology, which allows developers to take control and shape user's requests to their server.
   *
   * While UpUp abstracts much of service workers' complexity, their browser compatibility issues, and flattens the learning curve, there are a few things to be aware of.
   *
   * ### HTTPS only
   *
   * Service workers, and thus UpUp, only work when the user is accessing your server over a secure connection.
   *
   * During development you can also use UpUp through localhost (e.g. http://localhost/ or http://localhost:1234/)
   *
   * ### Scope
   *
   * UpUp can only serve offline content for requests within its scope. The scope is determined by where you placed the `upup.min.js` and `upup.sw.min.js` files.
   *
   * This means that if you placed the files in your `/js/` directory, UpUp will only be able to show your offline content when users try to look inside the `/js/` directory.
   *
   * **This is why you should almost always place the script as close to the root of your site as possible (e.g. https://www.talater.com/upup.min.js).**
   *
   * #### Scope - Advanced
   * It is possible to keep `upup.min.js` outside the scope (e.g. in a CDN), as long as `upup.sw.min.js` is kept local (that file's location determines the scope).
   * If you choose to keep the two in separate directories, make sure to pass the `service-worker-url` [setting](https://github.com/TalAter/UpUp/tree/master/docs#settings).
   * ````html
   * <script src="//cdnjs.cloudflare.com/ajax/libs/UpUp/1.0.0/upup.min.js"></script>
   * <script>
   * UpUp.start({
   *   'content-url': 'offline.html',
   *   'assets': ['/img/logo.png', '/css/style.css', 'headlines.json'],
   *   'service-worker-url': '/upup.sw.min.js'
   * });
   * </script>
   * ````
   *
   * ### Cache Versions
   *
   * When users visit your page, UpUp stores the files needed to display the offline content in their cache.
   *
   * When you make a change to your offline site, these files need to be downloaded again, and updated in the cache.
   *
   * This cache update happens automatically everytime you change the value of `content`, `content-url`, or `assets` (e.g. changing `content-url` from `offline-v1.html` to `offline-v2.html`).
   *
   * If you changed the content of one of those files, but none of the filenames changed, you may want to manually tell UpUp to update the cache anyway. For this purpose you can use a `cache-version`.
   *
   * For example, consider the following:
   *
   * ````javascript
   * UpUp.start({
   *   'content-url': '/offline.html',
   *   'assets': ['/img/logo.png']
   * });
   * ````
   *
   * If you were to add another file to the `assets` array, UpUp would know it had to download and store a new cache. If however, the change is within the contents of `/offline.html` you will need to signal UpUp that it needs to update the cache by changing the `cache-version`:
   *
   * ````javascript
   * UpUp.start({
   *   'cache-version': 'v2',
   *   'content-url': '/offline.html',
   *   'assets': ['/img/logo.png']
   * });
   * ````
   *
   * Everytime you change the value of `cache-version`, all the files needed to display the offline content will download again and be saved in the user's cache.
   *
   * ## Settings
   *
   * UpUp can be configured either by calling addSettings() with a settings object, or by passing the
   * same settings object directly to the start() method.
   *
   * #### Examples:
   * ````javascript
   * // Set up offline mode with a basic message
   * UpUp.addSettings({ content: 'Cannot reach site. Please check your internet connection.' });
   * UpUp.start();
   *
   * // The same thing can be achieved like this
   * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
   * ````
   *
   * The settings object supports the following options:
   * - `content-url`        (String)  The content to display when user is offline (url to the content that will be served)
   * - `content`            (String)  The content to display when user is offline (plain text, HTML, etc.)
   * - `assets`             (Array)   Array of assets to cache for offline access
   * - `cache-version`      (String|Number) Optional version number, change this when offline files change. UpUp will download and cache all content-url and assets files again
   * - `service-worker-url` (String)  The url to the service worker file (`upup.sw.min.js`)
   *                                  Allows loading `upup.min.js` from a CDN while `upup.sw.min.js` stays local (see [scope](https://github.com/TalAter/UpUp/blob/master/docs/README.md#scope))
   * - `registration-options` (Object) An object containing registration options for ServiceWorkerContainer.register().
   *
   * # API Reference
   */

  // Save a reference to the global object (window in the browser)
  var _root = this;

  // get service worker object
  var _serviceWorker = navigator.serviceWorker;

  // Check browser support
  // This is done as early as possible, to make it as fast as possible for unsupported browsers
  // Requires service worker
  if (!_serviceWorker) {
    _root.UpUp = null;
    return undefined;
  }

  // Settings live here, and these are their defaults
  var _settings = {
    'service-worker-url': 'upup.sw.min.js',
    'registration-options': {},
  };

  var _debugState = false;
  var _debugStyle = 'font-weight: bold; color: #00f;';

  // Expose functionality
  _root.UpUp = {

    /**
     * Make this site available offline
     *
     * Can receive a settings object directly, or be configured by running addSettings() first.
     * See Settings section of docs for details.
     *
     * #### Examples:
     * ````javascript
     * // Set up offline mode with a basic message
     * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
     *
     * // Set up offline mode with the settings defined previously via addSettings()
     * UpUp.start();
     * ````
     *
     * @param {Object} [settings] - Settings for offline mode
     * @method start
     */
    start: function(settings) {
      this.addSettings(settings);

      // register the service worker
      _serviceWorker.register(_settings['service-worker-url'], _settings['registration-options']).then(function(registration) {
        // Registration was successful
        if (_debugState) {
          console.log('Service worker registration successful with scope: %c'+registration.scope, _debugStyle);
        }

        // Send the settings to the service worker
        var messenger = registration.installing || _serviceWorker.controller || registration.active;
        messenger.postMessage({'action': 'set-settings', 'settings': _settings});

      }).catch(function(err) {
        // registration failed :(
        if (_debugState) {
          console.log('Service worker registration failed: %c'+err, _debugStyle);
        }
      });
    },

    /**
     * Adds settings to configure how UpUp behaves.
     * Call this before running start, or just pass the settings object when calling the start method.
     *
     * Receives a mandatory settings object. See Settings section of docs for details.
     *
     * #### Examples:
     * ````javascript
     * // Set up offline mode with a basic message
     * UpUp.addSettings({ content: 'Cannot reach site. Please check your internet connection.' });
     * UpUp.start();
     *
     * // The same thing can be achieved like this
     * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
     * ````
     *
     * @param {Object} [settings] - Settings for offline mode
     * @method addSettings
     * @see [Settings](#settings)
     */
    addSettings: function(settings) {
      settings = settings || {};

      // if we got a string, instead of a settings object, use that string as the content
      if (typeof settings === 'string') {
        settings = { content: settings };
      }

      // add new settings to our settings object
      [
        'content',
        'content-url',
        'assets',
        'service-worker-url',
        'cache-version',
        'registration-options',
      ].forEach(function(settingName) {
        if (settings[settingName] !== undefined) {
          _settings[settingName] = settings[settingName];
        }
      });
    },

    /**
     * Turn on or off the output of debug messages to the console.
     * Don't pass any parameters to turn on, or pass a boolean to explicitly set
     * debug state on or off.
     *
     * @param {Boolean} [newState=true] - Turn on/off debug messages
     * @method debug
     */
    debug: function(newState) {
      if (arguments.length > 0) {
        _debugState = !!newState;
      } else {
        _debugState = true;
      }
    },
  };
}.call(this));
<commit_msg>User no longer passes a `registration-options` settings but a `scope` setting<commit_after>//! UpUp
//! version : 1.0.0
//! author  : Tal Ater @TalAter
//! license : MIT
//! https://github.com/TalAter/UpUp

(function(undefined) {
  'use strict';

  /**
   * # Quick Start
   *
   * The quickest way to get started is to visit the [UpUp homepage](https://www.talater.com/upup/).
   *
   * Next, you'll want to experiment with the [Getting Started with Offline First using UpUp tutorial](https://www.talater.com/upup/getting-started-with-offline-first.html).
   *
   * Once you're ready for a more in-depth look at the UpUp API, read on.
   *
   * # Good to Know
   *
   * ## Service Worker
   *
   * At the heart of UpUp are *service workers* - a new web technology, which allows developers to take control and shape user's requests to their server.
   *
   * While UpUp abstracts much of service workers' complexity, their browser compatibility issues, and flattens the learning curve, there are a few things to be aware of.
   *
   * ### HTTPS only
   *
   * Service workers, and thus UpUp, only work when the user is accessing your server over a secure connection.
   *
   * During development you can also use UpUp through localhost (e.g. http://localhost/ or http://localhost:1234/)
   *
   * ### Scope
   *
   * UpUp can only serve offline content for requests within its scope. The scope is determined by where you placed the `upup.min.js` and `upup.sw.min.js` files.
   *
   * This means that if you placed the files in your `/js/` directory, UpUp will only be able to show your offline content when users try to look inside the `/js/` directory.
   *
   * **This is why you should almost always place the script as close to the root of your site as possible (e.g. https://www.talater.com/upup.min.js).**
   *
   * #### Scope - Advanced
   * It is possible to keep `upup.min.js` outside the scope (e.g. in a CDN), as long as `upup.sw.min.js` is kept local (that file's location determines the scope).
   * If you choose to keep the two in separate directories, make sure to pass the `service-worker-url` [setting](https://github.com/TalAter/UpUp/tree/master/docs#settings).
   * ````html
   * <script src="//cdnjs.cloudflare.com/ajax/libs/UpUp/1.0.0/upup.min.js"></script>
   * <script>
   * UpUp.start({
   *   'content-url': 'offline.html',
   *   'assets': ['/img/logo.png', '/css/style.css', 'headlines.json'],
   *   'service-worker-url': '/upup.sw.min.js'
   * });
   * </script>
   * ````
   *
   * ### Cache Versions
   *
   * When users visit your page, UpUp stores the files needed to display the offline content in their cache.
   *
   * When you make a change to your offline site, these files need to be downloaded again, and updated in the cache.
   *
   * This cache update happens automatically everytime you change the value of `content`, `content-url`, or `assets` (e.g. changing `content-url` from `offline-v1.html` to `offline-v2.html`).
   *
   * If you changed the content of one of those files, but none of the filenames changed, you may want to manually tell UpUp to update the cache anyway. For this purpose you can use a `cache-version`.
   *
   * For example, consider the following:
   *
   * ````javascript
   * UpUp.start({
   *   'content-url': '/offline.html',
   *   'assets': ['/img/logo.png']
   * });
   * ````
   *
   * If you were to add another file to the `assets` array, UpUp would know it had to download and store a new cache. If however, the change is within the contents of `/offline.html` you will need to signal UpUp that it needs to update the cache by changing the `cache-version`:
   *
   * ````javascript
   * UpUp.start({
   *   'cache-version': 'v2',
   *   'content-url': '/offline.html',
   *   'assets': ['/img/logo.png']
   * });
   * ````
   *
   * Everytime you change the value of `cache-version`, all the files needed to display the offline content will download again and be saved in the user's cache.
   *
   * ## Settings
   *
   * UpUp can be configured either by calling addSettings() with a settings object, or by passing the
   * same settings object directly to the start() method.
   *
   * #### Examples:
   * ````javascript
   * // Set up offline mode with a basic message
   * UpUp.addSettings({ content: 'Cannot reach site. Please check your internet connection.' });
   * UpUp.start();
   *
   * // The same thing can be achieved like this
   * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
   * ````
   *
   * The settings object supports the following options:
   * - `content-url`        (String)  The content to display when user is offline (url to the content that will be served)
   * - `content`            (String)  The content to display when user is offline (plain text, HTML, etc.)
   * - `assets`             (Array)   Array of assets to cache for offline access
   * - `cache-version`      (String|Number) Optional version number, change this when offline files change. UpUp will download and cache all content-url and assets files again
   * - `service-worker-url` (String)  The url to the service worker file (`upup.sw.min.js`)
   *                                  Allows loading `upup.min.js` from a CDN while `upup.sw.min.js` stays local (see [scope](https://github.com/TalAter/UpUp/blob/master/docs/README.md#scope))
   * - `scope`              (String)  The scope to limit the service worker to (see [scope](https://github.com/TalAter/UpUp/blob/master/docs/README.md#scope))
   *
   * # API Reference
   */

  // Save a reference to the global object (window in the browser)
  var _root = this;

  // get service worker object
  var _serviceWorker = navigator.serviceWorker;

  // Check browser support
  // This is done as early as possible, to make it as fast as possible for unsupported browsers
  // Requires service worker
  if (!_serviceWorker) {
    _root.UpUp = null;
    return undefined;
  }

  // Settings live here, and these are their defaults
  var _settings = {
    'service-worker-url': 'upup.sw.min.js',
    'registration-options': {},
  };

  var _debugState = false;
  var _debugStyle = 'font-weight: bold; color: #00f;';

  // Expose functionality
  _root.UpUp = {

    /**
     * Make this site available offline
     *
     * Can receive a settings object directly, or be configured by running addSettings() first.
     * See Settings section of docs for details.
     *
     * #### Examples:
     * ````javascript
     * // Set up offline mode with a basic message
     * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
     *
     * // Set up offline mode with the settings defined previously via addSettings()
     * UpUp.start();
     * ````
     *
     * @param {Object} [settings] - Settings for offline mode
     * @method start
     */
    start: function(settings) {
      this.addSettings(settings);

      // register the service worker
      _serviceWorker.register(_settings['service-worker-url'], _settings['registration-options']).then(function(registration) {
        // Registration was successful
        if (_debugState) {
          console.log('Service worker registration successful with scope: %c'+registration.scope, _debugStyle);
        }

        // Send the settings to the service worker
        var messenger = registration.installing || _serviceWorker.controller || registration.active;
        messenger.postMessage({'action': 'set-settings', 'settings': _settings});

      }).catch(function(err) {
        // registration failed :(
        if (_debugState) {
          console.log('Service worker registration failed: %c'+err, _debugStyle);
        }
      });
    },

    /**
     * Adds settings to configure how UpUp behaves.
     * Call this before running start, or just pass the settings object when calling the start method.
     *
     * Receives a mandatory settings object. See Settings section of docs for details.
     *
     * #### Examples:
     * ````javascript
     * // Set up offline mode with a basic message
     * UpUp.addSettings({ content: 'Cannot reach site. Please check your internet connection.' });
     * UpUp.start();
     *
     * // The same thing can be achieved like this
     * UpUp.start({ content: 'Cannot reach site. Please check your internet connection.' });
     * ````
     *
     * @param {Object} [settings] - Settings for offline mode
     * @method addSettings
     * @see [Settings](#settings)
     */
    addSettings: function(settings) {
      settings = settings || {};

      // if we got a string, instead of a settings object, use that string as the content
      if (typeof settings === 'string') {
        settings = { content: settings };
      }

      // add new settings to our settings object
      [
        'content',
        'content-url',
        'assets',
        'service-worker-url',
        'cache-version',
      ].forEach(function(settingName) {
        if (settings[settingName] !== undefined) {
          _settings[settingName] = settings[settingName];
        }
      });

      // Add scope setting
      if (settings['scope'] !== undefined) {
        _settings['registration-options']['scope'] = settings[scope];
      }
    },

    /**
     * Turn on or off the output of debug messages to the console.
     * Don't pass any parameters to turn on, or pass a boolean to explicitly set
     * debug state on or off.
     *
     * @param {Boolean} [newState=true] - Turn on/off debug messages
     * @method debug
     */
    debug: function(newState) {
      if (arguments.length > 0) {
        _debugState = !!newState;
      } else {
        _debugState = true;
      }
    },
  };
}.call(this));
<|endoftext|>


<commit_before>export const urls = {
    'newstories': (page) => `https://node-hnapi.herokuapp.com/newest?page=${page}`,
    'topstories': (page) => `https://node-hnapi.herokuapp.com/news?page=${page}`,
    'askstories': (page) => `https://node-hnapi.herokuapp.com/ask?page=${page}`,
    'jobstories': (page) => `https://node-hnapi.herokuapp.com/jobs?page=${page}`,
    'showstories': (page) => `https://node-hnapi.herokuapp.com/show?page=${page}`,
    'item': (id) => `https://node-hnapi.herokuapp.com/item/${id}`
};<commit_msg>Trying out hnpwa.com<commit_after>export const urls = {
    'newstories': (page) => `https://hnpwa.com/api/v0/newest.json?page=${page}`,
    'topstories': (page) => `https://hnpwa.com/api/v0/news.json?page=${page}`,
    'askstories': (page) => `https://hnpwa.com/api/v0/ask.json?page=${page}`,
    'jobstories': (page) => `https://hnpwa.com/api/v0/jobs.json?page=${page}`,
    'showstories': (page) => `https://hnpwa.com/api/v0/show.json?page=${page}`,
    'item': (id) => `https://hnpwa.com/api/v0/item/${id}.json`
};<|endoftext|>


<commit_before>/*global describe, it, beforeEach */
/*jshint expr:true */

'use strict';

var fs = require('fs')
  , cheerio = require('cheerio');

var expect = require('chai').expect
  , sinon = require('sinon')
  , packerator = require('../');

var testFile = __dirname + '/fixtures/foo.html'
  , testHtml = fs.readFileSync(testFile).toString()
  , barjs = fs.readFileSync(__dirname + '/fixtures/bar.js').toString().trim()
  , barcss = fs.readFileSync(__dirname + '/fixtures/bar.css').toString().trim();

var request = require('request');

// I don't want to reach out to the internets so let's stub `request.get`
sinon.stub(request, 'get', function(url, cb) {
  var map = {
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/foo.html': testHtml,
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.js': barjs,
    'https://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.js': barjs,
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.css': barcss,
    'https://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.css': barcss
  };
  if(map[url]) {
    cb(null, {statusCode: 200}, map[url]);
  } else {
    cb(null, {statusCode: 404}, 'FROWN TOWN');
  }
});

describe('packerator', function() {

  var $, err, packedHtml;

  beforeEach(function(done) {
    packerator(testHtml, {cwd: __dirname + '/fixtures'}, function(e, html) {
      err = e;
      packedHtml = html;
      $ = cheerio.load(packedHtml);
      done();
    });
  });

  it('should not generate an error', function() {
    expect(err).to.not.be.ok;
    expect(packedHtml).to.be.ok;
  });

  it.skip('should return a promise', function() {
    expect(false).to.be.ok;
  });

  it('should remove src attributes from scripts', function() {
    expect($('#script-local').attr('src')).to.not.be.ok;
  });

  it('should inline local JavaScript sources', function() {
    var actual = $('#script-local').html().trim();
    expect(actual).to.equal(barjs);
  });

  it('should inline remote JavaScript sources', function() {
    var actual = $('#script-remote').html().trim();
    expect(actual).to.equal(barjs);
  });

  it('should inline local stylesheets', function() {
    var actual = $('#style-local').html().trim();
    expect(actual).to.equal(barcss);
  });

  it('should not leave href attributes on style tags', function() {
    expect($('#style-local').attr('href')).to.not.be.ok;
  });

  it('should not leave rel attributes on style tags', function() {
    expect($('#style-local').attr('rel')).to.not.be.ok;
  });

  it('should inline remote stylesheets', function() {
    var actual = $('#style-remote').html().trim();
    expect(actual).to.equal(barcss);
  });

  it.skip('should datauri-ify local images', function() {
    expect(false).to.be.ok;
  });

  it.skip('should datauri-ify remote images', function() {
    expect(false).to.be.ok;
  });

  it.skip('should datauri-ify images in stylesheets', function() {
    expect(false).to.be.ok;
  });

  it.skip('should datauri-ify images in inline styles', function() {
    expect(false).to.be.ok;
  });

  it.skip('should not generte an error when there is nothing to do', function() {
    expect(false).to.be.ok;
  });

});
<commit_msg>test: Add tests for inlining raw images<commit_after>/*global describe, it, beforeEach */
/*jshint expr:true */

'use strict';

var fs = require('fs')
  , cheerio = require('cheerio');

var expect = require('chai').expect
  , sinon = require('sinon')
  , packerator = require('../');

var testFile = __dirname + '/fixtures/foo.html'
  , testHtml = fs.readFileSync(testFile).toString()
  , barjs = fs.readFileSync(__dirname + '/fixtures/bar.js').toString().trim()
  , barcss = fs.readFileSync(__dirname + '/fixtures/bar.css').toString().trim()
  , barjpg = fs.readFileSync(__dirname + '/fixtures/bar.jpg').toString('base64').trim()
  , barjpgDataUri = 'data:image/jpeg;base64,'+barjpg;

var request = require('request');

// I don't want to reach out to the internets so let's stub `request.get`
sinon.stub(request, 'get', function(url, cb) {
  var map = {
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/foo.html': testHtml,
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.js': barjs,
    'https://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.js': barjs,
    'http://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.css': barcss,
    'https://rawgit.com/jtrussell/packerator/master/test/fixtures/bar.css': barcss
  };
  if(map[url]) {
    cb(null, {statusCode: 200}, map[url]);
  } else {
    cb(null, {statusCode: 404}, 'FROWN TOWN');
  }
});

describe('packerator', function() {

  var $, err, packedHtml;

  beforeEach(function(done) {
    packerator(testHtml, {cwd: __dirname + '/fixtures'}, function(e, html) {
      err = e;
      packedHtml = html;
      $ = cheerio.load(packedHtml);
      done();
    });
  });

  it('should not generate an error', function() {
    expect(err).to.not.be.ok;
    expect(packedHtml).to.be.ok;
  });

  it.skip('should return a promise', function() {
    expect(false).to.be.ok;
  });

  it('should remove src attributes from scripts', function() {
    expect($('#script-local').attr('src')).to.not.be.ok;
  });

  it('should inline local JavaScript sources', function() {
    var actual = $('#script-local').html().trim();
    expect(actual).to.equal(barjs);
  });

  it('should inline remote JavaScript sources', function() {
    var actual = $('#script-remote').html().trim();
    expect(actual).to.equal(barjs);
  });

  it('should inline local stylesheets', function() {
    var actual = $('#style-local').html().trim();
    expect(actual).to.equal(barcss);
  });

  it('should not leave href attributes on style tags', function() {
    expect($('#style-local').attr('href')).to.not.be.ok;
  });

  it('should not leave rel attributes on style tags', function() {
    expect($('#style-local').attr('rel')).to.not.be.ok;
  });

  it('should inline remote stylesheets', function() {
    var actual = $('#style-remote').html().trim();
    expect(actual).to.equal(barcss);
  });

  it('should datauri-ify local images', function() {
    var actual = $('#img-local').attr('src');
    expect(actual).to.equal(barjpgDataUri);
  });

  it('should datauri-ify remote images', function() {
    var actual = $('#img-local').attr('src');
    expect(actual).to.equal(barjpgDataUri);
  });

  it.skip('should datauri-ify images in stylesheets', function() {
    expect(false).to.be.ok;
  });

  it.skip('should datauri-ify images in inline styles', function() {
    expect(false).to.be.ok;
  });

  it.skip('should not generte an error when there is nothing to do', function() {
    expect(false).to.be.ok;
  });

});
<|endoftext|>


<commit_before>var test = require('ava');

// Fake plugin-structure for wintersmith
var env = {
  helpers: {},
  locals: {}
};
require('../../plugins/helpers')(env, function helpers() {});

/**
 * Creates a page-mock
 *
 * @param {string} path - relative filepath
 * @return {object} page-mock
 */
function createPageMock(path) {
  return {
    filepath: {
      relative: path
    },
    parent: {
      parent: {
        alerts: {},
        progress: {}
      }
    }
  };
}

/**
 * Creates a contents-mock
 *
 * @return {object} contents-mock
 */
function createContentsMock() {
  return {
    javascripts: {
      'base.js': {
        filepath: {
          relative: 'javascripts/base.js'
        }
      }
    },
    stylesheets: {
      'base.css': {
        filepath: {
          relative: 'stylesheets/base.css'
        }
      }
    }
  };
}

test('getPatternTitle', function getPatternTitle(t) {
  var pageMock = createPageMock('/patterns/components/alerts/default.md');

  var title = env.helpers.getPatternTitle(pageMock);
  var expected = 'Alerts';

  t.is(title, expected);
});

test('getSubcategoryUrl', function getSubcategoryUrl(t) {
  var pagesMock = {
    'default.md': createPageMock('/patterns/components/alerts/default.md'),
    'link-color.md': createPageMock('patterns/components/alerts/link-color.md')
  };

  var url = env.helpers.getSubcategoryUrl(pagesMock);
  var expected = '/patterns/components/alerts/default.html';

  t.is(url, expected);
});

test('getJavaScriptFiles with config', function getJavaScriptFiles(t) {
  var contentMock = createContentsMock();
  var expected = ['http://www.example.com/a.js', 'http://www.example.com/b.js'];
  var javaScriptFiles;

  env.locals = { javascripts: expected };
  javaScriptFiles = env.helpers.getJavaScriptFiles(contentMock);

  t.same(javaScriptFiles, expected);
});

// TODO: Doesn't work
test('getJavaScriptFiles without config', function getJavaScriptFiles(t) {
  var contentMock = createContentsMock();
  var javaScriptFiles;

  env.locals = {};
  javaScriptFiles = env.helpers.getJavaScriptFiles(contentMock);

  t.same(javaScriptFiles, ['javascripts/base.js']);
});

test('getStylesheetFiles with config', function getStylesheetFiles(t) {
  var contentMock = createContentsMock();
  var expected = ['http://www.example.com/a.css', 'http://www.example.com/b.css'];
  var stylesheetFiles;

  env.locals = { stylesheets: expected };
  stylesheetFiles = env.helpers.getStylesheetFiles(contentMock);

  t.same(stylesheetFiles, expected);
});

// TODO: Doesn't work
test('getStylesheetFiles without config', function getStylesheetFiles(t) {
  var contentMock = createContentsMock();
  var stylesheetFiles;

  env.locals = {};
  stylesheetFiles = env.helpers.getStylesheetFiles(contentMock);

  t.same(stylesheetFiles, ['stylesheets/base.css']);
});
<commit_msg>changed unit-tests for behavior of url-property in wintersmith<commit_after>var test = require('ava');

// Fake plugin-structure for wintersmith
var env = {
  helpers: {},
  locals: {}
};
require('../../plugins/helpers')(env, function helpers() {});

/**
 * Creates a page-mock
 *
 * @param {string} path - relative filepath
 * @return {object} page-mock
 */
function createPageMock(path) {
  return {
    filepath: {
      relative: path
    },
    parent: {
      parent: {
        alerts: {},
        progress: {}
      }
    }
  };
}

/**
 * Creates a contents-mock
 *
 * @return {object} contents-mock
 */
function createContentsMock() {
  return {
    javascripts: {
      'base.js': {
        filepath: {
          relative: 'javascripts/base.js'
        },
        // wintersmith would use url-module
        // and resolve the relative path with the base-url
        url: '/javascripts/base.js'
      }
    },
    stylesheets: {
      'base.css': {
        filepath: {
          relative: 'stylesheets/base.css'
        },
        url: '/stylesheets/base.css'
      }
    }
  };
}

test('getPatternTitle', function getPatternTitle(t) {
  var pageMock = createPageMock('/patterns/components/alerts/default.md');

  var title = env.helpers.getPatternTitle(pageMock);
  var expected = 'Alerts';

  t.is(title, expected);
});

test('getSubcategoryUrl', function getSubcategoryUrl(t) {
  var pagesMock = {
    'default.md': createPageMock('/patterns/components/alerts/default.md'),
    'link-color.md': createPageMock('patterns/components/alerts/link-color.md')
  };

  var url = env.helpers.getSubcategoryUrl(pagesMock);
  var expected = '/patterns/components/alerts/default.html';

  t.is(url, expected);
});

test('getJavaScriptFiles with config', function getJavaScriptFiles(t) {
  var contentMock = createContentsMock();
  var expected = ['http://www.example.com/a.js', 'http://www.example.com/b.js'];
  var javaScriptFiles;

  env.locals = { javascripts: expected };
  javaScriptFiles = env.helpers.getJavaScriptFiles(contentMock);

  t.same(javaScriptFiles, expected);
});

test('getJavaScriptFiles without config', function getJavaScriptFiles(t) {
  var contentMock = createContentsMock();
  var javaScriptFiles;

  env.locals = {};
  javaScriptFiles = env.helpers.getJavaScriptFiles(contentMock);

  t.same(javaScriptFiles, ['/javascripts/base.js']);
});

test('getStylesheetFiles with config', function getStylesheetFiles(t) {
  var contentMock = createContentsMock();
  var expected = ['http://www.example.com/a.css', 'http://www.example.com/b.css'];
  var stylesheetFiles;

  env.locals = { stylesheets: expected };
  stylesheetFiles = env.helpers.getStylesheetFiles(contentMock);

  t.same(stylesheetFiles, expected);
});

test('getStylesheetFiles without config', function getStylesheetFiles(t) {
  var contentMock = createContentsMock();
  var stylesheetFiles;

  env.locals = {};
  stylesheetFiles = env.helpers.getStylesheetFiles(contentMock);

  t.same(stylesheetFiles, ['/stylesheets/base.css']);
});
<|endoftext|>


<commit_before>/**
 * Creates an object of type wgaPipeline for drawing whole genome alignment visualizations
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @constructor
 * @param {Object} svg - jQuery object containing a svg DOM element. Visualizations will be drawn on this svg. Size may be changed by object methods. Previous content will be deleted.
 * @example
 * // initializes an wgaPipeline object (wga) on the svg element with id 'canvas'
 * var svg = $('#canvas');
 * var wga = new wgaPipeline(svg);
 */
function WgaPipeline(svg) {
	/**
	 * property to contain the svg DOM element as jQuery Object
	 */
	this.svg = svg;
	/**
	 * property to contain the svg DOM element as d3 Object
	 */
	this.svgD3 = d3.selectAll(svg);
	/**
	 * property to store the data
	 * @property {Object}  karyo                        - the chromosome information
	 * @property {Array}   karyo.order                  - array of chromosome IDs in the desired order (circular layout)
	 * @property {Array}   karyo.genome_order           - array of genome IDs in the desired order (linear layout)
	 * @property {Object}  karyo.chromosomes            - the chromosome details, karyo IDs as keys
	 * @property {Number}  karyo.chromosomes.genome_id  - number of genome to which this chromosome belongs
	 * @property {Number}  karyo.chromosomes.length     - length in bp
	 * @property {Boolean} karyo.chromosomes.rc         - should the sequence be treated as its reverse complement
	 * @property {String}  karyo.chromosomes.seq        - sequence of the chromosome
	 * @property {Object}  features                     - the feature information, feature IDs as keys
	 * @property {String}  features.karyo               - the karyo ID
	 * @property {Number}  features.start               - start position on the sequence
	 * @property {Number}  features.end                 - end position on the sequence
	 * @property {Object}  links                        - the link information
	 * @property {String}  links.source                 - source feature of the link
	 * @property {String}  links.target                 - target feature of the link
	 * @property {Number}  links.identity               - identity of the link
	 */
	this.data = {};
	/**
	 * property to store configuration options
	 * @property {Number}  width                  - The width of the svg in px.
	 * @property {Number}  height                 - The height of the svg in px.
	 * @property {Object}  linear                 - The configuration options for the linear layout.
	 * @property {Number}  linear.genomeDistance  - The vertical distance between adjacent genomes in px.
	 * @property {Number}  linear.karyoHeight     - The height of each chromosome in px.
	 * @property {Number}  linear.karyoDistance   - The horizontal distance between adjacent chromosomes of the same genome in bp.
	 * @property {Object}  circular               - The configuration options for the circular layout.
	 * @property {Number}  circular.karyoDistance - The distance between adjacent chromosomes on the circle in bp.
	 */
	this.conf = {
		width: 1000,
		height: 1000,
		linear: {
			genomeDistance: 300,
			karyoHeight: 30,
			karyoDistance: 10
		},
		circular: {
			karyoDistance: 10
		}
	};
	// Initialize svg size
	svg.height(this.conf.height);
	svg.width(this.conf.width);
}

/**
 * Sets the data of the WgaPipeline object.
 * For the required format see the example
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @param {Object} data - Object containing karyo, link and feature information
 * @example
 * var svg = $('#canvas');
 * var wga = new wgaPipeline(svg);
 * var karyo = {
 * 	'order': ['c1', 'c2'],
 * 	'genome_order': ['0', '1'],
 * 	'chromosomes': {
 * 	'c1': {'genome_id': 0, 'length': 2000, 'rc': false, 'seq': null},
 * 	'c2': {'genome_id': 1, 'length': 1000, 'rc': false, 'seq': null}
 * 	}
 * };
 * var features = {
 * 	'f1': {'karyo': 'c1', 'start': 300, 'end': 800},
 * 	'f2': {'karyo': 'c2', 'start': 100, 'end': 600}
 * };
 * var links = [
 * 	{'source': 'f1', 'target': 'f2', 'identity': 90}
 * ];
 * wga.setData({'karyo': karyo, 'features': features, 'links': links};
 */
WgaPipeline.prototype.setData = function(data) {
	this.data = data;
};

/**
 * Calculates coordinates for the chromosomes to draw in the linear layout.
 * This function operates on the data property of the object and therefore needs no parameters.
 * This function is primarily meant for internal usage, the user should not need to call this directly.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @returns {Array} Array containing one Object for each element in data.karyo of the form {karyo: 'karyo_name', x:0, y:0, width:10, height:10}
 */
WgaPipeline.prototype.getLinearKaryoCoords = function() {
	var linearKaryoCoords = [];
	var genome_order = this.data.karyo.genome_order;
	var conf = this.conf;

	var total = [];
	var current = [];
	var i;
	// Initialize total with the negative of one karyoDistance - as there is one space less then karyos per genome
	for (i = 0; i < genome_order.length; i++) {
		total.push(-conf.linear.karyoDistance);
		current.push(0);
	}

	$.each(this.data.karyo.chromosomes, function(key, value) {
		total[genome_order.indexOf(value.genome_id)] += value.length + conf.linear.karyoDistance;
	});

	var maxTotalSize = Math.max.apply(null, total);

	for (i = 0; i < this.data.karyo.order.length; i++) {
		var key = this.data.karyo.order[i];
		var value = this.data.karyo.chromosomes[key];
		var coord = {
			'karyo': key,
			'x': (current[genome_order.indexOf(value.genome_id)] / maxTotalSize) * conf.width,
			'y': genome_order.indexOf(value.genome_id) * conf.linear.genomeDistance,
			'width': (value.length / maxTotalSize) * conf.width,
			'height': conf.linear.karyoHeight
		};
		current[genome_order.indexOf(value.genome_id)] += value.length + conf.linear.karyoDistance;
		linearKaryoCoords.push(coord);
	}

	return linearKaryoCoords;
};

/**
 * This function draws the karyos in the linear layout
 * @author Markus Ankenbrand and Sonja Hohlfeld
 * @param {Array} The array containing the coordinates as returned by getLinearKaryoCoords()
 */
WgaPipeline.prototype.drawLinearKaryo = function(coords) {
	this.svgD3.selectAll(".karyoGroup").remove();
	this.svgD3.append("g")
		.attr("class", "karyoGroup")
		.selectAll("path")
		.data(coords)
		.enter()
		.append("rect")
		.attr("class", "karyo")
		.attr("x", function(d) {
			return d.x;
		})
		.attr("y", function(d) {
			return d.y;
		})
		.attr("width", function(d) {
			return d.width;
		})
		.attr("height", function(d) {
			return d.height;
		});
};

/**
 * This function draws the data in the linear layout.
 * It operates on the data of the object and therefore needs no parameters.
 * It draws directly on the svg and therefore has no return value.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 */
WgaPipeline.prototype.drawLinear = function() {
	var karyoCoords = this.getLinearKaryoCoords();
	this.drawLinearKaryo(karyoCoords);
};

/**
 * Calculates coordinates for the chromosomes to draw in the circular layout.
 * This function operates on the data property of the object and therefore needs no parameters.
 * This function is primarily meant for internal usage, the user should not need to call this directly.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @returns {Array} Array containing one Object for each element in data.karyo of the form {karyo: 'karyo_name', startAngle:0, endAngle:1}
 */
WgaPipeline.prototype.getCircularKaryoCoords = function() {
	var circularKaryoCoords = [];
	var total = 0;
	var spacer = this.conf.circular.karyoDistance;
	var karyo = this.data.karyo;
	var current = -spacer;
	$.each(karyo.chromosomes, function(key, value) {
		total += value.length + spacer;
	});
	for (var i = 0; i < karyo.order.length; i++) {
		var key = karyo.order[i];
		var value = karyo.chromosomes[key];
		var data = {
			"karyo": key,
			"startAngle": ((current + spacer) / total) * (2 * Math.PI),
		};
		current += value.length + spacer;
		data.endAngle = (current / total) * (2 * Math.PI);
		if (value.rc === true) {
			var startAngle = data.startAngle;
			var endAngle = data.endAngle;
			data.startAngle = endAngle;
			data.endAngle = startAngle;
		}
		circularKaryoCoords.push(data);
	}
	return circularKaryoCoords;
};
<commit_msg>commented out the untested reverse complement functionality<commit_after>/**
 * Creates an object of type wgaPipeline for drawing whole genome alignment visualizations
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @constructor
 * @param {Object} svg - jQuery object containing a svg DOM element. Visualizations will be drawn on this svg. Size may be changed by object methods. Previous content will be deleted.
 * @example
 * // initializes an wgaPipeline object (wga) on the svg element with id 'canvas'
 * var svg = $('#canvas');
 * var wga = new wgaPipeline(svg);
 */
function WgaPipeline(svg) {
	/**
	 * property to contain the svg DOM element as jQuery Object
	 */
	this.svg = svg;
	/**
	 * property to contain the svg DOM element as d3 Object
	 */
	this.svgD3 = d3.selectAll(svg);
	/**
	 * property to store the data
	 * @property {Object}  karyo                        - the chromosome information
	 * @property {Array}   karyo.order                  - array of chromosome IDs in the desired order (circular layout)
	 * @property {Array}   karyo.genome_order           - array of genome IDs in the desired order (linear layout)
	 * @property {Object}  karyo.chromosomes            - the chromosome details, karyo IDs as keys
	 * @property {Number}  karyo.chromosomes.genome_id  - number of genome to which this chromosome belongs
	 * @property {Number}  karyo.chromosomes.length     - length in bp
	 * @property {Boolean} karyo.chromosomes.rc         - should the sequence be treated as its reverse complement
	 * @property {String}  karyo.chromosomes.seq        - sequence of the chromosome
	 * @property {Object}  features                     - the feature information, feature IDs as keys
	 * @property {String}  features.karyo               - the karyo ID
	 * @property {Number}  features.start               - start position on the sequence
	 * @property {Number}  features.end                 - end position on the sequence
	 * @property {Object}  links                        - the link information
	 * @property {String}  links.source                 - source feature of the link
	 * @property {String}  links.target                 - target feature of the link
	 * @property {Number}  links.identity               - identity of the link
	 */
	this.data = {};
	/**
	 * property to store configuration options
	 * @property {Number}  width                  - The width of the svg in px.
	 * @property {Number}  height                 - The height of the svg in px.
	 * @property {Object}  linear                 - The configuration options for the linear layout.
	 * @property {Number}  linear.genomeDistance  - The vertical distance between adjacent genomes in px.
	 * @property {Number}  linear.karyoHeight     - The height of each chromosome in px.
	 * @property {Number}  linear.karyoDistance   - The horizontal distance between adjacent chromosomes of the same genome in bp.
	 * @property {Object}  circular               - The configuration options for the circular layout.
	 * @property {Number}  circular.karyoDistance - The distance between adjacent chromosomes on the circle in bp.
	 */
	this.conf = {
		width: 1000,
		height: 1000,
		linear: {
			genomeDistance: 300,
			karyoHeight: 30,
			karyoDistance: 10
		},
		circular: {
			karyoDistance: 10
		}
	};
	// Initialize svg size
	svg.height(this.conf.height);
	svg.width(this.conf.width);
}

/**
 * Sets the data of the WgaPipeline object.
 * For the required format see the example
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @param {Object} data - Object containing karyo, link and feature information
 * @example
 * var svg = $('#canvas');
 * var wga = new wgaPipeline(svg);
 * var karyo = {
 * 	'order': ['c1', 'c2'],
 * 	'genome_order': ['0', '1'],
 * 	'chromosomes': {
 * 	'c1': {'genome_id': 0, 'length': 2000, 'rc': false, 'seq': null},
 * 	'c2': {'genome_id': 1, 'length': 1000, 'rc': false, 'seq': null}
 * 	}
 * };
 * var features = {
 * 	'f1': {'karyo': 'c1', 'start': 300, 'end': 800},
 * 	'f2': {'karyo': 'c2', 'start': 100, 'end': 600}
 * };
 * var links = [
 * 	{'source': 'f1', 'target': 'f2', 'identity': 90}
 * ];
 * wga.setData({'karyo': karyo, 'features': features, 'links': links};
 */
WgaPipeline.prototype.setData = function(data) {
	this.data = data;
};

/**
 * Calculates coordinates for the chromosomes to draw in the linear layout.
 * This function operates on the data property of the object and therefore needs no parameters.
 * This function is primarily meant for internal usage, the user should not need to call this directly.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @returns {Array} Array containing one Object for each element in data.karyo of the form {karyo: 'karyo_name', x:0, y:0, width:10, height:10}
 */
WgaPipeline.prototype.getLinearKaryoCoords = function() {
	var linearKaryoCoords = [];
	var genome_order = this.data.karyo.genome_order;
	var conf = this.conf;

	var total = [];
	var current = [];
	var i;
	// Initialize total with the negative of one karyoDistance - as there is one space less then karyos per genome
	for (i = 0; i < genome_order.length; i++) {
		total.push(-conf.linear.karyoDistance);
		current.push(0);
	}

	$.each(this.data.karyo.chromosomes, function(key, value) {
		total[genome_order.indexOf(value.genome_id)] += value.length + conf.linear.karyoDistance;
	});

	var maxTotalSize = Math.max.apply(null, total);

	for (i = 0; i < this.data.karyo.order.length; i++) {
		var key = this.data.karyo.order[i];
		var value = this.data.karyo.chromosomes[key];
		var coord = {
			'karyo': key,
			'x': (current[genome_order.indexOf(value.genome_id)] / maxTotalSize) * conf.width,
			'y': genome_order.indexOf(value.genome_id) * conf.linear.genomeDistance,
			'width': (value.length / maxTotalSize) * conf.width,
			'height': conf.linear.karyoHeight
		};
		current[genome_order.indexOf(value.genome_id)] += value.length + conf.linear.karyoDistance;
		linearKaryoCoords.push(coord);
	}

	return linearKaryoCoords;
};

/**
 * This function draws the karyos in the linear layout
 * @author Markus Ankenbrand and Sonja Hohlfeld
 * @param {Array} The array containing the coordinates as returned by getLinearKaryoCoords()
 */
WgaPipeline.prototype.drawLinearKaryo = function(coords) {
	this.svgD3.selectAll(".karyoGroup").remove();
	this.svgD3.append("g")
		.attr("class", "karyoGroup")
		.selectAll("path")
		.data(coords)
		.enter()
		.append("rect")
		.attr("class", "karyo")
		.attr("x", function(d) {
			return d.x;
		})
		.attr("y", function(d) {
			return d.y;
		})
		.attr("width", function(d) {
			return d.width;
		})
		.attr("height", function(d) {
			return d.height;
		});
};

/**
 * This function draws the data in the linear layout.
 * It operates on the data of the object and therefore needs no parameters.
 * It draws directly on the svg and therefore has no return value.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 */
WgaPipeline.prototype.drawLinear = function() {
	var karyoCoords = this.getLinearKaryoCoords();
	this.drawLinearKaryo(karyoCoords);
};

/**
 * Calculates coordinates for the chromosomes to draw in the circular layout.
 * This function operates on the data property of the object and therefore needs no parameters.
 * This function is primarily meant for internal usage, the user should not need to call this directly.
 * @author Markus Ankenbrand <markus.ankenbrand@uni-wuerzburg.de>
 * @returns {Array} Array containing one Object for each element in data.karyo of the form {karyo: 'karyo_name', startAngle:0, endAngle:1}
 */
WgaPipeline.prototype.getCircularKaryoCoords = function() {
	var circularKaryoCoords = [];
	var total = 0;
	var spacer = this.conf.circular.karyoDistance;
	var karyo = this.data.karyo;
	var current = -spacer;
	$.each(karyo.chromosomes, function(key, value) {
		total += value.length + spacer;
	});
	for (var i = 0; i < karyo.order.length; i++) {
		var key = karyo.order[i];
		var value = karyo.chromosomes[key];
		var data = {
			"karyo": key,
			"startAngle": ((current + spacer) / total) * (2 * Math.PI),
		};
		current += value.length + spacer;
		data.endAngle = (current / total) * (2 * Math.PI);
		// not yet tested
		//		if (value.rc === true) {
		//			var startAngle = data.startAngle;
		//			var endAngle = data.endAngle;
		//			data.startAngle = endAngle;
		//			data.endAngle = startAngle;
		//		}
		circularKaryoCoords.push(data);
	}
	return circularKaryoCoords;
};
<|endoftext|>