<commit_before>/*
* Bitmap
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

// namespace:
this.createjs = this.createjs||{};

(function() {

/**
 * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
 * HTML element, or a string.
 *
 *
Example

 *      var bitmap = new createjs.Bitmap("imagePath.jpg");
 *
 * Notes:
 *

 *
When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
 *      will be displayed.

 *
Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
 *     the Bitmap can be cached.

 *
Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
 *     happens in all browsers except recent Firefox builds.

 *

 *
 * @class Bitmap
 * @extends DisplayObject
 * @constructor
 * @param {Image | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
 * display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
 * If it is a URI, a new Image object will be constructed and assigned to the .image property.
 **/
var Bitmap = function(imageOrUri) {
  this.initialize(imageOrUri);
}
var p = Bitmap.prototype = new createjs.DisplayObject();

// public properties:
	/**
	 * The image to render. This can be an Image, a Canvas, or a Video.
	 * @property image
	 * @type Image | HTMLCanvasElement | HTMLVideoElement
	 **/
	p.image = null;

	/**
	 * Whether or not the Bitmap should be draw to the canvas at whole pixel coordinates.
	 * @property snapToPixel
	 * @type Boolean
	 * @default true
	 **/
	p.snapToPixel = true;

	/**
	 * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
	 * @property sourceRect
	 * @type Rectangle
	 * @default null
	 */
	p.sourceRect = null;

	// constructor:

	/**
	 * @property DisplayObject_initialize
	 * @type Function
	 * @private
	 **/
	p.DisplayObject_initialize = p.initialize;

	/**
	 * Initialization method.
	 * @method initialize
	 * @param {Image | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
	 * display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
	 * If it is a URI, a new Image object will be constructed and assigned to the `.image` property.
	 * @protected
	 **/
	p.initialize = function(imageOrUri) {
		this.DisplayObject_initialize();
		if (typeof imageOrUri == "string") {
			this.image = new Image();
			this.image.src = imageOrUri;
		} else {
			this.image = imageOrUri;
		}
	}

// public methods:

	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || (this.image && (this.image.complete || this.image.getContext || this.image.readyState >= 2));
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	}

	/**
	 * @property DisplayObject_draw
	 * @type Function
	 * @private
	 **/
	p.DisplayObject_draw = p.draw;

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		var rect = this.sourceRect;
		if (rect) {
			ctx.drawImage(this.image, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
		} else {
			ctx.drawImage(this.image, 0, 0);
		}
		return true;
	}

	//Note, the doc sections below document using the specified APIs (from DisplayObject)  from
	//Bitmap. This is why they have no method implementations.

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method cache
	 **/

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method updateCache
	 **/

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method uncache
	 **/

	/**
	 * Returns a clone of the Bitmap instance.
	 * @method clone
	 * @return {Bitmap} a clone of the Bitmap instance.
	 **/
	p.clone = function() {
		var o = new Bitmap(this.image);
		if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
		this.cloneProps(o);
		return o;
	}

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Bitmap (name="+  this.name +")]";
	}

// private methods:

createjs.Bitmap = Bitmap;
}());<commit_msg>Added Bitmap.getBounds().<commit_after>/*
* Bitmap
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

// namespace:
this.createjs = this.createjs||{};

(function() {

/**
 * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
 * HTML element, or a string.
 *
 *
Example

 *      var bitmap = new createjs.Bitmap("imagePath.jpg");
 *
 * Notes:
 *

 *
When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
 *      will be displayed.

 *
Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
 *     the Bitmap can be cached.

 *
Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
 *     happens in all browsers except recent Firefox builds.

 *

 *
 * @class Bitmap
 * @extends DisplayObject
 * @constructor
 * @param {Image | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
 * display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
 * If it is a URI, a new Image object will be constructed and assigned to the .image property.
 **/
var Bitmap = function(imageOrUri) {
  this.initialize(imageOrUri);
}
var p = Bitmap.prototype = new createjs.DisplayObject();

// public properties:
	/**
	 * The image to render. This can be an Image, a Canvas, or a Video.
	 * @property image
	 * @type Image | HTMLCanvasElement | HTMLVideoElement
	 **/
	p.image = null;

	/**
	 * Whether or not the Bitmap should be draw to the canvas at whole pixel coordinates.
	 * @property snapToPixel
	 * @type Boolean
	 * @default true
	 **/
	p.snapToPixel = true;

	/**
	 * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
	 * @property sourceRect
	 * @type Rectangle
	 * @default null
	 */
	p.sourceRect = null;

	// constructor:

	/**
	 * @property DisplayObject_initialize
	 * @type Function
	 * @private
	 **/
	p.DisplayObject_initialize = p.initialize;

	/**
	 * Initialization method.
	 * @method initialize
	 * @param {Image | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
	 * display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
	 * If it is a URI, a new Image object will be constructed and assigned to the `.image` property.
	 * @protected
	 **/
	p.initialize = function(imageOrUri) {
		this.DisplayObject_initialize();
		if (typeof imageOrUri == "string") {
			this.image = new Image();
			this.image.src = imageOrUri;
		} else {
			this.image = imageOrUri;
		}
	}

// public methods:

	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || (this.image && (this.image.complete || this.image.getContext || this.image.readyState >= 2));
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	}

	/**
	 * @property DisplayObject_draw
	 * @type Function
	 * @private
	 **/
	p.DisplayObject_draw = p.draw;

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		var rect = this.sourceRect;
		if (rect) {
			ctx.drawImage(this.image, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
		} else {
			ctx.drawImage(this.image, 0, 0);
		}
		return true;
	}

	//Note, the doc sections below document using the specified APIs (from DisplayObject)  from
	//Bitmap. This is why they have no method implementations.

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method cache
	 **/

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method updateCache
	 **/

	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should not cache Bitmap instances as it can degrade performance.
	 *
	 * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method uncache
	 **/

	/**
	 * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of display object.
	 * This ignores transformations on the display object.
	 *
	 * @method getBounds
	 * @return {Rectangle} A Rectangle instance. Returns null if the image is not fully
	 * loaded.
	 **/
	p.getBounds = function() {
		var o = this.sourceRect || this.image;
		var hasContent = (this.image && (this.image.complete || this.image.getContext || this.image.readyState >= 2));
		return hasContent ? new createjs.Rectangle(0, 0, o.width, o.height) : null;
	};

	/**
	 * Returns a clone of the Bitmap instance.
	 * @method clone
	 * @return {Bitmap} a clone of the Bitmap instance.
	 **/
	p.clone = function() {
		var o = new Bitmap(this.image);
		if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
		this.cloneProps(o);
		return o;
	}

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Bitmap (name="+  this.name +")]";
	}

// private methods:

createjs.Bitmap = Bitmap;
}());<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const pj       = require('path').join;
const execSync = require('child_process').execSync;
const readdir  = require('fs').readdirSync;
const realpath = require('fs').realpathSync;

/* eslint-disable no-console */
/* global test, mkdir, rm */

const appMain     = 'nodeca';
const appMainDir  = pj(__dirname, '../');

const appsRepoOrg = 'nodeca';
const appsDir     = pj(appMainDir, 'nodeca_modules');

const argv        = process.argv;

const defaultApps = [
  'nodeca.core',
  'nodeca.users',
  'nodeca.forum',
  'nodeca.blogs'
];

// Install shelljs if not exists
function init(callback) {
  try {
    require('shelljs/global');
    callback();
    return;

  } catch (e) {}

  console.log('-- Pre-install dependencies');

  try {
    execSync('npm install shelljs', { stdio: 'inherit', cwd: appMainDir });
  } catch (e) {
    console.log(e);
    process.exit(1);
  }

  process.nextTick(() => {
    // That should be done in callback, to not fail in node 6
    require('shelljs/global');
    callback();
  });
}

// Create synlinks for all app repos if not exist
function relink() {
  console.log('-- Check npm links');

  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .filter(name => test('-f', pj(appsDir, name, 'package.json')))
      .forEach(name => {
        const pkgName = require(pj(appsDir, name, 'package.json')).name;
        const pkgPath = pj(appMainDir, 'node_modules', pkgName);

        if (test('-L', pkgPath)) {
          try {
            if (realpath(pkgPath) === pj(appsDir, name)) return;
          } catch (e) {}
        }

        rm('-rf', pkgPath); // Remove [folder|broken symlink] prior to create new

        console.log(`-- Add npm link for '${name}'`);
        execSync('npm link', { stdio: 'inherit', cwd: pj(appsDir, name) });
        execSync(`npm link ${pkgName}`, { stdio: 'inherit', cwd: appMainDir });
        console.log('');
      });
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
}

// `git pull` in all apps repos.
// if app not exists or .git subfolder missed - reclone, relink & npm install
//
function do_pull(readOnly) {
  let freshApps = []; // New apps, installed on this call

  if (!test('-d', appsDir)) mkdir(appsDir);

  // pull/clone default apps
  defaultApps.forEach(app => {
    const appDir = pj(appsDir, app);

    const repo   = readOnly ?
      `https://github.com/${appsRepoOrg}/${app}.git`
    :
      `git@github.com:${appsRepoOrg}/${app}.git`;

    if (test('-d', appDir) && !test('-d', pj(appDir, '.git'))) rm('-rf', appDir);

    if (!test('-d', appDir)) mkdir(appDir);

    try {
      if (test('-d', pj(appDir, '.git'))) {
        console.log(`-- Updating '${app}'`);
        execSync('git pull', { stdio: 'inherit', cwd: appDir });
      } else {
        console.log(`-- Cloning '${app}', ${repo}`);
        execSync(`git clone ${repo}`, { stdio: 'inherit', cwd: appsDir });
        freshApps.push(app);

        console.log(`-- Installing '${app}' dependencies`);
        execSync('npm install', { stdio: 'inherit', cwd: appDir });
      }
    } catch (e) { process.exit(1); }
  });

  // pull additional apps
  readdir(appsDir)
    .filter(name => defaultApps.indexOf(name) === -1)
    .filter(name => test('-d', pj(appsDir, name, '.git')))
    .forEach(name => {
      try {
        console.log(`-- Updating '${name}'`);
        execSync('git pull', { stdio: 'inherit', cwd: pj(appsDir, name) });
      } catch (e) { process.exit(1); }
    });

  // pull main app
  try {
    console.log(`-- Updating '${appMain}'`);
    execSync('git pull', { stdio: 'inherit', appMainDir });
  } catch (e) { process.exit(1); }

  relink();

  if (freshApps.length) {
    console.log(`-- Installing '${appMain}' dependencies`);
    execSync('npm install', { stdio: 'inherit', cwd: appMainDir });
  }
}

let task = {};

task.pull = function () {
  do_pull();
};

task['pull-ro'] = function () {
  do_pull(true);
};

task.push = function () {
  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .forEach(name => {
        console.log(`-- Pushing '${name}'`);
        execSync('git push', { stdio: 'inherit', cwd: pj(appsDir, name) });
      });
  } catch (e) {}

  try {
    console.log(`-- Pushing '${appMain}'`);
    execSync('git push', { stdio: 'inherit', cwd: appMainDir });
  } catch (e) { process.exit(1); }
};

task.status = function () {
  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .forEach(name => {
        console.log(`-- Status of '${name}'`);
        execSync('git status', { stdio: 'inherit', cwd: pj(appsDir, name) });
        console.log('');
      });
  } catch (e) {}

  try {
    console.log(`-- Status of '${appMain}'`);
    execSync('git status', { stdio: 'inherit', cwd: appMainDir });
    console.log('');
  } catch (e) {}
};

////////////////////////////////////////////////////////////////////////////////

let command = argv[2];

if (!task.hasOwnProperty(command)) {
  console.log(`Help: run [${Object.keys(task).join('|')}]`);
  return;
}

init(() => task[command]());
<commit_msg>Autoinstall dev-app dependencies if needed<commit_after>#!/usr/bin/env node

'use strict';

const pj       = require('path').join;
const execSync = require('child_process').execSync;
const readdir  = require('fs').readdirSync;
const realpath = require('fs').realpathSync;

/* eslint-disable no-console */
/* global test, mkdir, rm */

const appMain     = 'nodeca';
const appMainDir  = pj(__dirname, '../');

const appsRepoOrg = 'nodeca';
const appsDir     = pj(appMainDir, 'nodeca_modules');

const argv        = process.argv;

const defaultApps = [
  'nodeca.core',
  'nodeca.users',
  'nodeca.forum',
  'nodeca.blogs'
];

// Install shelljs if not exists
function init(callback) {
  try {
    require('shelljs/global');
    callback();
    return;

  } catch (e) {}

  console.log('-- Pre-install dependencies');

  try {
    execSync('npm install shelljs', { stdio: 'inherit', cwd: appMainDir });
  } catch (e) {
    console.log(e);
    process.exit(1);
  }

  process.nextTick(() => {
    // That should be done in callback, to not fail in node 6
    require('shelljs/global');
    callback();
  });
}

// Create synlinks for all app repos if not exist
function relink() {
  console.log('-- Check npm links');

  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .filter(name => test('-f', pj(appsDir, name, 'package.json')))
      .forEach(name => {
        const pkgName = require(pj(appsDir, name, 'package.json')).name;
        const pkgPath = pj(appMainDir, 'node_modules', pkgName);

        if (test('-L', pkgPath)) {
          try {
            if (realpath(pkgPath) === pj(appsDir, name)) return;
          } catch (e) {}
        }

        rm('-rf', pkgPath); // Remove [folder|broken symlink] prior to create new

        console.log(`-- Add npm link for '${name}'`);
        execSync('npm link', { stdio: 'inherit', cwd: pj(appsDir, name) });
        execSync(`npm link ${pkgName}`, { stdio: 'inherit', cwd: appMainDir });
        console.log('');
      });
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
}

// Install applications dependencies
function app_deps_install() {
  console.log('-- Check app dependencies');

  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .filter(name => test('-f', pj(appsDir, name, 'package.json')))
      .filter(name => !test('-d', pj(appsDir, name, 'node_modules')))
      .filter(name => require(pj(appsDir, name, 'package.json')).dependencies)
      .forEach(name => {
        console.log(`-- Install deps for '${name}'`);
        execSync('npm install', { stdio: 'inherit', cwd: pj(appsDir, name) });
      });
  } catch (e) {
    console.log(e);
    process.exit(1);
  }
}

// `git pull` in all apps repos.
// if app not exists or .git subfolder missed - reclone, relink & npm install
//
function do_pull(readOnly) {
  let freshApps = []; // New apps, installed on this call

  if (!test('-d', appsDir)) mkdir(appsDir);

  // pull/clone default apps
  defaultApps.forEach(app => {
    const appDir = pj(appsDir, app);

    const repo   = readOnly ?
      `https://github.com/${appsRepoOrg}/${app}.git`
    :
      `git@github.com:${appsRepoOrg}/${app}.git`;

    if (test('-d', appDir) && !test('-d', pj(appDir, '.git'))) rm('-rf', appDir);

    if (!test('-d', appDir)) mkdir(appDir);

    try {
      if (test('-d', pj(appDir, '.git'))) {
        console.log(`-- Updating '${app}'`);
        execSync('git pull', { stdio: 'inherit', cwd: appDir });
      } else {
        console.log(`-- Cloning '${app}', ${repo}`);
        execSync(`git clone ${repo}`, { stdio: 'inherit', cwd: appsDir });
        freshApps.push(app);

        console.log(`-- Installing '${app}' dependencies`);
        execSync('npm install', { stdio: 'inherit', cwd: appDir });
      }
    } catch (e) { process.exit(1); }
  });

  // pull additional apps
  readdir(appsDir)
    .filter(name => defaultApps.indexOf(name) === -1)
    .filter(name => test('-d', pj(appsDir, name, '.git')))
    .forEach(name => {
      try {
        console.log(`-- Updating '${name}'`);
        execSync('git pull', { stdio: 'inherit', cwd: pj(appsDir, name) });
      } catch (e) { process.exit(1); }
    });

  // pull main app
  try {
    console.log(`-- Updating '${appMain}'`);
    execSync('git pull', { stdio: 'inherit', appMainDir });
  } catch (e) { process.exit(1); }

  app_deps_install();
  relink();

  if (freshApps.length) {
    console.log(`-- Installing '${appMain}' dependencies`);
    execSync('npm install', { stdio: 'inherit', cwd: appMainDir });
  }
}

let task = {};

task.pull = function () {
  do_pull();
};

task['pull-ro'] = function () {
  do_pull(true);
};

task.push = function () {
  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .forEach(name => {
        console.log(`-- Pushing '${name}'`);
        execSync('git push', { stdio: 'inherit', cwd: pj(appsDir, name) });
      });
  } catch (e) {}

  try {
    console.log(`-- Pushing '${appMain}'`);
    execSync('git push', { stdio: 'inherit', cwd: appMainDir });
  } catch (e) { process.exit(1); }
};

task.status = function () {
  try {
    readdir(appsDir)
      .filter(name => test('-d', pj(appsDir, name, '.git')))
      .forEach(name => {
        console.log(`-- Status of '${name}'`);
        execSync('git status', { stdio: 'inherit', cwd: pj(appsDir, name) });
        console.log('');
      });
  } catch (e) {}

  try {
    console.log(`-- Status of '${appMain}'`);
    execSync('git status', { stdio: 'inherit', cwd: appMainDir });
    console.log('');
  } catch (e) {}
};

////////////////////////////////////////////////////////////////////////////////

let command = argv[2];

if (!task.hasOwnProperty(command)) {
  console.log(`Help: run [${Object.keys(task).join('|')}]`);
  return;
}

init(() => task[command]());
<|endoftext|>


<commit_before><commit_msg>Added test function to illustrate autocomplete issue in brackets<commit_after><|endoftext|>


<commit_before>import Collection from '../collection.js';
import Events from '../events.js';
import defineGetterSetter from '../utils/get-set.js';
import {lerp} from '../utils/math.js';
import _ from '../utils/underscore.js';

import Path from '../path.js';
import Anchor from '../anchor.js';
import Vector from '../vector.js';
import Rectangle from '../shapes/rectangle.js';
import Texture from './texture.js';

/**
 * @name Two.ImageSequence
 * @class
 * @extends Two.Rectangle
 * @param {String|String[]|Two.Texture|Two.Texture[]} paths - A list of URLs or {@link Two.Texture}s.
 * @param {Number} [ox=0] - The initial `x` position of the Two.ImageSequence.
 * @param {Number} [oy=0] - The initial `y` position of the Two.ImageSequence.
 * @param {Number} [frameRate=30] - The frame rate at which the images should playback at.
 * @description A convenient package to display still or animated images organized as a series of still images.
 */
function ImageSequence(paths, ox, oy, frameRate) {

  // Not using default constructor of Rectangle due to odd `beginning` / `ending` behavior.
  // See: https://github.com/jonobr1/two.js/issues/383
  Path.call(this, [
    new Anchor(),
    new Anchor(),
    new Anchor(),
    new Anchor()
  ], true);

  this._renderer.flagTextures = ImageSequence.FlagTextures.bind(this);
  this._renderer.bindTextures = ImageSequence.BindTextures.bind(this);
  this._renderer.unbindTextures = ImageSequence.UnbindTextures.bind(this);

  this.noStroke();
  this.noFill();

  /**
   * @name Two.ImageSequence#textures
   * @property {Two.Texture[]} - A list of textures to be used as frames for animating the {@link Two.ImageSequence}.
   */
  if (Array.isArray(paths)) {
    this.textures = paths.map(ImageSequence.GenerateTexture.bind(this));
  } else {
    // If just a single path convert into a single Two.Texture
    this.textures = [ImageSequence.GenerateTexture(paths)];
  }

  this.origin = new Vector();

  this._update();
  this.translation.set(ox || 0, oy || 0);

  /**
   * @name Two.ImageSequence#frameRate
   * @property {Number} - The number of frames to animate against per second.
   */
  if (typeof frameRate === 'number') {
    this.frameRate = frameRate;
  } else {
    this.frameRate = ImageSequence.DefaultFrameRate;
  }

  /**
   * @name Two.ImageSequence#index
   * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.
   */
  this.index = 0;

}

_.extend(ImageSequence, {

  /**
   * @name Two.ImageSequence.Properties
   * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.
   */
  Properties: [
    'frameRate',
    'index'
  ],

  /**
   * @name Two.ImageSequence.DefaultFrameRate
   * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.
   */
  DefaultFrameRate: 30,

  /**
   * @name Two.ImageSequence.FlagTextures
   * @function
   * @description Cached method to let renderers know textures have been updated on a {@link Two.ImageSequence}.
   */
  FlagTextures: function() {
    this._flagTextures = true;
  },

  /**
   * @name Two.ImageSequence.BindTextures
   * @function
   * @description Cached method to let {@link Two.ImageSequence} know textures have been added to the instance.
   */
  BindTextures: function(items) {

    var i = items.length;
    while (i--) {
      items[i].bind(Events.Types.change, this._renderer.flagTextures);
    }

    this._renderer.flagTextures();

  },

  /**
   * @name Two.ImageSequence.UnbindVertices
   * @function
   * @description Cached method to let {@link Two.ImageSequence} know textures have been removed from the instance.
   */
  UnbindTextures: function(items) {

    var i = items.length;
    while (i--) {
      items[i].unbind(Events.Types.change, this._renderer.flagTextures);
    }

    this._renderer.flagTextures();

  },

  /**
   * @name Two.ImageSequence.MakeObservable
   * @function
   * @param {Object} object - The object to make observable.
   * @description Convenience function to apply observable qualities of a {@link Two.ImageSequence} to any object. Handy if you'd like to extend or inherit the {@link Two.ImageSequence} class on a custom class.
   */
  MakeObservable: function(obj) {

    Rectangle.MakeObservable(obj);
    _.each(ImageSequence.Properties, defineGetterSetter, obj);

    Object.defineProperty(obj, 'textures', {

      enumerable: true,

      get: function() {
        return this._textures;
      },

      set: function(textures) {

        var bindTextures = this._renderer.bindTextures;
        var unbindTextures = this._renderer.unbindTextures;

        // Remove previous listeners
        if (this._textures) {
          this._textures
            .unbind(Events.Types.insert, bindTextures)
            .unbind(Events.Types.remove, unbindTextures);
        }

        // Create new Collection with copy of vertices
        this._textures = new Collection((textures || []).slice(0));

        // Listen for Collection changes and bind / unbind
        this._textures
          .bind(Events.Types.insert, bindTextures)
          .bind(Events.Types.remove, unbindTextures);

        // Bind Initial Textures
        bindTextures(this._textures);

      }

    });

  },

  /**
   * @name Two.ImageSequence.GenerateTexture
   * @property {Function} - Shorthand function to prepare source image material into readable format by {@link Two.ImageSequence}.
   * @param {String|Two.Texture} textureOrString - The texture or string to create a {@link Two.Texture} from.
   * @description Function used internally by {@link Two.ImageSequence} to parse arguments and return {@link Two.Texture}s.
   * @returns {Two.Texture}
   */
  GenerateTexture: function(obj) {
    if (obj instanceof Texture) {
      return obj;
    } else if (typeof obj === 'string') {
      return new Texture(obj);
    }
  }

});

_.extend(ImageSequence.prototype, Rectangle.prototype, {

  constructor: ImageSequence,

  /**
   * @name Two.ImageSequence#_flagTextures
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.
   */
  _flagTextures: false,

  /**
   * @name Two.ImageSequence#_flagFrameRate
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.
   */
  _flagFrameRate: false,

  /**
   * @name Two.ImageSequence#_flagIndex
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.
   */
  _flagIndex: false,

  // Private variables

  /**
   * @name Two.ImageSequence#_amount
   * @private
   * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.
   */
  _amount: 1,

  /**
   * @name Two.ImageSequence#_duration
   * @private
   * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.
   */
  _duration: 0,

  /**
   * @name Two.ImageSequence#_index
   * @private
   * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.
   */
  _index: 0,

  /**
   * @name Two.ImageSequence#_startTime
   * @private
   * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.
   */
  _startTime: 0,

  /**
   * @name Two.ImageSequence#_playing
   * @private
   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.
   */
  _playing: false,

  /**
   * @name Two.ImageSequence#_firstFrame
   * @private
   * @property {Number} - The frame the {@link Two.ImageSequence} should start with.
   */
  _firstFrame: 0,

  /**
   * @name Two.ImageSequence#_lastFrame
   * @private
   * @property {Number} - The frame the {@link Two.ImageSequence} should end with.
   */
  _lastFrame: 0,

  /**
   * @name Two.ImageSequence#_playing
   * @private
   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.
   */
  _loop: true,

  // Exposed through getter-setter

  /**
   * @name Two.ImageSequence#_textures
   * @private
   * @see {@link Two.ImageSequence#textures}
   */
  _textures: null,

  /**
   * @name Two.ImageSequence#_frameRate
   * @private
   * @see {@link Two.ImageSequence#frameRate}
   */
  _frameRate: 0,

  /**
   * @name Two.ImageSequence#_origin
   * @private
   * @see {@link Two.ImageSequence#origin}
   */
  _origin: null,

  /**
   * @name Two.ImageSequence#play
   * @function
   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.
   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.
   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.
   * @description Initiate animation playback of a {@link Two.ImageSequence}.
   */
  play: function(firstFrame, lastFrame, onLastFrame) {

    this._playing = true;
    this._firstFrame = 0;
    this._lastFrame = this.amount - 1;
    this._startTime = _.performance.now();

    if (typeof firstFrame === 'number') {
      this._firstFrame = firstFrame;
    }
    if (typeof lastFrame === 'number') {
      this._lastFrame = lastFrame;
    }
    if (typeof onLastFrame === 'function') {
      this._onLastFrame = onLastFrame;
    } else {
      delete this._onLastFrame;
    }

    if (this._index !== this._firstFrame) {
      this._startTime -= 1000 * Math.abs(this._index - this._firstFrame)
        / this._frameRate;
    }

    return this;

  },

  /**
   * @name Two.ImageSequence#pause
   * @function
   * @description Halt animation playback of a {@link Two.ImageSequence}.
   */
  pause: function() {

    this._playing = false;
    return this;

  },

  /**
   * @name Two.ImageSequence#stop
   * @function
   * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.
   */
  stop: function() {

    this._playing = false;
    this._index = this._firstFrame;

    return this;

  },

  /**
   * @name Two.ImageSequence#clone
   * @function
   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.
   * @returns {Two.ImageSequence}
   * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.
   */
  clone: function(parent) {

    var clone = new ImageSequence(this.textures, this.translation.x,
      this.translation.y, this.frameRate);

    clone._loop = this._loop;

    if (this._playing) {
      clone.play();
    }

    if (parent) {
      parent.add(clone);
    }

    return clone;

  },

  /**
   * @name Two.ImageSequence#toObject
   * @function
   * @returns {Object}
   * @description Return a JSON compatible plain object that represents the path.
   */
  toObject: function() {
    var object = Rectangle.prototype.toObject.call(this);
    object.textures = this.textures.map(function(texture) {
      return texture.toObject();
    });
    object.frameRate = this.frameRate;
    object.index = this.index;
    object._firstFrame = this._firstFrame;
    object._lastFrame = this._lastFrame;
    object._loop = this._loop;
    return object;
  },

  /**
   * @name Two.ImageSequence#_update
   * @function
   * @private
   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.
   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.
   * @nota-bene Try not to call this method more than once a frame.
   */
  _update: function() {

    var effects = this._textures;
    var width, height, elapsed, amount, duration, texture;
    var index, frames;

    if (this._flagTextures) {
      this._amount = effects.length;
    }

    if (this._flagFrameRate) {
      this._duration = 1000 * this._amount / this._frameRate;
    }

    if (this._playing && this._frameRate > 0) {

      amount = this._amount;

      if (_.isNaN(this._lastFrame)) {
        this._lastFrame = amount - 1;
      }

      // TODO: Offload perf logic to instance of `Two`.
      elapsed = _.performance.now() - this._startTime;
      frames = this._lastFrame + 1;
      duration = 1000 * (frames - this._firstFrame) / this._frameRate;

      if (this._loop) {
        elapsed = elapsed % duration;
      } else {
        elapsed = Math.min(elapsed, duration);
      }

      index = lerp(this._firstFrame, frames, elapsed / duration);
      index = Math.floor(index);

      if (index !== this._index) {

        this._index = index;
        texture = effects[this._index];

        if (texture.loaded) {

          width = texture.image.width;
          height = texture.image.height;

          if (this.width !== width) {
            this.width = width;
          }
          if (this.height !== height) {
            this.height = height;
          }

          this.fill = texture;

          if (index >= this._lastFrame - 1 && this._onLastFrame) {
            this._onLastFrame();  // Shortcut for chainable sprite animations
          }

        }

      }

    } else if (this._flagIndex || !(this.fill instanceof Texture)) {

      texture = effects[this._index];

      if (texture.loaded) {

        width = texture.image.width;
        height = texture.image.height;

        if (this.width !== width) {
          this.width = width;
        }
        if (this.height !== height) {
          this.height = height;
        }

      }

      this.fill = texture;

    }

    Rectangle.prototype._update.call(this);

    return this;

  },

  /**
   * @name Two.ImageSequence#flagReset
   * @function
   * @private
   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.
   */
  flagReset: function() {

    this._flagTextures = this._flagFrameRate = false;
    Rectangle.prototype.flagReset.call(this);

    return this;

  }

});

ImageSequence.MakeObservable(ImageSequence.prototype);

export default ImageSequence;
<commit_msg>Converted Two.ImageSequence to ES6 Class<commit_after>import { Collection } from '../collection.js';
import { Events } from '../events.js';
import { lerp } from '../utils/math.js';
import { _ } from '../utils/underscore.js';

import { Vector } from '../vector.js';
import { Rectangle } from '../shapes/rectangle.js';
import { Texture } from './texture.js';

/**
 * @name Two.ImageSequence
 * @class
 * @extends Two.Rectangle
 * @param {String|String[]|Two.Texture|Two.Texture[]} paths - A list of URLs or {@link Two.Texture}s.
 * @param {Number} [ox=0] - The initial `x` position of the Two.ImageSequence.
 * @param {Number} [oy=0] - The initial `y` position of the Two.ImageSequence.
 * @param {Number} [frameRate=30] - The frame rate at which the images should playback at.
 * @description A convenient package to display still or animated images organized as a series of still images.
 */
export class ImageSequence extends Rectangle {

  /**
   * @name Two.ImageSequence#_flagTextures
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#textures} need updating.
   */
  _flagTextures = false;

  /**
   * @name Two.ImageSequence#_flagFrameRate
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#frameRate} needs updating.
   */
  _flagFrameRate = false;

  /**
   * @name Two.ImageSequence#_flagIndex
   * @private
   * @property {Boolean} - Determines whether the {@link Two.ImageSequence#index} needs updating.
   */
  _flagIndex = false;

  // Private variables

  /**
   * @name Two.ImageSequence#_amount
   * @private
   * @property {Number} - Number of frames for a given {@link Two.ImageSequence}.
   */
  _amount = 1;

  /**
   * @name Two.ImageSequence#_duration
   * @private
   * @property {Number} - Number of milliseconds a {@link Two.ImageSequence}.
   */
  _duration = 0;

  /**
   * @name Two.ImageSequence#_index
   * @private
   * @property {Number} - The current frame the {@link Two.ImageSequence} is currently displaying.
   */
  _index = 0;

  /**
   * @name Two.ImageSequence#_startTime
   * @private
   * @property {Milliseconds} - Epoch time in milliseconds of when the {@link Two.ImageSequence} started.
   */
  _startTime = 0;

  /**
   * @name Two.ImageSequence#_playing
   * @private
   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} is animating or not.
   */
  _playing = false;

  /**
   * @name Two.ImageSequence#_firstFrame
   * @private
   * @property {Number} - The frame the {@link Two.ImageSequence} should start with.
   */
  _firstFrame = 0;

  /**
   * @name Two.ImageSequence#_lastFrame
   * @private
   * @property {Number} - The frame the {@link Two.ImageSequence} should end with.
   */
  _lastFrame = 0;

  /**
   * @name Two.ImageSequence#_playing
   * @private
   * @property {Boolean} - Dictates whether the {@link Two.ImageSequence} should loop or not.
   */
  _loop = true;

  // Exposed through getter-setter

  /**
   * @name Two.ImageSequence#_textures
   * @private
   * @see {@link Two.ImageSequence#textures}
   */
  _textures = null;

  /**
   * @name Two.ImageSequence#_frameRate
   * @private
   * @see {@link Two.ImageSequence#frameRate}
   */
  _frameRate = 0;

  /**
   * @name Two.ImageSequence#_origin
   * @private
   * @see {@link Two.ImageSequence#origin}
   */
  _origin = null;

  constructor(paths, ox, oy, frameRate) {

    super(ox, oy, 0, 0);

    for (let prop in proto) {
      Object.defineProperty(this, prop, proto[prop]);
    }

    this._renderer.flagTextures = FlagTextures.bind(this);
    this._renderer.bindTextures = BindTextures.bind(this);
    this._renderer.unbindTextures = UnbindTextures.bind(this);

    this.noStroke();
    this.noFill();

    /**
     * @name Two.ImageSequence#textures
     * @property {Two.Texture[]} - A list of textures to be used as frames for animating the {@link Two.ImageSequence}.
     */
    if (Array.isArray(paths)) {
      this.textures = paths.map(GenerateTexture.bind(this));
    } else {
      // If just a single path convert into a single Two.Texture
      this.textures = [GenerateTexture(paths)];
    }

    this.origin = new Vector();

    this._update();

    /**
     * @name Two.ImageSequence#frameRate
     * @property {Number} - The number of frames to animate against per second.
     */
    if (typeof frameRate === 'number') {
      this.frameRate = frameRate;
    } else {
      this.frameRate = ImageSequence.DefaultFrameRate;
    }

    /**
     * @name Two.ImageSequence#index
     * @property {Number} - The index of the current tile of the sprite to display. Defaults to `0`.
     */
    this.index = 0;

  }

  /**
   * @name Two.ImageSequence.Properties
   * @property {String[]} - A list of properties that are on every {@link Two.ImageSequence}.
   */
  static Properties = [
    'textures',
    'frameRate',
    'index'
  ];

  /**
   * @name Two.ImageSequence.DefaultFrameRate
   * @property The default frame rate that {@link Two.ImageSequence#frameRate} is set to when instantiated.
   */
  static DefaultFrameRate = 30;

  /**
   * @name Two.ImageSequence#play
   * @function
   * @param {Number} [firstFrame=0] - The index of the frame to start the animation with.
   * @param {Number} [lastFrame] - The index of the frame to end the animation with. Defaults to the last item in the {@link Two.ImageSequence#textures}.
   * @param {Function} [onLastFrame] - Optional callback function to be triggered after playing the last frame. This fires multiple times when the image sequence is looped.
   * @description Initiate animation playback of a {@link Two.ImageSequence}.
   */
  play(firstFrame, lastFrame, onLastFrame) {

    this._playing = true;
    this._firstFrame = 0;
    this._lastFrame = this.amount - 1;
    this._startTime = _.performance.now();

    if (typeof firstFrame === 'number') {
      this._firstFrame = firstFrame;
    }
    if (typeof lastFrame === 'number') {
      this._lastFrame = lastFrame;
    }
    if (typeof onLastFrame === 'function') {
      this._onLastFrame = onLastFrame;
    } else {
      delete this._onLastFrame;
    }

    if (this._index !== this._firstFrame) {
      this._startTime -= 1000 * Math.abs(this._index - this._firstFrame)
        / this._frameRate;
    }

    return this;

  }

  /**
   * @name Two.ImageSequence#pause
   * @function
   * @description Halt animation playback of a {@link Two.ImageSequence}.
   */
  pause() {

    this._playing = false;
    return this;

  }

  /**
   * @name Two.ImageSequence#stop
   * @function
   * @description Halt animation playback of a {@link Two.ImageSequence} and set the current frame back to the first frame.
   */
  stop() {

    this._playing = false;
    this._index = this._firstFrame;

    return this;

  }

  /**
   * @name Two.ImageSequence#clone
   * @function
   * @param {Two.Group} [parent] - The parent group or scene to add the clone to.
   * @returns {Two.ImageSequence}
   * @description Create a new instance of {@link Two.ImageSequence} with the same properties of the current image sequence.
   */
  clone(parent) {

    const clone = new ImageSequence(this.textures, this.translation.x,
      this.translation.y, this.frameRate);

    clone._loop = this._loop;

    if (this._playing) {
      clone.play();
    }

    if (parent) {
      parent.add(clone);
    }

    return clone;

  }

  /**
   * @name Two.ImageSequence#toObject
   * @function
   * @returns {Object}
   * @description Return a JSON compatible plain object that represents the path.
   */
  toObject() {
    const object = super.toObject.call(this);
    object.textures = this.textures.map(function(texture) {
      return texture.toObject();
    });
    object.frameRate = this.frameRate;
    object.index = this.index;
    object._firstFrame = this._firstFrame;
    object._lastFrame = this._lastFrame;
    object._loop = this._loop;
    return object;
  }

  /**
   * @name Two.ImageSequence#_update
   * @function
   * @private
   * @param {Boolean} [bubbles=false] - Force the parent to `_update` as well.
   * @description This is called before rendering happens by the renderer. This applies all changes necessary so that rendering is up-to-date but not updated more than it needs to be.
   * @nota-bene Try not to call this method more than once a frame.
   */
  _update() {

    const effects = this._textures;
    let width, height, elapsed, amount, duration, texture;
    let index, frames;

    if (this._flagTextures) {
      this._amount = effects.length;
    }

    if (this._flagFrameRate) {
      this._duration = 1000 * this._amount / this._frameRate;
    }

    if (this._playing && this._frameRate > 0) {

      amount = this._amount;

      if (_.isNaN(this._lastFrame)) {
        this._lastFrame = amount - 1;
      }

      // TODO: Offload perf logic to instance of `Two`.
      elapsed = _.performance.now() - this._startTime;
      frames = this._lastFrame + 1;
      duration = 1000 * (frames - this._firstFrame) / this._frameRate;

      if (this._loop) {
        elapsed = elapsed % duration;
      } else {
        elapsed = Math.min(elapsed, duration);
      }

      index = lerp(this._firstFrame, frames, elapsed / duration);
      index = Math.floor(index);

      if (index !== this._index) {

        this._index = index;
        texture = effects[this._index];

        if (texture.loaded) {

          width = texture.image.width;
          height = texture.image.height;

          if (this.width !== width) {
            this.width = width;
          }
          if (this.height !== height) {
            this.height = height;
          }

          this.fill = texture;

          if (index >= this._lastFrame - 1 && this._onLastFrame) {
            this._onLastFrame();  // Shortcut for chainable sprite animations
          }

        }

      }

    } else if (this._flagIndex || !(this.fill instanceof Texture)) {

      texture = effects[this._index];

      if (texture.loaded) {

        width = texture.image.width;
        height = texture.image.height;

        if (this.width !== width) {
          this.width = width;
        }
        if (this.height !== height) {
          this.height = height;
        }

      }

      this.fill = texture;

    }

    super._update.call(this);

    return this;

  }

  /**
   * @name Two.ImageSequence#flagReset
   * @function
   * @private
   * @description Called internally to reset all flags. Ensures that only properties that change are updated before being sent to the renderer.
   */
  flagReset() {

    this._flagTextures = this._flagFrameRate = false;
    super.flagReset.call(this);

    return this;

  }

}

const proto = {
  frameRate: {
    enumerable: true,
    get: function() {
      return this._frameRate;
    },
    set: function(v) {
      this._frameRate = v;
      this._flagFrameRate = true;
    }
  },
  index: {
    enumerable: true,
    get: function() {
      return this._index;
    },
    set: function(v) {
      this._index = v;
      this._flagIndex = true;
    }
  },
  textures: {
    enumerable: true,
    get: function() {
      return this._textures;
    },
    set: function(textures) {

      const bindTextures = this._renderer.bindTextures;
      const unbindTextures = this._renderer.unbindTextures;

      // Remove previous listeners
      if (this._textures) {
        this._textures
          .unbind(Events.Types.insert, bindTextures)
          .unbind(Events.Types.remove, unbindTextures);
      }

      // Create new Collection with copy of vertices
      this._textures = new Collection((textures || []).slice(0));

      // Listen for Collection changes and bind / unbind
      this._textures
        .bind(Events.Types.insert, bindTextures)
        .bind(Events.Types.remove, unbindTextures);

      // Bind Initial Textures
      bindTextures(this._textures);

    }
  }
};

/**
 * @name FlagTextures
 * @private
 * @function
 * @description Cached method to let renderers know textures have been updated on a {@link Two.ImageSequence}.
 */
function FlagTextures() {
  this._flagTextures = true;
}

/**
 * @name BindTextures
 * @private
 * @function
 * @description Cached method to let {@link Two.ImageSequence} know textures have been added to the instance.
 */
function BindTextures(items) {

  let i = items.length;
  while (i--) {
    items[i].bind(Events.Types.change, this._renderer.flagTextures);
  }

  this._renderer.flagTextures();

}

/**
 * @name UnbindTextures
 * @private
 * @function
 * @description Cached method to let {@link Two.ImageSequence} know textures have been removed from the instance.
 */
function UnbindTextures(items) {

  let i = items.length;
  while (i--) {
    items[i].unbind(Events.Types.change, this._renderer.flagTextures);
  }

  this._renderer.flagTextures();

}

/**
 * @name GenerateTexture
 * @private
 * @property {Function} - Shorthand function to prepare source image material into readable format by {@link Two.ImageSequence}.
 * @param {String|Two.Texture} textureOrString - The texture or string to create a {@link Two.Texture} from.
 * @description Function used internally by {@link Two.ImageSequence} to parse arguments and return {@link Two.Texture}s.
 * @returns {Two.Texture}
 */
function GenerateTexture(obj) {
  if (obj instanceof Texture) {
    return obj;
  } else if (typeof obj === 'string') {
    return new Texture(obj);
  }
}
<|endoftext|>


<commit_before>import { ATTR_KEY } from '../constants';
import { isSameNodeType, isNamedNode } from './index';
import { buildComponentFromVNode } from './component';
import { createNode, setAccessor } from '../dom/index';
import { unmountComponent } from './component';
import options from '../options';
import { removeNode } from '../dom';

/** Queue of components that have been mounted and are awaiting componentDidMount */
export const mounts = [];

/** Diff recursion count, used to track the end of the diff cycle. */
export let diffLevel = 0;

/** Global flag indicating if the diff is currently within an SVG */
let isSvgMode = false;

/** Global flag indicating if the diff is performing hydration */
let hydrating = false;

/** Invoke queued componentDidMount lifecycle methods */
export function flushMounts() {
	let c;
	while ((c=mounts.pop())) {
		if (options.afterMount) options.afterMount(c);
		if (c.componentDidMount) c.componentDidMount();
	}
}

/** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
 *	@param {Element} [dom=null]		A DOM node to mutate into the shape of the `vnode`
 *	@param {VNode} vnode			A VNode (with descendants forming a tree) representing the desired DOM structure
 *	@returns {Element} dom			The created/mutated element
 *	@private
 */
export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
	// diffLevel having been 0 here indicates initial entry into the diff (not a subdiff)
	if (!diffLevel++) {
		// when first starting the diff, check if we're diffing an SVG or within an SVG
		isSvgMode = parent!=null && parent.ownerSVGElement!==undefined;

		// hydration is indicated by the existing element to be diffed not having a prop cache
		hydrating = dom!=null && !(ATTR_KEY in dom);
	}

	let ret = idiff(dom, vnode, context, mountAll, componentRoot);

	// append the element if its a new parent
	if (parent && ret.parentNode!==parent) parent.appendChild(ret);

	// diffLevel being reduced to 0 means we're exiting the diff
	if (!--diffLevel) {
		hydrating = false;
		// invoke queued componentDidMount lifecycle methods
		if (!componentRoot) flushMounts();
	}

	return ret;
}

/** Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing. */
function idiff(dom, vnode, context, mountAll, componentRoot) {
	let out = dom,
		prevSvgMode = isSvgMode;

	// empty values (null, undefined, booleans) render as empty Text nodes
	if (vnode==null || typeof vnode==='boolean') vnode = '';

	// Fast case: Strings & Numbers create/update Text nodes.
	if (typeof vnode==='string' || typeof vnode==='number') {

		// update if it's already a Text node:
		if (dom && dom.splitText!==undefined && dom.parentNode && (!dom._component || componentRoot)) {
			if (dom.nodeValue!=vnode) {
				dom.nodeValue = vnode;
			}
		}
		else {
			// it wasn't a Text node: replace it with one and recycle the old Element
			out = document.createTextNode(vnode);
			if (dom) {
				if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
				recollectNodeTree(dom, true);
			}
		}

		out[ATTR_KEY] = true;

		return out;
	}

	// If the VNode represents a Component, perform a component diff:
	let vnodeName = vnode.nodeName;
	if (typeof vnodeName==='function') {
		return buildComponentFromVNode(dom, vnode, context, mountAll);
	}

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvgMode = vnodeName==='svg' ? true : vnodeName==='foreignObject' ? false : isSvgMode;

	// If there's no existing element or it's the wrong type, create a new one:
	vnodeName = String(vnodeName);
	if (!dom || !isNamedNode(dom, vnodeName)) {
		out = createNode(vnodeName, isSvgMode);

		if (dom) {
			// move children into the replacement node
			while (dom.firstChild) out.appendChild(dom.firstChild);

			// if the previous Element was mounted into the DOM, replace it inline
			if (dom.parentNode) dom.parentNode.replaceChild(out, dom);

			// recycle the old element (skips non-Element node types)
			recollectNodeTree(dom, true);
		}
	}

	let fc = out.firstChild,
		props = out[ATTR_KEY],
		vchildren = vnode.children;

	if (props==null) {
		props = out[ATTR_KEY] = {};
		for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value;
	}

	// Optimization: fast-path for elements containing a single TextNode:
	if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
		if (fc.nodeValue!=vchildren[0]) {
			fc.nodeValue = vchildren[0];
		}
	}
	// otherwise, if there are existing or new children, diff them:
	else if (vchildren && vchildren.length || fc!=null) {
		innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null);
	}

	// Apply attributes/props from VNode to the DOM Element:
	diffAttributes(out, vnode.attributes, props);

	// restore previous SVG mode: (in case we're exiting an SVG namespace)
	isSvgMode = prevSvgMode;

	return out;
}

/** Apply child and attribute changes between a VNode and a DOM Node to the DOM.
 *	@param {Element} dom			Element whose children should be compared & mutated
 *	@param {Array} vchildren		Array of VNodes to compare to `dom.childNodes`
 *	@param {Object} context			Implicitly descendant context object (from most recent `getChildContext()`)
 *	@param {Boolean} mountAll
 *	@param {Boolean} isHydrating	If `true`, consumes externally created elements similar to hydration
 */
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
	let originalChildren = dom.childNodes,
		children = [],
		keyed = {},
		keyedLen = 0,
		min = 0,
		len = originalChildren.length,
		childrenLen = 0,
		vlen = vchildren ? vchildren.length : 0,
		j, c, vchild, child;

	// Build up a map of keyed children and an Array of unkeyed children:
	if (len!==0) {
		for (let i=0; i<len; i++) {
			let child = originalChildren[i],
				props = child[ATTR_KEY],
				key = vlen && props ? child._component ? child._component.__key : props.key : null;
			if (key!=null) {
				keyedLen++;
				keyed[key] = child;
			}
			else if (props || (child.splitText!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) {
				children[childrenLen++] = child;
			}
		}
	}

	if (vlen!==0) {
		for (let i=0; i<vlen; i++) {
			vchild = vchildren[i];
			child = null;

			// attempt to find a node based on key matching
			let key = vchild.key;
			if (key!=null) {
				if (keyedLen && keyed[key]!==undefined) {
					child = keyed[key];
					keyed[key] = undefined;
					keyedLen--;
				}
			}
			// attempt to pluck a node of the same type from the existing children
			else if (!child && min<childrenLen) {
				for (j=min; j<childrenLen; j++) {
					if (children[j]!==undefined && isSameNodeType(c = children[j], vchild, isHydrating)) {
						child = c;
						children[j] = undefined;
						if (j===childrenLen-1) childrenLen--;
						if (j===min) min++;
						break;
					}
				}
			}

			// morph the matched/found/created DOM child to match vchild (deep)
			child = idiff(child, vchild, context, mountAll);

			if (child && child!==dom) {
				if (i>len) {
					dom.appendChild(child);
				}
				else if (child!==originalChildren[i]) {
					if (child===originalChildren[i+1]) {
						removeNode(originalChildren[i]);
					}
					else {
						dom.insertBefore(child, originalChildren[i] || null);
					}
				}
			}
		}
	}

	// remove unused keyed children:
	if (keyedLen) {
		for (let i in keyed) if (keyed[i]!==undefined) recollectNodeTree(keyed[i], false);
	}

	// remove orphaned unkeyed children:
	while (min<=childrenLen) {
		if ((child = children[childrenLen--])!==undefined) recollectNodeTree(child, false);
	}
}

/** Recursively recycle (or just unmount) a node an its descendants.
 *	@param {Node} node						DOM node to start unmount/removal from
 *	@param {Boolean} [unmountOnly=false]	If `true`, only triggers unmount lifecycle, skips removal
 */
export function recollectNodeTree(node, unmountOnly) {
	let component = node._component;
	if (component) {
		// if node is owned by a Component, unmount that component (ends up recursing back here)
		unmountComponent(component);
	}
	else {
		// If the node's VNode had a ref function, invoke it with null here.
		// (this is part of the React spec, and smart for unsetting references)
		if (node[ATTR_KEY]!=null && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);

		if (unmountOnly===false || node[ATTR_KEY]==null) {
			removeNode(node);
		}

		removeChildren(node);
	}
}

/** Recollect/unmount all children.
 *	- we use .lastChild here because it causes less reflow than .firstChild
 *	- it's also cheaper than accessing the .childNodes Live NodeList
 */
export function removeChildren(node) {
	node = node.lastChild;
	while (node) {
		let next = node.previousSibling;
		recollectNodeTree(node, true);
		node = next;
	}
}

/** Apply differences in attributes from a VNode to the given DOM Element.
 *	@param {Element} dom		Element with attributes to diff `attrs` against
 *	@param {Object} attrs		The desired end-state key-value attribute pairs
 *	@param {Object} old			Current/previous attributes (from previous VNode or element's prop cache)
 */
function diffAttributes(dom, attrs, old) {
	let name;

	// remove attributes no longer present on the vnode by setting them to undefined
	for (name in old) {
		if (!(attrs && attrs[name]!=null) && old[name]!=null) {
			setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode);
		}
	}

	// add new & update changed attributes
	for (name in attrs) {
		if (name!=='children' && name!=='innerHTML' && (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name]))) {
			setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
		}
	}
}
<commit_msg>Improvement to @windyGex's awesome work from #717. This entirely removes the cases where preact would erroneously invoke appendChild() when it didn't need to.  The diff no longer depends on DOM length values 🎉  Big thanks to @e1ectronic for pointing this out and providing a great repro (http://jsfiddle.net/o402afwp/).<commit_after>import { ATTR_KEY } from '../constants';
import { isSameNodeType, isNamedNode } from './index';
import { buildComponentFromVNode } from './component';
import { createNode, setAccessor } from '../dom/index';
import { unmountComponent } from './component';
import options from '../options';
import { removeNode } from '../dom';

/** Queue of components that have been mounted and are awaiting componentDidMount */
export const mounts = [];

/** Diff recursion count, used to track the end of the diff cycle. */
export let diffLevel = 0;

/** Global flag indicating if the diff is currently within an SVG */
let isSvgMode = false;

/** Global flag indicating if the diff is performing hydration */
let hydrating = false;

/** Invoke queued componentDidMount lifecycle methods */
export function flushMounts() {
	let c;
	while ((c=mounts.pop())) {
		if (options.afterMount) options.afterMount(c);
		if (c.componentDidMount) c.componentDidMount();
	}
}

/** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
 *	@param {Element} [dom=null]		A DOM node to mutate into the shape of the `vnode`
 *	@param {VNode} vnode			A VNode (with descendants forming a tree) representing the desired DOM structure
 *	@returns {Element} dom			The created/mutated element
 *	@private
 */
export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
	// diffLevel having been 0 here indicates initial entry into the diff (not a subdiff)
	if (!diffLevel++) {
		// when first starting the diff, check if we're diffing an SVG or within an SVG
		isSvgMode = parent!=null && parent.ownerSVGElement!==undefined;

		// hydration is indicated by the existing element to be diffed not having a prop cache
		hydrating = dom!=null && !(ATTR_KEY in dom);
	}

	let ret = idiff(dom, vnode, context, mountAll, componentRoot);

	// append the element if its a new parent
	if (parent && ret.parentNode!==parent) parent.appendChild(ret);

	// diffLevel being reduced to 0 means we're exiting the diff
	if (!--diffLevel) {
		hydrating = false;
		// invoke queued componentDidMount lifecycle methods
		if (!componentRoot) flushMounts();
	}

	return ret;
}

/** Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing. */
function idiff(dom, vnode, context, mountAll, componentRoot) {
	let out = dom,
		prevSvgMode = isSvgMode;

	// empty values (null, undefined, booleans) render as empty Text nodes
	if (vnode==null || typeof vnode==='boolean') vnode = '';

	// Fast case: Strings & Numbers create/update Text nodes.
	if (typeof vnode==='string' || typeof vnode==='number') {

		// update if it's already a Text node:
		if (dom && dom.splitText!==undefined && dom.parentNode && (!dom._component || componentRoot)) {
			if (dom.nodeValue!=vnode) {
				dom.nodeValue = vnode;
			}
		}
		else {
			// it wasn't a Text node: replace it with one and recycle the old Element
			out = document.createTextNode(vnode);
			if (dom) {
				if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
				recollectNodeTree(dom, true);
			}
		}

		out[ATTR_KEY] = true;

		return out;
	}

	// If the VNode represents a Component, perform a component diff:
	let vnodeName = vnode.nodeName;
	if (typeof vnodeName==='function') {
		return buildComponentFromVNode(dom, vnode, context, mountAll);
	}

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvgMode = vnodeName==='svg' ? true : vnodeName==='foreignObject' ? false : isSvgMode;

	// If there's no existing element or it's the wrong type, create a new one:
	vnodeName = String(vnodeName);
	if (!dom || !isNamedNode(dom, vnodeName)) {
		out = createNode(vnodeName, isSvgMode);

		if (dom) {
			// move children into the replacement node
			while (dom.firstChild) out.appendChild(dom.firstChild);

			// if the previous Element was mounted into the DOM, replace it inline
			if (dom.parentNode) dom.parentNode.replaceChild(out, dom);

			// recycle the old element (skips non-Element node types)
			recollectNodeTree(dom, true);
		}
	}

	let fc = out.firstChild,
		props = out[ATTR_KEY],
		vchildren = vnode.children;

	if (props==null) {
		props = out[ATTR_KEY] = {};
		for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value;
	}

	// Optimization: fast-path for elements containing a single TextNode:
	if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
		if (fc.nodeValue!=vchildren[0]) {
			fc.nodeValue = vchildren[0];
		}
	}
	// otherwise, if there are existing or new children, diff them:
	else if (vchildren && vchildren.length || fc!=null) {
		innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null);
	}

	// Apply attributes/props from VNode to the DOM Element:
	diffAttributes(out, vnode.attributes, props);

	// restore previous SVG mode: (in case we're exiting an SVG namespace)
	isSvgMode = prevSvgMode;

	return out;
}

/** Apply child and attribute changes between a VNode and a DOM Node to the DOM.
 *	@param {Element} dom			Element whose children should be compared & mutated
 *	@param {Array} vchildren		Array of VNodes to compare to `dom.childNodes`
 *	@param {Object} context			Implicitly descendant context object (from most recent `getChildContext()`)
 *	@param {Boolean} mountAll
 *	@param {Boolean} isHydrating	If `true`, consumes externally created elements similar to hydration
 */
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
	let originalChildren = dom.childNodes,
		children = [],
		keyed = {},
		keyedLen = 0,
		min = 0,
		len = originalChildren.length,
		childrenLen = 0,
		vlen = vchildren ? vchildren.length : 0,
		j, c, f, vchild, child;

	// Build up a map of keyed children and an Array of unkeyed children:
	if (len!==0) {
		for (let i=0; i<len; i++) {
			let child = originalChildren[i],
				props = child[ATTR_KEY],
				key = vlen && props ? child._component ? child._component.__key : props.key : null;
			if (key!=null) {
				keyedLen++;
				keyed[key] = child;
			}
			else if (props || (child.splitText!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) {
				children[childrenLen++] = child;
			}
		}
	}

	if (vlen!==0) {
		for (let i=0; i<vlen; i++) {
			vchild = vchildren[i];
			child = null;

			// attempt to find a node based on key matching
			let key = vchild.key;
			if (key!=null) {
				if (keyedLen && keyed[key]!==undefined) {
					child = keyed[key];
					keyed[key] = undefined;
					keyedLen--;
				}
			}
			// attempt to pluck a node of the same type from the existing children
			else if (!child && min<childrenLen) {
				for (j=min; j<childrenLen; j++) {
					if (children[j]!==undefined && isSameNodeType(c = children[j], vchild, isHydrating)) {
						child = c;
						children[j] = undefined;
						if (j===childrenLen-1) childrenLen--;
						if (j===min) min++;
						break;
					}
				}
			}

			// morph the matched/found/created DOM child to match vchild (deep)
			child = idiff(child, vchild, context, mountAll);

			f = originalChildren[i];
			if (child && child!==dom && child!==f) {
				if (f==null) {
					dom.appendChild(child);
				}
				else if (child===f.nextSibling) {
					removeNode(f);
				}
				else {
					dom.insertBefore(child, f);
				}
			}
		}
	}

	// remove unused keyed children:
	if (keyedLen) {
		for (let i in keyed) if (keyed[i]!==undefined) recollectNodeTree(keyed[i], false);
	}

	// remove orphaned unkeyed children:
	while (min<=childrenLen) {
		if ((child = children[childrenLen--])!==undefined) recollectNodeTree(child, false);
	}
}

/** Recursively recycle (or just unmount) a node an its descendants.
 *	@param {Node} node						DOM node to start unmount/removal from
 *	@param {Boolean} [unmountOnly=false]	If `true`, only triggers unmount lifecycle, skips removal
 */
export function recollectNodeTree(node, unmountOnly) {
	let component = node._component;
	if (component) {
		// if node is owned by a Component, unmount that component (ends up recursing back here)
		unmountComponent(component);
	}
	else {
		// If the node's VNode had a ref function, invoke it with null here.
		// (this is part of the React spec, and smart for unsetting references)
		if (node[ATTR_KEY]!=null && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);

		if (unmountOnly===false || node[ATTR_KEY]==null) {
			removeNode(node);
		}

		removeChildren(node);
	}
}

/** Recollect/unmount all children.
 *	- we use .lastChild here because it causes less reflow than .firstChild
 *	- it's also cheaper than accessing the .childNodes Live NodeList
 */
export function removeChildren(node) {
	node = node.lastChild;
	while (node) {
		let next = node.previousSibling;
		recollectNodeTree(node, true);
		node = next;
	}
}

/** Apply differences in attributes from a VNode to the given DOM Element.
 *	@param {Element} dom		Element with attributes to diff `attrs` against
 *	@param {Object} attrs		The desired end-state key-value attribute pairs
 *	@param {Object} old			Current/previous attributes (from previous VNode or element's prop cache)
 */
function diffAttributes(dom, attrs, old) {
	let name;

	// remove attributes no longer present on the vnode by setting them to undefined
	for (name in old) {
		if (!(attrs && attrs[name]!=null) && old[name]!=null) {
			setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode);
		}
	}

	// add new & update changed attributes
	for (name in attrs) {
		if (name!=='children' && name!=='innerHTML' && (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name]))) {
			setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
		}
	}
}
<|endoftext|>


<commit_before>!(function(){

    var QUnit = require('qunit');
    var $ = require('jquery');

    // A few QUnit configuration settings
    // For more, see: http://api.qunitjs.com/QUnit.config/
    QUnit.config.autostart  = false;
    QUnit.config.reorder    = false;

    /* Setup variables */
    var testList = {};
    testList.Name = new Date().toString();
    testList.Description = "This is the test description.";

    QUnit.test( "AddList", function(assert) {

        assert.expect(2);
        var done = assert.async();

        var p = $().SPServices({
            operation: "AddList",
            listName: testList.Name,
            description: testList.Description,
            templateID: 100
        });
        p.done(function(){

            var listInfo = $(p.responseXML).find("List");
            equal(listInfo.attr("Title"), testList.Name, "Title of list should be should be ::" + testList.Name);
            equal(listInfo.attr("Description"), testList.Description, "Description of list should be should be ::" + testList.Description);
            done();

        });

    });

    QUnit.test( "UpdateListItems - New", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "UpdateListItems",
            listName: testList.Name,
            batchCmd: "New",
            valuepairs: [["Title", "New Title Value"]]
        });
        p.done(function(){

            var listError = $(p.responseXML).SPFilterNode("ErrorCode").text();
            equal(listError, "0x00000000", "Error code should be should be 0x00000000");
            done();

        });

    });

    QUnit.test( "GetListItems", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "GetListItems",
            listName: testList.Name
        });
        p.done(function(){

            var listCount = $(p.responseXML).SPFilterNode("rs:data").attr("ItemCount");
            equal(listCount, 1, "Count of list items should be should be 1");
            done();

        });

    });

    QUnit.test("SPGetDisplayFromStatic", function(assert) {

        assert.expect(1);

        var thisDisplayName =  $().SPServices.SPGetDisplayFromStatic({
            listName: testList.Name,
            columnStaticName: "Title"
        });

        equal(thisDisplayName, "Title", "SPGetDisplayFromStatic retrieved displayName=::" + thisDisplayName + "::");

    });

    QUnit.test("SPGetStaticFromDisplay", function(assert) {

        assert.expect(1);

        var thisStaticName =  $().SPServices.SPGetStaticFromDisplay({
            listName: testList.Name,
            columnDisplayName: "Title"
        });

        equal(thisStaticName, "Title", "SPGetStaticFromDisplay retrieved staticName=::" + thisStaticName + "::");

    });

    QUnit.test("SPGetLastItemId", function(assert) {

        assert.expect(1);

        var thisItemId =  $().SPServices.SPGetLastItemId({
            listName: testList.Name
        });

        equal(thisItemId, 1, "SPGetLastItemId retrieved thisItemId=::" + thisItemId + "::");

    });

    QUnit.test( "DeleteList", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "DeleteList",
            listName: testList.Name
        });
        p.done(function(){

            var listInfo = $(p.responseXML).find("DeleteListResponse");

            equal(listInfo.length, 1, "List deleted successfully" + testList.Name);
            done();

        });

    });

}());<commit_msg>Fixed some lint issues<commit_after>!(function(){

    var QUnit = require('qunit');
    var $ = require('jquery');
    var equal = require('equal');

    // A few QUnit configuration settings
    // For more, see: http://api.qunitjs.com/QUnit.config/
    QUnit.config.autostart  = false;
    QUnit.config.reorder    = false;

    /* Setup variables */
    var testList = {};
    testList.Name = new Date().toString();
    testList.Description = "This is the test description.";

    QUnit.test( "AddList", function(assert) {

        assert.expect(2);
        var done = assert.async();

        var p = $().SPServices({
            operation: "AddList",
            listName: testList.Name,
            description: testList.Description,
            templateID: 100
        });
        p.done(function(){

            var listInfo = $(p.responseXML).find("List");
            equal(listInfo.attr("Title"), testList.Name, "Title of list should be should be ::" + testList.Name);
            equal(listInfo.attr("Description"), testList.Description, "Description of list should be should be ::" + testList.Description);
            done();

        });

    });

    QUnit.test( "UpdateListItems - New", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "UpdateListItems",
            listName: testList.Name,
            batchCmd: "New",
            valuepairs: [["Title", "New Title Value"]]
        });
        p.done(function(){

            var listError = $(p.responseXML).SPFilterNode("ErrorCode").text();
            equal(listError, "0x00000000", "Error code should be should be 0x00000000");
            done();

        });

    });

    QUnit.test( "GetListItems", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "GetListItems",
            listName: testList.Name
        });
        p.done(function(){

            var listCount = $(p.responseXML).SPFilterNode("rs:data").attr("ItemCount");
            equal(listCount, 1, "Count of list items should be should be 1");
            done();

        });

    });

    QUnit.test("SPGetDisplayFromStatic", function(assert) {

        assert.expect(1);

        var thisDisplayName =  $().SPServices.SPGetDisplayFromStatic({
            listName: testList.Name,
            columnStaticName: "Title"
        });

        equal(thisDisplayName, "Title", "SPGetDisplayFromStatic retrieved displayName=::" + thisDisplayName + "::");

    });

    QUnit.test("SPGetStaticFromDisplay", function(assert) {

        assert.expect(1);

        var thisStaticName =  $().SPServices.SPGetStaticFromDisplay({
            listName: testList.Name,
            columnDisplayName: "Title"
        });

        equal(thisStaticName, "Title", "SPGetStaticFromDisplay retrieved staticName=::" + thisStaticName + "::");

    });

    QUnit.test("SPGetLastItemId", function(assert) {

        assert.expect(1);

        var thisItemId =  $().SPServices.SPGetLastItemId({
            listName: testList.Name
        });

        equal(thisItemId, 1, "SPGetLastItemId retrieved thisItemId=::" + thisItemId + "::");

    });

    QUnit.test( "DeleteList", function(assert) {

        assert.expect(1);
        var done = assert.async();

        var p = $().SPServices({
            operation: "DeleteList",
            listName: testList.Name
        });
        p.done(function(){

            var listInfo = $(p.responseXML).find("DeleteListResponse");

            equal(listInfo.length, 1, "List deleted successfully" + testList.Name);
            done();

        });

    });

}());<|endoftext|>


<commit_before>(function (enyo) {
	//*@protected
	/**
		This is a delegate (strategy) used by _enyo.DataGridList_ for vertically oriented
		lists. This is used by all lists for this strategy and does not get copied but
		called directly from the list. It is only available to _enyo.DataGridLists_.
	*/
	var p = enyo.clone(enyo.DataGridList.delegates.vertical);
	enyo.kind.extendMethods(p, {
		/**
			Once the list is initially rendered it will generate its scroller (so
			we know that is available). Now we need to cache our initial size values
			and apply them to our pages individually.
		*/
		rendered: function (list) {
			// get our initial sizing cached now since we should actually have
			// bounds at this point
			this.updateMetrics(list);
			// now if we already have a length then that implies we have a controller
			// and that we have data to render at this point, otherwise we don't
			// want to do any more initialization
			if (list.collection && list.collection.length) { this.reset(list); }
		},
		/**
			Reset the page with the added class update to the list.
		*/
		reset: enyo.inherit(function (sup) {
			return function (list) {
				sup.apply(this, arguments);
				if (list.hasReset && !list.hasClass("reset")) {
					list.canAddResetClass = true;
				}
			};
		}),
		/**
			Unlike in DataList we can calculate the page heights since we know the structure
			and layout of the page precisely.
		*/
		pageHeight: function (list, page) {
			var n  = page.node || page.hasNode(),
				a  = n.children.length,
				mx = list.metrics.pages[page.index], s;
			s = (Math.floor(a/list.columns)+(a%list.columns? 1: 0))*(list.tileHeight+list.spacing);
			n.style.height = s + "px";
			mx.height = s;
			return s;
		},
		/**
			This method generates the markup for the page content.
		*/
		generatePage: enyo.inherit(function (sup) {
			return function (list, page) {
				sup.apply(this, arguments);
				this.layout(list, page);
			};
		}),
		/**
			Returns the calculated width for the given page.
		*/
		pageWidth: function (list, page) {
			var s  = list.boundsCache.width,
				n  = page.node || page.hasNode(),
				mx = list.metrics.pages[page.index];
			n.style.width = s + "px";
			mx.width = s;
			return s;
		},
		/**
			Retrieves the default page size.
		*/
		defaultPageSize: function (list) {
			return (Math.ceil(this.controlsPerPage(list)/list.columns) * (list.tileHeight+list.spacing));
		},
		/**
			Calculates metric values required for the absolute positioning and scaling of
			the children in the list.
		*/
		updateMetrics: function (list) {
			this.updateBounds(list);
			var bs = list.boundsCache,
				w  = bs.width,
				s  = list.spacing,
				m  = list.minWidth,
				h  = list.minHeight;
			// the number of columns is the ratio of the available width minus the spacing
			// by the minimum tile width plus the spacing
			list.columns    = Math.max(Math.floor((w-s) / (m+s)), 1);
			// the actual tile width is a ratio of the remaining width after all columns
			// and spacing are accounted for and the number of columns that we know we should have
			list.tileWidth  = ((w-(s*(list.columns+1)))/list.columns);
			// the actual tile height is related to the tile width
			list.tileHeight = (h*(list.tileWidth/m));
			// unfortunately this forces us to recalculate the number of controls that can
			// be used for each page
			this.controlsPerPage(list);
			// Compute first and last row index bounds
			if (list.collection) {
				this.updateIndexBound(list);
			}
		},
		/**
			Calculates index bound that is required for adjusting page position
		*/
		updateIndexBound: function(list) {
			// If updateMetrics is not called before
			if (list.columns === undefined) {
				var bs = list.boundsCache,
					w  = bs.width,
					s  = list.spacing,
					m  = list.minWidth;
				list.columns = Math.max(Math.floor((w-s) / (m+s)), 1);
			}

			list.indexBoundFirstRow = list.columns;
			list.indexBoundLastRow = (Math.ceil(list.collection.length / list.columns) - 1) * list.columns - 1;
		},
		/**
			The number of controls necessary to fill a page will change depending on some
			factors such as scaling and list-size adjustments. It is a function of the calculated
			size required (pageSizeMultiplier * the current boundary height) and the adjusted tile height and
			spacing.
		*/
		controlsPerPage: enyo.inherit(function (sup) {
			return function (list) {
				var orig    = list._updatedControlsPerPage,
					perPage = sup.apply(this, arguments);
				if (orig != list._updatedControlsPerPage) {
					// we need to adjust this value as it did not take into account
					// the spacing or the columns
					perPage = list.controlsPerPage = (perPage * list.columns);
				}
				return perPage;
			};
		}),
		childSize: function (list) {
			// currently DataGridList is only vertical
			/*jshint -W093 */
			return (list.childSize = (list.tileHeight + list.spacing));
		},
		/**
			Takes a given page and arbitrarily positions its children according to the pre-computed
			metrics of the list.

			TODO: This could be optimized to use requestAnimationFrame as well as render not by
			child index but by row thus cutting down some of the over-calculation when iterating
			over every child.
		*/
		layout: function (list, page) {
			if (list.canAddResetClass) {
				list.addClass("reset");
				delete list.canAddResetClass;
			}
			var cc = list.columns,
				s  = list.spacing,
				w  = list.tileWidth,
				h  = list.tileHeight,
				r  = 0,
				n  = page,
				cn = n.children, co;
			if (cn.length) {
				for (var i=0, c; (c=cn[i]); ++i) {
					// the column
					co = i % cc;
					c.addStyles(
						"top: "    + Math.round(s  + (r  * (h+s))) + "px; " +
						(list.rtl ? "right: " : "left: ") + Math.round(s  + (co * (w+s))) + "px; " +
						"width: "  + Math.round(w) +                 "px; " +
						"height: " + Math.round(h) +                 "px"
					);
					// check if we need to increment the row
					if ((i+1) % cc === 0) { ++r; }
				}
			}
		},
		/**
			Recalculates the buffer size based on the current metrics for the given
			list. This may or may not be completely accurate until the final page is
			scrolled into view.
		*/
		adjustBuffer: function (list) {
			var pc = this.pageCount(list),
				ds = this.defaultPageSize(list),
				bs = 0, sp = list.psizeProp, ss = list.ssizeProp,
				n = list.$.buffer.node || list.$.buffer.hasNode(), p;
			if (n) {
				for (var i=0; i<pc; ++i) {
					p = list.metrics.pages[i];
					bs += (p && p[sp]) || ds;
				}
				bs += list.spacing;
				list.bufferSize = bs;
				n.style[sp] = bs + "px";
				n.style[ss] = this[ss](list) + "px";
			}
		},
		/**
			Delegate's resize event handler.
		*/
		didResize: function (list) {
			// store the previous stats for comparative purposes
			var prev = list.boundsCache;

			// flag the list to have its bounds updated
			list._updateBounds = true;
			this.updateMetrics(list);

			// if no change it the viewport then we didn't update anything size-wise
			// and do not need to refresh at all
			if (
				prev.left   === list.boundsCache.left  &&
				prev.top    === list.boundsCache.top   &&
				prev.width  === list.boundsCache.width &&
				prev.height === list.boundsCache.height
			) {
				return;
			}

			// it is necessary to update the content of the list according to our
			// new sizing
			this.refresh(list);
		}
	}, true);
	enyo.DataGridList.delegates.verticalGrid = p;
})(enyo);

<commit_msg>BHV-10114 Clear logic and add comments<commit_after>(function (enyo) {
	//*@protected
	/**
		This is a delegate (strategy) used by _enyo.DataGridList_ for vertically oriented
		lists. This is used by all lists for this strategy and does not get copied but
		called directly from the list. It is only available to _enyo.DataGridLists_.
	*/
	var p = enyo.clone(enyo.DataGridList.delegates.vertical);
	enyo.kind.extendMethods(p, {
		/**
			Once the list is initially rendered it will generate its scroller (so
			we know that is available). Now we need to cache our initial size values
			and apply them to our pages individually.
		*/
		rendered: function (list) {
			// get our initial sizing cached now since we should actually have
			// bounds at this point
			this.updateMetrics(list);
			// now if we already have a length then that implies we have a controller
			// and that we have data to render at this point, otherwise we don't
			// want to do any more initialization
			if (list.collection && list.collection.length) { this.reset(list); }
		},
		/**
			Reset the page with the added class update to the list.
		*/
		reset: enyo.inherit(function (sup) {
			return function (list) {
				sup.apply(this, arguments);
				if (list.hasReset && !list.hasClass("reset")) {
					list.canAddResetClass = true;
				}
			};
		}),
		/**
			Unlike in DataList we can calculate the page heights since we know the structure
			and layout of the page precisely.
		*/
		pageHeight: function (list, page) {
			var n  = page.node || page.hasNode(),
				a  = n.children.length,
				mx = list.metrics.pages[page.index], s;
			s = (Math.floor(a/list.columns)+(a%list.columns? 1: 0))*(list.tileHeight+list.spacing);
			n.style.height = s + "px";
			mx.height = s;
			return s;
		},
		/**
			This method generates the markup for the page content.
		*/
		generatePage: enyo.inherit(function (sup) {
			return function (list, page) {
				sup.apply(this, arguments);
				this.layout(list, page);
			};
		}),
		/**
			Returns the calculated width for the given page.
		*/
		pageWidth: function (list, page) {
			var s  = list.boundsCache.width,
				n  = page.node || page.hasNode(),
				mx = list.metrics.pages[page.index];
			n.style.width = s + "px";
			mx.width = s;
			return s;
		},
		/**
			Retrieves the default page size.
		*/
		defaultPageSize: function (list) {
			return (Math.ceil(this.controlsPerPage(list)/list.columns) * (list.tileHeight+list.spacing));
		},
		/**
			Calculates metric values required for the absolute positioning and scaling of
			the children in the list.
		*/
		updateMetrics: function (list) {
			this.updateBounds(list);
			var bs = list.boundsCache,
				w  = bs.width,
				s  = list.spacing,
				m  = list.minWidth,
				h  = list.minHeight;
			// the number of columns is the ratio of the available width minus the spacing
			// by the minimum tile width plus the spacing
			list.columns    = Math.max(Math.floor((w-s) / (m+s)), 1);
			// the actual tile width is a ratio of the remaining width after all columns
			// and spacing are accounted for and the number of columns that we know we should have
			list.tileWidth  = ((w-(s*(list.columns+1)))/list.columns);
			// the actual tile height is related to the tile width
			list.tileHeight = (h*(list.tileWidth/m));
			// unfortunately this forces us to recalculate the number of controls that can
			// be used for each page
			this.controlsPerPage(list);
			// Compute first and last row index bounds
			if (list.collection) {
				this.updateIndexBound(list);
			}
		},
		/**
			Calculates index bound that is required for adjusting page position
			You can call this method after DataGridList is rendered.
		*/
		updateIndexBound: function(list) {
			// If user calls this method before DataGridList is rendered
			if (list.boundsCache === undefined) {
				this.updateMetrics(list);
			}

			list.indexBoundFirstRow = list.columns;
			list.indexBoundLastRow = (Math.ceil(list.collection.length / list.columns) - 1) * list.columns - 1;
		},
		/**
			The number of controls necessary to fill a page will change depending on some
			factors such as scaling and list-size adjustments. It is a function of the calculated
			size required (pageSizeMultiplier * the current boundary height) and the adjusted tile height and
			spacing.
		*/
		controlsPerPage: enyo.inherit(function (sup) {
			return function (list) {
				var orig    = list._updatedControlsPerPage,
					perPage = sup.apply(this, arguments);
				if (orig != list._updatedControlsPerPage) {
					// we need to adjust this value as it did not take into account
					// the spacing or the columns
					perPage = list.controlsPerPage = (perPage * list.columns);
				}
				return perPage;
			};
		}),
		childSize: function (list) {
			// currently DataGridList is only vertical
			/*jshint -W093 */
			return (list.childSize = (list.tileHeight + list.spacing));
		},
		/**
			Takes a given page and arbitrarily positions its children according to the pre-computed
			metrics of the list.

			TODO: This could be optimized to use requestAnimationFrame as well as render not by
			child index but by row thus cutting down some of the over-calculation when iterating
			over every child.
		*/
		layout: function (list, page) {
			if (list.canAddResetClass) {
				list.addClass("reset");
				delete list.canAddResetClass;
			}
			var cc = list.columns,
				s  = list.spacing,
				w  = list.tileWidth,
				h  = list.tileHeight,
				r  = 0,
				n  = page,
				cn = n.children, co;
			if (cn.length) {
				for (var i=0, c; (c=cn[i]); ++i) {
					// the column
					co = i % cc;
					c.addStyles(
						"top: "    + Math.round(s  + (r  * (h+s))) + "px; " +
						(list.rtl ? "right: " : "left: ") + Math.round(s  + (co * (w+s))) + "px; " +
						"width: "  + Math.round(w) +                 "px; " +
						"height: " + Math.round(h) +                 "px"
					);
					// check if we need to increment the row
					if ((i+1) % cc === 0) { ++r; }
				}
			}
		},
		/**
			Recalculates the buffer size based on the current metrics for the given
			list. This may or may not be completely accurate until the final page is
			scrolled into view.
		*/
		adjustBuffer: function (list) {
			var pc = this.pageCount(list),
				ds = this.defaultPageSize(list),
				bs = 0, sp = list.psizeProp, ss = list.ssizeProp,
				n = list.$.buffer.node || list.$.buffer.hasNode(), p;
			if (n) {
				for (var i=0; i<pc; ++i) {
					p = list.metrics.pages[i];
					bs += (p && p[sp]) || ds;
				}
				bs += list.spacing;
				list.bufferSize = bs;
				n.style[sp] = bs + "px";
				n.style[ss] = this[ss](list) + "px";
			}
		},
		/**
			Delegate's resize event handler.
		*/
		didResize: function (list) {
			// store the previous stats for comparative purposes
			var prev = list.boundsCache;

			// flag the list to have its bounds updated
			list._updateBounds = true;
			this.updateMetrics(list);

			// if no change it the viewport then we didn't update anything size-wise
			// and do not need to refresh at all
			if (
				prev.left   === list.boundsCache.left  &&
				prev.top    === list.boundsCache.top   &&
				prev.width  === list.boundsCache.width &&
				prev.height === list.boundsCache.height
			) {
				return;
			}

			// it is necessary to update the content of the list according to our
			// new sizing
			this.refresh(list);
		}
	}, true);
	enyo.DataGridList.delegates.verticalGrid = p;
})(enyo);

<|endoftext|>


<commit_before><commit_msg>Add tests for isNodeList() #47<commit_after><|endoftext|>