<commit_before>/**
 * Peento Application
 *
 * @author Zongmin Lei <leizongmin@gmail.com>
 */

var fs = require('fs');
var path = require('path');
var events = require('events');
var util = require('util');
var async = require('async');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('cookie-session');
var morgan = require('morgan');
var serveStatic = require('serve-static');
var timeout = require('connect-timeout');
var rd = require('rd');
var expressLiquid = require('express-liquid');
var createNamespace = require('lei-ns').Namespace;
var MySQLPool = require('lei-mysql');
var MySQLModel = require('lei-mysql-model');
var Pipe = require('lei-pipe');
var RoutesSort = require('lei-routes-sort');
var FileLookup = require('file-lookup');
var errorhandler = require('./middleware/errorhandler');
var assetsMiddleware = require('./middleware/assets');
var csrfMiddleware = require('./middleware/csrf');
var multipartyMiddleware = require('./middleware/multiparty');
var utils = require('./lib/utils');
var Plugin = require('./lib/plugin');
var createDebug = require('./lib/debug');
var defaultConfig = require('./lib/default_config');
var debug = require('./lib/debug')('app');

function warning (str) {
  console.error('Warning: ' + str);
}

module.exports = function (config) {
  return new PeentoApplication(config);
};

function PeentoApplication (config) {
  debug('new');

  // fill default config
  config = utils.merge(defaultConfig, config || {});

  // init global namespace
  var ns = this.ns = createNamespace();
  ns('app', this);
  ns('config', config);
  ns('utils', utils);
  ns('debug', createDebug);
  ns('middleware.csrf', csrfMiddleware(ns, debug));
  ns('middleware.multiparty', multipartyMiddleware(ns, debug));
  this._is_debug = !!config.debug;
  this._fileLookup = {
    view:  new FileLookup(),
    asset: new FileLookup()
  };

  // init express
  var app = this.express = express();
  this.router = RoutesSort.create();
  var me = this;
  this._registerBaseMiddlewares = [];
  ['logger', 'body', 'query', 'cookie', 'session', 'assets', 'timeout'].forEach(function (name) {
    me.useMiddleware(me._getDefaultMiddleware(name));
  });
}
util.inherits(PeentoApplication, events.EventEmitter);

PeentoApplication.prototype.listen = function (port) {
  debug('listen %s', port);
  this.express.listen(port);
};

PeentoApplication.prototype.start = function () {
  debug('start');
  this._initBaseMiddlewares();
  this._initDb();
  this._initPlugins();
  this._initFilters();
  this._initLocals();
  this.router.register(this.express);
  this.express.use(errorhandler());
  this._loadDefaultViews();
  this.listen(this.ns('config.port'));
  this.emit('start');
};

/******************************************************************************/

PeentoApplication.prototype.useMiddleware = function (options, fn) {
  var app = this.express;
  if (typeof options === 'function') {
    fn = options;
    options = {};
  }
  options.name = options.name || utils.randomString(10);
  options.path = options.path || '/';
  debug('use middleware: %s [%s] before:%s after:%s', options.path, options.name, options.before, options.after);
  this._registerBaseMiddlewares.push({
    name: options.name,
    before: options.before,
    after:  options.after,
    fn: function () {
      app.use(options.path, fn);
    }
  });
};

/******************************************************************************/

PeentoApplication.prototype._loadDefaultViews = function () {
  var views = this.ns('view');
  if (!views['view_not_found.liquid']) {
    views['view_not_found.liquid'] = path.resolve(__dirname, 'view/view_not_found.liquid');
  }
};

PeentoApplication.prototype._usePlugin = function (name, fn) {
  var ns = this.ns;
  var plugin = new Plugin(name, ns);
  ns('plugin.' + name, plugin);
  fn(ns, plugin, plugin.debug);

  if (!Array.isArray(this._plugins)) this._plugins = [];
  this._plugins.push(plugin);

  this._fileLookup.asset.add(path.resolve(plugin.dir, 'asset'));
  this._fileLookup.view.add(path.resolve(plugin.dir, 'view'));
}

PeentoApplication.prototype.use = function (name) {
  var errs = [];
  var m;

  if (typeof name === 'function') {
    m = name;
    name = utils.randomString(8);
  }

  // try to load from working path: ./name
  if (!m) {
    try {
      m = require(path.resolve(name));
    } catch (err) {
      errs.push(err);
    }
  }

  // try to load from package "peento-xxx"
  if (!m) {
    try {
      var n = 'peento-' + name;
      m = require(n);
    } catch (err) {
      errs.push(err);
    }
  }

  if (typeof m !== 'function') {
    throw new Error('Plugin ' + name + ' not found');
  }

  this._usePlugin(name, m);
};

PeentoApplication.prototype._initPlugins = function () {
  debug('_initPlugins');
  if (Array.isArray(this._plugins)) {
    this._plugins.forEach(function (plugin) {
      plugin.init();
    });
  } else {
    warning('no plugin was loaded.');
  }
};

/******************************************************************************/

PeentoApplication.prototype._initTpl = function () {
  debug('_initTpl');
  var me = this;
  var ns = this.ns;
  var app = this.express;

  var baseContext = this.context = expressLiquid.newContext();
  var renderLiquid = this.renderLiquid = expressLiquid({
    context: baseContext,
    resolveFilename: function (name, settings) {
      var ext = path.extname(name);
      if (!ext) name += '.liquid';
      return name;
    },
    includeFile: function (name, callback) {
      var views = ns('view');
      if (name[0] === '/') name = name.slice(1);
      var f = views[name];
      // if is debug mode, try to lookup from view paths
      if (!f && me._is_debug) {
        f = me._fileLookup.view.resolveSync(name);
      }
      // view not found
      if (!f) f = views['view_not_found.liquid'];
      debug('resolve view: [%s] %s', name, f);

      // read file
      fs.readFile(f, {encoding: 'utf8'}, function (err, tpl) {
        if (err) return callback(err);
        callback(null, '{% assign _view_name="' + name.slice(0, -7) + '" %}' + tpl)
      });
    },
    traceError: me._is_debug
  });

  app.use(function (req, res, next) {
    res.context = expressLiquid.newContext();
    res._render = res.render;

    res.render = function (tpl) {
      debug('render: %s', tpl);

      res.context.setLocals('_server', {
        query:  req.query,
        body:   req.body,
        params: req.params,
        headers: req.headers,
        session: req.session
      });
      res.context.setLocals('_config', ns('config'));

      renderLiquid(tpl, {
        context:    res.context,
        cache:      !me._is_debug,
        settings:   {}
      }, function (err, html) {
        if (err) return next(err);
        res.header('content-type', 'text/html');
        res.end(html);
      });
    };

    res.setLocals = function (n, v) {
      return res.context.setLocals(n, v);
    };

    next();
  });
};

PeentoApplication.prototype._initFilters = function () {
  debug('_initFilters');
  var ns = this.ns;
  var baseContext = this.context;
  var filters = ns('filter');
  for (var i in filters) {
    if (i.substr(-5) === 'Async') {
      baseContext.setAsyncFilter(i.substr(0, i.length - 5), filters[i]);
    } else {
      baseContext.setFilter(i, filters[i]);
    }
  }
};

PeentoApplication.prototype._initLocals = function () {
  debug('_initLocals');
  var ns = this.ns;
  var baseContext = this.context;
  var locals = ns('locals');
  for (var i in locals) {
    var item = locals[i];
    baseContext.setAsyncLocals(item.p, item.fn);
  }
};

PeentoApplication.prototype._initDb = function () {
  debug('_initDb');
  var ns = this.ns;
  var db = new MySQLPool(ns('config.mysql'));
  this.db = db;
  ns('db', db);

  var debugSql = createDebug('db:query');
  db.use('sql', function (sql, next) {
    debugSql(sql);
    next(null, sql);
  });
};

PeentoApplication.prototype._initBaseMiddlewares = function () {
  debug('_initBaseMiddlewares');
  var me = this;
  var ns = this.ns;
  var app = this.express;

  var p = new Pipe();
  this._registerBaseMiddlewares.forEach(function (item) {
    p.add(item.name, {
      before: item.before,
      after:  item.after
    }, item.fn);
  });
  p.start({}, function (err) {
    if (err) throw err;
  });

  this._initTpl();
};

/******************************************************************************/

PeentoApplication.prototype._getDefaultMiddleware = function (name) {
  name = name.toLowerCase();
  debug('_getDefaultMiddleware: %s', name);
  var ns = this.ns;
  switch (name) {
    case 'logger': return morgan();
    case 'body': return bodyParser();
    case 'query': return express.query();
    case 'cookie': return cookieParser(ns('config.cookie.secret'));
    case 'session': return session({keys: [ns('config.session.secret')]});
    case 'assets': return assetsMiddleware(ns);
    case 'timeout': return timeout(ns('config.request.timeout'));
    default: throw new Error('no default middleware "' + name + '"');
  }
};

/******************************************************************************/

PeentoApplication.prototype._getCallPipe = function (name) {
  this._callPipes = this._callPipes || {};
  this._callPipes[name] = this._callPipes[name] || new Pipe();
  return this._callPipes[name];
};

PeentoApplication.prototype.call = function (name, params, callback) {
  var me = this;
  var ns = me.ns;

  var call = ns('call.' + name);
  if (typeof call !== 'function') {
    return callback(new TypeError('Cannot call ' + name));
  }

  async.series([

    // before.xxxx
    function (next) {
      debug('call: before %s', name);
      var before = me._getCallPipe('before.' + name);
      before.start(params, function (err, data) {
        params = data;
        next(err);
      });
    },

    // xxxx
    function (next) {
      debug('call: %s', name);
      call(params, function (err, data) {
        params = data;
        next(err);
      });
    },

    // after.xxx
    function (next) {
      debug('call: after %s', name);
      var after = me._getCallPipe('after.' + name);
      after.start(params, function (err, data) {
        params = data;
        next(err);
      });
    }

  ], function (err) {
    callback(err, params);
  });
};
<commit_msg>app.removeMiddleware()<commit_after>/**
 * Peento Application
 *
 * @author Zongmin Lei <leizongmin@gmail.com>
 */

var fs = require('fs');
var path = require('path');
var events = require('events');
var util = require('util');
var async = require('async');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('cookie-session');
var morgan = require('morgan');
var serveStatic = require('serve-static');
var timeout = require('connect-timeout');
var rd = require('rd');
var expressLiquid = require('express-liquid');
var createNamespace = require('lei-ns').Namespace;
var MySQLPool = require('lei-mysql');
var MySQLModel = require('lei-mysql-model');
var Pipe = require('lei-pipe');
var RoutesSort = require('lei-routes-sort');
var FileLookup = require('file-lookup');
var errorhandler = require('./middleware/errorhandler');
var assetsMiddleware = require('./middleware/assets');
var csrfMiddleware = require('./middleware/csrf');
var multipartyMiddleware = require('./middleware/multiparty');
var utils = require('./lib/utils');
var Plugin = require('./lib/plugin');
var createDebug = require('./lib/debug');
var defaultConfig = require('./lib/default_config');
var debug = require('./lib/debug')('app');

function warning (str) {
  console.error('Warning: ' + str);
}

module.exports = function (config) {
  return new PeentoApplication(config);
};

function PeentoApplication (config) {
  debug('new');

  // fill default config
  config = utils.merge(defaultConfig, config || {});

  // init global namespace
  var ns = this.ns = createNamespace();
  ns('app', this);
  ns('config', config);
  ns('utils', utils);
  ns('debug', createDebug);
  ns('middleware.csrf', csrfMiddleware(ns, debug));
  ns('middleware.multiparty', multipartyMiddleware(ns, debug));
  this._is_debug = !!config.debug;
  this._fileLookup = {
    view:  new FileLookup(),
    asset: new FileLookup()
  };

  // init express
  var app = this.express = express();
  this.router = RoutesSort.create();
  var me = this;
  this._registerBaseMiddlewares = [];
  ['logger', 'body', 'query', 'cookie', 'session', 'assets', 'timeout'].forEach(function (name) {
    me.useMiddleware(me._getDefaultMiddleware(name));
  });
}
util.inherits(PeentoApplication, events.EventEmitter);

PeentoApplication.prototype.listen = function (port) {
  debug('listen %s', port);
  this.express.listen(port);
};

PeentoApplication.prototype.start = function () {
  debug('start');
  this._initBaseMiddlewares();
  this._initDb();
  this._initPlugins();
  this._initFilters();
  this._initLocals();
  this.router.register(this.express);
  this.express.use(errorhandler());
  this._loadDefaultViews();
  this.listen(this.ns('config.port'));
  this.emit('start');
};

/******************************************************************************/

PeentoApplication.prototype.useMiddleware = function (options, fn) {
  var app = this.express;
  if (typeof options === 'function') {
    fn = options;
    options = {};
  }
  options.name = options.name || utils.randomString(10);
  options.path = options.path || '/';
  debug('use middleware: %s [%s] before:%s after:%s', options.path, options.name, options.before, options.after);
  this._registerBaseMiddlewares.push({
    name: options.name,
    before: options.before,
    after:  options.after,
    fn: function () {
      app.use(options.path, fn);
    }
  });
};

PeentoApplication.prototype.removeMiddleware = function (name) {
  name = name.toLowerCase();
  debug('remove middleware: %s', name);
  this._registerBaseMiddlewares = this._registerBaseMiddlewares.filter(function (item) {
    return (item.name !== name);
  });
};

/******************************************************************************/

PeentoApplication.prototype._loadDefaultViews = function () {
  var views = this.ns('view');
  if (!views['view_not_found.liquid']) {
    views['view_not_found.liquid'] = path.resolve(__dirname, 'view/view_not_found.liquid');
  }
};

PeentoApplication.prototype._usePlugin = function (name, fn) {
  var ns = this.ns;
  var plugin = new Plugin(name, ns);
  ns('plugin.' + name, plugin);
  fn(ns, plugin, plugin.debug);

  if (!Array.isArray(this._plugins)) this._plugins = [];
  this._plugins.push(plugin);

  this._fileLookup.asset.add(path.resolve(plugin.dir, 'asset'));
  this._fileLookup.view.add(path.resolve(plugin.dir, 'view'));
}

PeentoApplication.prototype.use = function (name) {
  var errs = [];
  var m;

  if (typeof name === 'function') {
    m = name;
    name = utils.randomString(8);
  }

  // try to load from working path: ./name
  if (!m) {
    try {
      m = require(path.resolve(name));
    } catch (err) {
      errs.push(err);
    }
  }

  // try to load from package "peento-xxx"
  if (!m) {
    try {
      var n = 'peento-' + name;
      m = require(n);
    } catch (err) {
      errs.push(err);
    }
  }

  if (typeof m !== 'function') {
    throw new Error('Plugin ' + name + ' not found');
  }

  this._usePlugin(name, m);
};

PeentoApplication.prototype._initPlugins = function () {
  debug('_initPlugins');
  if (Array.isArray(this._plugins)) {
    this._plugins.forEach(function (plugin) {
      plugin.init();
    });
  } else {
    warning('no plugin was loaded.');
  }
};

/******************************************************************************/

PeentoApplication.prototype._initTpl = function () {
  debug('_initTpl');
  var me = this;
  var ns = this.ns;
  var app = this.express;

  var baseContext = this.context = expressLiquid.newContext();
  var renderLiquid = this.renderLiquid = expressLiquid({
    context: baseContext,
    resolveFilename: function (name, settings) {
      var ext = path.extname(name);
      if (!ext) name += '.liquid';
      return name;
    },
    includeFile: function (name, callback) {
      var views = ns('view');
      if (name[0] === '/') name = name.slice(1);
      var f = views[name];
      // if is debug mode, try to lookup from view paths
      if (!f && me._is_debug) {
        f = me._fileLookup.view.resolveSync(name);
      }
      // view not found
      if (!f) f = views['view_not_found.liquid'];
      debug('resolve view: [%s] %s', name, f);

      // read file
      fs.readFile(f, {encoding: 'utf8'}, function (err, tpl) {
        if (err) return callback(err);
        callback(null, '{% assign _view_name="' + name.slice(0, -7) + '" %}' + tpl)
      });
    },
    traceError: me._is_debug
  });

  app.use(function (req, res, next) {
    res.context = expressLiquid.newContext();
    res._render = res.render;

    res.render = function (tpl) {
      debug('render: %s', tpl);

      res.context.setLocals('_server', {
        query:  req.query,
        body:   req.body,
        params: req.params,
        headers: req.headers,
        session: req.session
      });
      res.context.setLocals('_config', ns('config'));

      renderLiquid(tpl, {
        context:    res.context,
        cache:      !me._is_debug,
        settings:   {}
      }, function (err, html) {
        if (err) return next(err);
        res.header('content-type', 'text/html');
        res.end(html);
      });
    };

    res.setLocals = function (n, v) {
      return res.context.setLocals(n, v);
    };

    next();
  });
};

PeentoApplication.prototype._initFilters = function () {
  debug('_initFilters');
  var ns = this.ns;
  var baseContext = this.context;
  var filters = ns('filter');
  for (var i in filters) {
    if (i.substr(-5) === 'Async') {
      baseContext.setAsyncFilter(i.substr(0, i.length - 5), filters[i]);
    } else {
      baseContext.setFilter(i, filters[i]);
    }
  }
};

PeentoApplication.prototype._initLocals = function () {
  debug('_initLocals');
  var ns = this.ns;
  var baseContext = this.context;
  var locals = ns('locals');
  for (var i in locals) {
    var item = locals[i];
    baseContext.setAsyncLocals(item.p, item.fn);
  }
};

PeentoApplication.prototype._initDb = function () {
  debug('_initDb');
  var ns = this.ns;
  var db = new MySQLPool(ns('config.mysql'));
  this.db = db;
  ns('db', db);

  var debugSql = createDebug('db:query');
  db.use('sql', function (sql, next) {
    debugSql(sql);
    next(null, sql);
  });
};

PeentoApplication.prototype._initBaseMiddlewares = function () {
  debug('_initBaseMiddlewares');
  var me = this;
  var ns = this.ns;
  var app = this.express;

  var p = new Pipe();
  this._registerBaseMiddlewares.forEach(function (item) {
    p.add(item.name, {
      before: item.before,
      after:  item.after
    }, item.fn);
  });
  p.start({}, function (err) {
    if (err) throw err;
  });

  this._initTpl();
};

/******************************************************************************/

PeentoApplication.prototype._getDefaultMiddleware = function (name) {
  name = name.toLowerCase();
  debug('_getDefaultMiddleware: %s', name);
  var ns = this.ns;
  switch (name) {
    case 'logger': return morgan();
    case 'body': return bodyParser();
    case 'query': return express.query();
    case 'cookie': return cookieParser(ns('config.cookie.secret'));
    case 'session': return session({keys: [ns('config.session.secret')]});
    case 'assets': return assetsMiddleware(ns);
    case 'timeout': return timeout(ns('config.request.timeout'));
    default: throw new Error('no default middleware "' + name + '"');
  }
};

/******************************************************************************/

PeentoApplication.prototype._getCallPipe = function (name) {
  this._callPipes = this._callPipes || {};
  this._callPipes[name] = this._callPipes[name] || new Pipe();
  return this._callPipes[name];
};

PeentoApplication.prototype.call = function (name, params, callback) {
  var me = this;
  var ns = me.ns;

  var call = ns('call.' + name);
  if (typeof call !== 'function') {
    return callback(new TypeError('Cannot call ' + name));
  }

  async.series([

    // before.xxxx
    function (next) {
      debug('call: before %s', name);
      var before = me._getCallPipe('before.' + name);
      before.start(params, function (err, data) {
        params = data;
        next(err);
      });
    },

    // xxxx
    function (next) {
      debug('call: %s', name);
      call(params, function (err, data) {
        params = data;
        next(err);
      });
    },

    // after.xxx
    function (next) {
      debug('call: after %s', name);
      var after = me._getCallPipe('after.' + name);
      after.start(params, function (err, data) {
        params = data;
        next(err);
      });
    }

  ], function (err) {
    callback(err, params);
  });
};
<|endoftext|>


<commit_before>import { VNode } from "./vnode"

const stack = new Array(64)
const components = {}
let stackIndex = 0
let bodyNode = null

const elementOpen = function(type, props, srcElement)
{
	const parent = stack[stackIndex]
	let prevNode = parent.children[parent.index]
	let vnode = prevNode

	if(!prevNode)
	{
		const element = srcElement || document.createElement(type)
		vnode = new VNode(parent.index, type, props, element, parent)

		if(props) {
			for(let key in props) {
				setProp(element, key, props[key])
			}
		}

		vnode.props = props
		parent.children[parent.index] = vnode
	}
	else
	{
		if(prevNode.type !== type)
		{
			const element = srcElement || document.createElement(type)

			if(prevNode.component)
			{
				vnode = new VNode(prevNode.index, type, props, element, parent)

				const children = prevNode.children
				prevNode.children = vnode.children
				vnode.children = children

				for(let n = 0; n < children.length; n++)
				{
					const child = children[n]
					child.parent = vnode

					if(!child.component) {
						element.appendChild(child.element)
					}
					else {
						element.appendChild(node.component.base)
						child.element = element
						appendChildren(element, child.children)
					}
				}

				removeComponent(prevNode)

				prevNode.parent.children[prevNode.index] = vnode
			}
			else
			{
				const prevElement = prevNode.element

				while(prevElement.firstChild) {
					element.appendChild(prevElement.firstChild)
				}

				prevElement.parentElement.replaceChild(element, prevElement)

				vnode.type = type
				vnode.element = element
			}

			if(props)
			{
				for(let key in props) {
					setProp(element, key, props[key])
				}
				vnode.props = props
			}
		}
		else
		{
			const element = prevNode.element
			const prevProps = prevNode.props

			if(props !== prevProps)
			{
				if(props)
				{
					if(prevProps)
					{
						for(let key in prevProps) {
							if(props[key] === undefined) {
								unsetProp(element, key)
							}
						}

						for(let key in props) {
							const value = props[key]
							if(value !== prevProps[key]) {
								setProp(element, key, value)
							}
						}
					}
					else
					{
						for(let key in props) {
							setProp(element, key, props[key])
						}
					}

					prevNode.props = props
				}
				else
				{
					if(prevProps)
					{
						for(let key in prevProps) {
							unsetProp(element, key)
						}

						prevNode.props = null
					}
				}
			}
		}
	}

	parent.index++
	stackIndex++
	stack[stackIndex] = vnode

	return vnode
}

const appendChildren = (node, element, children) => {
	for(let n = 0; n < children.length; n++) {
		const child = children[n]
		if(!child.component) {
			element.appendChild(child.element)
		}
		else {
			element.appendChild(node.component.base)
			child.element = element
			appendChildren(node, element, child.children)
		}
	}
}

const elementClose = function(type)
{
	const node = stack[stackIndex]

	if(node.type !== type) {
		console.error(`(Element.close) Unexpected element closed: ${type} but was expecting: ${node.type}`)
	}

	if(node.index !== node.children.length) {
		removeUnusedNodes(node)
	}
	node.index = 0

	if(!node.element.parentElement) {
		appendElement(node.element, node.parent)
	}

	stackIndex--
}

const elementVoid = function(type, props) {
	const node = elementOpen(type, props)
	elementClose(type)
	return node
}

const element = function(element, props) {
	const node = elementOpen(element.localName, props, element)
	elementClose(element.localName)
	return node
}

const componentVoid = (ctor, props) =>
{
	const parent = stack[stackIndex]
	const node = parent.children[parent.index]
	let component
	let vnode

	if(node)
	{
		component = node.component
		if(component)
		{
			if(component.constructor === ctor) {
				diffComponentProps(component, node, props)
				vnode = node
			}
			else
			{
				vnode = createComponent(ctor, node, parent)
				component = vnode.component

				const children = node.children
				node.children = vnode.children
				vnode.children = children

				vnode.element.insertBefore(component.base, node.component.base)
				removeComponent(node)

				for(let n = 0; n < children.length; n++) {
					children[n].parent = vnode
				}
				parent.children[vnode.id] = vnode

				if(props) {
					for(let key in props) {
						component[key] = props[key]
					}
				}
			}
		}
		else
		{
			vnode = createComponent(ctor, node, parent)
			component = vnode.component

			vnode.children.push(node)
			vnode.element.insertBefore(component.base, node.element)
			node.index = 0
			node.id = 0
			node.parent = vnode

			parent.children[vnode.id] = vnode

			if(props) {
				for(let key in props) {
					component[key] = props[key]
				}
			}
		}
	}
	else
	{
		vnode = createComponent(ctor, null, parent)
		component = vnode.component

		vnode.id = parent.children.length
		parent.children.push(vnode)
		parent.element.appendChild(component.base)

		parent.children[vnode.id] = vnode

		if(props) {
			for(let key in props) {
				component[key] = props[key]
			}
		}
	}

	if(component.mount) {
		component.mount()
	}

	parent.index++
	stackIndex++
	stack[stackIndex] = vnode

	component.depth = stackIndex
	component.render()
	component.dirty = false

	if(vnode.index !== vnode.children.length) {
		removeUnusedNodes(vnode)
	}

	vnode.index = 0
	stackIndex--

	return component
}

const diffComponentProps = (component, node, props) =>
{
	const prevProps = node.props

	if(props !== prevProps)
	{
		if(props)
		{
			if(prevProps)
			{
				for(let key in prevProps)
				{
					if(props[key] === undefined) {
						if(key[0] === "$") {
							component[key] = component.state[key.slice(1)]
						}
						else {
							component[key] = null
						}
					}
				}

				for(let key in props) {
					const value = props[key]
					if(component[key] !== value) {
						component[key] = value
					}
				}
			}
			else
			{
				for(let key in props) {
					component[key] = props[key]
				}
			}

			node.props = props
		}
		else if(prevProps)
		{
			for(let key in prevProps) {
				if(key[0] === "$") {
					component[key] = component.state[ket.slice(1)]
				}
				else {
					component[key] = null
				}
			}

			node.props = null
		}
	}
}

const createComponent = (ctor, node, parent) =>
{
	const buffer = components[ctor.prototype.__componentIndex]
	let component = buffer ? buffer.pop() : null
	let vnode

	if(!component) {
		component = new ctor()
		vnode = new VNode(0, null, null, parent.element, parent)
		vnode.component = component
		component.vnode = vnode
	}
	else {
		vnode = component.vnode
	}

	if(node) {
		vnode.id = node.id
	}
	vnode.parent = parent
	vnode.element = parent.element
	return vnode
}

const removeComponent = (node) =>
{
	const component = node.component
	const buffer = components[component.__componentIndex]
	if(buffer) {
		buffer.push(component)
	}
	else {
		components[component.__componentIndex] = [ component ]
	}

	component.remove()
	component.base.remove()
	removeUnusedNodes(node)
}

const text = (text) =>
{
	const parent = stack[stackIndex]
	let node = parent.children[parent.index]

	if(node)
	{
		if(node.type === "#text") {
			if(node.element.nodeValue !== text) {
				node.element.nodeValue = text
			}
		}
		else
		{
			const element = document.createTextNode(text)

			if(node.component) {
				const vnodeNew = new VNode(parent.index, "#text", null, element, parent, null)
				parent.children[parent.index] = vnodeNew
				node.element.insertBefore(element, node.component.base)
				node.index = 0
				removeComponent(node)
			}
			else {
				node.element.parentElement.replaceChild(element, node.element)
				node.type = "#text"
				node.element = element
			}
		}
	}
	else {
		const element = document.createTextNode(text)
		const vnode = new VNode(parent.index, "#text", null, element, parent, null)
		parent.children[parent.index] = vnode
		parent.element.appendChild(element)
	}

	parent.index++

	return node
}

const appendElement = (element, parent) => {
	const component = parent.component
	if(component) {
		if(parent.id === 0) {
			parent.element.insertBefore(element, component.base.nextSibling)
		}
		else {
			const prev = parent.children[parent.index - 1]
			parent.element.insertBefore(element, prev.element.nextSibling)
		}
	}
	else {
		parent.element.appendChild(element)
	}
}

const setProp = function(element, name, value)
{
	if(name === "class") {
		element.className = value
	}
	else if(name === "style")
	{
		if(typeof value === "object") {
			const elementStyle = element.style
			for(let key in value) {
				elementStyle[key] = value[key]
			}
		}
		else {
			element.style.cssText = value
		}
	}
	else if(name[0] === "o" && name[1] === "n") {
		element[name] = value
	}
	else {
		element.setAttribute(name, value)
	}
}

const unsetProp = function(element, name)
{
	if(name === "class") {
		element.className = ""
	}
	else if(name === "style") {
		element.style.cssText = ""
	}
	else if(name[0] === "o" && name[1] === "n") {
		element[name] = null
	}
	else {
		element.removeAttribute(name)
	}
}

const render = function(component, parentElement)
{
	if(!bodyNode) {
		bodyNode = new VNode(0, "body", null, parentElement, null)
	}

	stackIndex = 0
	stack[0] = bodyNode

	componentVoid(component)

	if(bodyNode.index !== bodyNode.children.length) {
		removeUnusedNodes(bodyNode)
	}

	bodyNode.index = 0
}

const renderInstance = function(instance)
{
	const vnode = instance.vnode
	stackIndex = instance.depth
	stack[instance.depth] = vnode
	instance.render()
	instance.dirty = false

	if(vnode.index !== vnode.children.length) {
		removeUnusedNodes(vnode)
	}

	vnode.index = 0
}

const removeUnusedNodes = (node) =>
{
	const children = node.children
	for(let n = node.index; n < children.length; n++) {
		const child = children[n]
		removeNode(child)
	}

	children.length = node.index
}

const removeNode = (node) =>
{
	if(node.component) {
		removeComponent(node)
	}
	else {
		if(node.element.parentElement) {
			node.element.parentElement.removeChild(node.element)
		}
	}

	const children = node.children
	for(let n = 0; n < children.length; n++) {
		const child = children[n]
		removeNode(child)
	}

	node.children.length = 0
}

const removeAll = () => {
	removeUnusedNodes(bodyNode)
}

const getBodyNode = () => {
	return bodyNode
}

export {
	elementOpen,
	elementClose,
	elementVoid,
	element,
	componentVoid,
	text,
	render,
	renderInstance,
	removeAll,
	getBodyNode
}<commit_msg>Fix element appending<commit_after>import { VNode } from "./vnode"

const stack = new Array(64)
const components = {}
let stackIndex = 0
let bodyNode = null

const elementOpen = function(type, props, srcElement)
{
	const parent = stack[stackIndex]
	let prevNode = parent.children[parent.index]
	let vnode = prevNode

	if(!prevNode)
	{
		const element = srcElement || document.createElement(type)
		vnode = new VNode(parent.index, type, props, element, parent)

		if(props) {
			for(let key in props) {
				setProp(element, key, props[key])
			}
		}

		vnode.props = props
		parent.children[parent.index] = vnode
	}
	else
	{
		if(prevNode.type !== type)
		{
			const element = srcElement || document.createElement(type)

			if(prevNode.component)
			{
				vnode = new VNode(prevNode.index, type, props, element, parent)

				const children = prevNode.children
				prevNode.children = vnode.children
				vnode.children = children

				for(let n = 0; n < children.length; n++)
				{
					const child = children[n]
					child.parent = vnode

					if(!child.component) {
						element.appendChild(child.element)
					}
					else {
						element.appendChild(node.component.base)
						child.element = element
						appendChildren(node, element, child.children)
					}
				}

				removeComponent(prevNode)

				prevNode.parent.children[prevNode.index] = vnode
			}
			else
			{
				const prevElement = prevNode.element
				const children = prevNode.children
				appendElementChildren(element, children)

				prevElement.parentElement.replaceChild(element, prevElement)

				vnode.type = type
				vnode.element = element
			}

			if(props)
			{
				for(let key in props) {
					setProp(element, key, props[key])
				}
				vnode.props = props
			}
		}
		else
		{
			const element = prevNode.element
			const prevProps = prevNode.props

			if(props !== prevProps)
			{
				if(props)
				{
					if(prevProps)
					{
						for(let key in prevProps) {
							if(props[key] === undefined) {
								unsetProp(element, key)
							}
						}

						for(let key in props) {
							const value = props[key]
							if(value !== prevProps[key]) {
								setProp(element, key, value)
							}
						}
					}
					else
					{
						for(let key in props) {
							setProp(element, key, props[key])
						}
					}

					prevNode.props = props
				}
				else
				{
					if(prevProps)
					{
						for(let key in prevProps) {
							unsetProp(element, key)
						}

						prevNode.props = null
					}
				}
			}
		}
	}

	parent.index++
	stackIndex++
	stack[stackIndex] = vnode

	return vnode
}

const appendChildren = (node, element, children) => {
	for(let n = 0; n < children.length; n++) {
		const child = children[n]
		if(!child.component) {
			element.appendChild(child.element)
		}
		else {
			element.appendChild(node.component.base)
			child.element = element
			appendChildren(node, element, child.children)
		}
	}
}

const appendElementChildren = (element, children) =>
{
	for(let n = 0; n < children.length; n++) {
		const child = children[n]
		if(child.component) {
			child.element = element
			element.appendChild(child.component.base)
			appendElementChildren(element, child.children)
		}
		else {
			element.appendChild(child.element)
		}
	}
}

const elementClose = function(type)
{
	const node = stack[stackIndex]

	if(node.type !== type) {
		console.error(`(Element.close) Unexpected element closed: ${type} but was expecting: ${node.type}`)
	}

	if(node.index !== node.children.length) {
		removeUnusedNodes(node)
	}
	node.index = 0

	if(!node.element.parentElement)
	{
		const parent = node.parent
		const component = parent.component
		if(component) {
			if(node.id === 0) {
				parent.element.insertBefore(node.element, component.base.nextSibling)
			}
			else {
				const prev = parent.children[parent.index - 1]
				parent.element.insertBefore(node.element, prev.element.nextSibling)
			}
		}
		else {
			if(node.id === (parent.children.length - 1)) {
				parent.element.appendChild(node.element)
			}
			else {
				const next = parent.children[node.id + 1]
				if(next.component) {
					parent.element.insertBefore(node.element, next.component.base)
				}
				else {
					parent.element.insertBefore(node.element, next.element)
				}
			}
		}
	}

	stackIndex--
}

const elementVoid = function(type, props) {
	const node = elementOpen(type, props)
	elementClose(type)
	return node
}

const element = function(element, props) {
	const node = elementOpen(element.localName, props, element)
	elementClose(element.localName)
	return node
}

const componentVoid = (ctor, props) =>
{
	const parent = stack[stackIndex]
	const node = parent.children[parent.index]
	let component
	let vnode

	if(node)
	{
		component = node.component
		if(component)
		{
			if(component.constructor === ctor) {
				diffComponentProps(component, node, props)
				vnode = node
			}
			else
			{
				vnode = createComponent(ctor, node, parent)
				component = vnode.component

				const children = node.children
				node.children = vnode.children
				vnode.children = children

				vnode.element.insertBefore(component.base, node.component.base)
				removeComponent(node)

				for(let n = 0; n < children.length; n++) {
					children[n].parent = vnode
				}
				parent.children[vnode.id] = vnode

				if(props) {
					for(let key in props) {
						component[key] = props[key]
					}
				}
			}
		}
		else
		{
			vnode = createComponent(ctor, node, parent)
			component = vnode.component

			vnode.children.push(node)
			vnode.element.insertBefore(component.base, node.element)
			node.index = 0
			node.id = 0
			node.parent = vnode

			parent.children[vnode.id] = vnode

			if(props) {
				for(let key in props) {
					component[key] = props[key]
				}
			}
		}
	}
	else
	{
		vnode = createComponent(ctor, null, parent)
		component = vnode.component

		vnode.id = parent.children.length
		parent.children.push(vnode)
		parent.element.appendChild(component.base)

		parent.children[vnode.id] = vnode

		if(props) {
			for(let key in props) {
				component[key] = props[key]
			}
		}
	}

	if(component.mount) {
		component.mount()
	}

	parent.index++
	stackIndex++
	stack[stackIndex] = vnode

	component.depth = stackIndex
	component.render()
	component.dirty = false

	if(vnode.index !== vnode.children.length) {
		removeUnusedNodes(vnode)
	}

	vnode.index = 0
	stackIndex--

	return component
}

const diffComponentProps = (component, node, props) =>
{
	const prevProps = node.props

	if(props !== prevProps)
	{
		if(props)
		{
			if(prevProps)
			{
				for(let key in prevProps)
				{
					if(props[key] === undefined) {
						if(key[0] === "$") {
							component[key] = component.state[key.slice(1)]
						}
						else {
							component[key] = null
						}
					}
				}

				for(let key in props) {
					const value = props[key]
					if(component[key] !== value) {
						component[key] = value
					}
				}
			}
			else
			{
				for(let key in props) {
					component[key] = props[key]
				}
			}

			node.props = props
		}
		else if(prevProps)
		{
			for(let key in prevProps) {
				if(key[0] === "$") {
					component[key] = component.state[ket.slice(1)]
				}
				else {
					component[key] = null
				}
			}

			node.props = null
		}
	}
}

const createComponent = (ctor, node, parent) =>
{
	const buffer = components[ctor.prototype.__componentIndex]
	let component = buffer ? buffer.pop() : null
	let vnode

	if(!component) {
		component = new ctor()
		vnode = new VNode(0, null, null, parent.element, parent)
		vnode.component = component
		component.vnode = vnode
	}
	else {
		vnode = component.vnode
	}

	if(node) {
		vnode.id = node.id
	}
	vnode.parent = parent
	vnode.element = parent.element
	return vnode
}

const removeComponent = (node) =>
{
	const component = node.component
	const buffer = components[component.__componentIndex]
	if(buffer) {
		buffer.push(component)
	}
	else {
		components[component.__componentIndex] = [ component ]
	}

	component.remove()
	component.base.remove()
	removeUnusedNodes(node)
}

const text = (text) =>
{
	const parent = stack[stackIndex]
	let node = parent.children[parent.index]

	if(node)
	{
		if(node.type === "#text") {
			if(node.element.nodeValue !== text) {
				node.element.nodeValue = text
			}
		}
		else
		{
			const element = document.createTextNode(text)

			if(node.component) {
				const vnodeNew = new VNode(parent.index, "#text", null, element, parent, null)
				parent.children[parent.index] = vnodeNew
				node.element.insertBefore(element, node.component.base)
				node.index = 0
				removeComponent(node)
			}
			else {
				node.element.parentElement.replaceChild(element, node.element)
				node.type = "#text"
				node.element = element
			}
		}
	}
	else {
		const element = document.createTextNode(text)
		const vnode = new VNode(parent.index, "#text", null, element, parent, null)
		parent.children[parent.index] = vnode
		parent.element.appendChild(element)
	}

	parent.index++

	return node
}

const setProp = function(element, name, value)
{
	if(name === "class") {
		element.className = value
	}
	else if(name === "style")
	{
		if(typeof value === "object") {
			const elementStyle = element.style
			for(let key in value) {
				elementStyle[key] = value[key]
			}
		}
		else {
			element.style.cssText = value
		}
	}
	else if(name[0] === "o" && name[1] === "n") {
		element[name] = value
	}
	else {
		element.setAttribute(name, value)
	}
}

const unsetProp = function(element, name)
{
	if(name === "class") {
		element.className = ""
	}
	else if(name === "style") {
		element.style.cssText = ""
	}
	else if(name[0] === "o" && name[1] === "n") {
		element[name] = null
	}
	else {
		element.removeAttribute(name)
	}
}

const render = function(component, parentElement)
{
	if(!bodyNode) {
		bodyNode = new VNode(0, "body", null, parentElement, null)
	}

	stackIndex = 0
	stack[0] = bodyNode

	componentVoid(component)

	if(bodyNode.index !== bodyNode.children.length) {
		removeUnusedNodes(bodyNode)
	}

	bodyNode.index = 0
}

const renderInstance = function(instance)
{
	const vnode = instance.vnode
	stackIndex = instance.depth
	stack[instance.depth] = vnode
	instance.render()
	instance.dirty = false

	if(vnode.index !== vnode.children.length) {
		removeUnusedNodes(vnode)
	}

	vnode.index = 0
}

const removeUnusedNodes = (node) =>
{
	const children = node.children
	for(let n = node.index; n < children.length; n++) {
		const child = children[n]
		removeNode(child)
	}

	children.length = node.index
}

const removeNode = (node) =>
{
	if(node.component) {
		removeComponent(node)
	}
	else {
		if(node.element.parentElement) {
			node.element.parentElement.removeChild(node.element)
		}
	}

	const children = node.children
	for(let n = 0; n < children.length; n++) {
		const child = children[n]
		removeNode(child)
	}

	node.children.length = 0
}

const removeAll = () => {
	removeUnusedNodes(bodyNode)
}

const getBodyNode = () => {
	return bodyNode
}

export {
	elementOpen,
	elementClose,
	elementVoid,
	element,
	componentVoid,
	text,
	render,
	renderInstance,
	removeAll,
	getBodyNode
}<|endoftext|>


<commit_before>/*
 * grunt-contrib-handlebars
 * http://gruntjs.com/
 *
 * Copyright (c) 2014 Tim Branyen, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      all: [
        'Gruntfile.js',
        'tasks/*.js',
        '<%= nodeunit.tests %>'
      ],
      options: {
        jshintrc: '.jshintrc'
      }
    },

    jscs: {
      src: ['tasks/**/*.js', 'test/*.js', 'Gruntfile.js'],
      options: {
        config: '.jscsrc'
      }
    },

    // Before generating any new files, remove any previously-created files.
    clean: {
      tests: ['tmp']
    },

    // Configuration to be run (and then tested).
    handlebars: {
      compile: {
        options: {
          namespace: 'JST'
        },
        files: {
          'tmp/handlebars.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      compileNode: {
        options: {
          namespace: 'JST',
          node: true
        },
        files: {
          'tmp/handlebars-node.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      nowrap: {
        options: {
          namespace: 'JST',
          wrapped: false
        },
        files: {
          'tmp/handlebarsnowrap.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      uglyfile: {
        files: {
          'tmp/uglyfile.js': ['test/fixtures/*bad-filename*']
        }
      },
      ns_nested: {
        options: {
          namespace: 'MyApp.JST.Main'
        },
        files: {
          'tmp/ns_nested.js': ['test/fixtures/basic.hbs']
        }
      },
      ns_nested_this: {
        options: {
          namespace: 'this.MyApp.JST.Main'
        },
        files: {
          'tmp/ns_nested_this.js': ['test/fixtures/basic.hbs']
        }
      },
      no_namespace: {
        options: {
          namespace: false
        },
        files: {
          'tmp/no_namespace.js': ['test/fixtures/basic.hbs']
        }
      },
      processcontent: {
        options: {
          processContent: function(content) {
            content = content.replace(/^[\x20\t]+/mg, '').replace(/[\x20\t]+$/mg, '');
            content = content.replace(/^[\r\n]+/, '').replace(/[\r\n]+$/, '');
            return content;
          }
        },
        files: {
          'tmp/processcontent.js': ['test/fixtures/has-spaces.hbs']
        }
      },
      process_ast: {
        options: {
          processAST: function(ast) {
            ast.statements.forEach(function(statement, i) {
              if (statement.type === 'partial') {
                ast.statements[i] = {type: 'content', string: 'Fooville'};
              }
            });
            return ast;
          }
        },
        files: {
          'tmp/process_ast.js': ['test/fixtures/one.hbs']
        }
      },
      amd_compile: {
        options: {
          amd: true
        },
        files: {
          'tmp/amd_compile.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_direct: {
        options: {
          amd: true,
          namespace: false
        },
        files: {
          'tmp/amd_compile_direct.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_string_path: {
        options: {
          amd: 'lib/handlebars',
          namespace: false
        },
        files: {
          'tmp/amd_compile_string_path.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_string_deps: {
        options: {
          amd: 'handlebars\', \'handlebars.helpers',
          namespace: false
        },
        files: {
          'tmp/amd_compile_string_deps.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_array: {
        options: {
          amd: ['handlebars', 'handlebars.helpers'],
          namespace: false
        },
        files: {
          'tmp/amd_compile_array.js': ['test/fixtures/amd.html']
        }
      },
      amd_namespace: {
        options: {
          amd: ['handlebars', 'handlebars.helpers'],
          namespace: 'foo'
        },
        files: {
          'tmp/amd_namespace.js': ['test/fixtures/amd.html']
        }
      },
      amd_partials_use_namespace: {
        options: {
          amd: ['handlebars'],
          partialsUseNamespace: true
        },
        files: {
          'tmp/amd_partials_use_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      amd_partials_no_namespace: {
        options: {
          amd: ['handlebars'],
          partialsUseNamespace: false
        },
        files: {
          'tmp/amd_partials_no_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      commonjs_compile: {
        options: {
          commonjs: true
        },
        files: {
          'tmp/commonjs_compile.js': ['test/fixtures/commonjs.html']
        }
      },
      commonjs_compile_direct: {
        options: {
          commonjs: true,
          namespace: false
        },
        files: {
          'tmp/commonjs_compile_direct.js': ['test/fixtures/commonjs.html']
        }
      },
      custom_separator: {
        options: {
          separator: ';;;;;'
        },
        files: {
          'tmp/custom_separator.js': ['test/fixtures/basic.hbs']
        }
      },
      processname: {
        options: {
          processName: function(filename) {
            return filename.toUpperCase();
          }
        },
        files: {
          'tmp/processname.js': ['test/fixtures/basic.hbs']
        }
      },
      process_partial_name: {
        options: {
          processPartialName: function(filepath) {
            return filepath.replace('test/fixtures/_weird_prefix_', '').replace('.hbs', '');
          }
        },
        files: {
          'tmp/process_partial_name.js': ['test/fixtures/_weird_prefix_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partial_regex: {
        options: {
          partialRegex: /^par_/,
          processPartialName: function(filepath) {
            return filepath.replace('test/fixtures/par_', '').replace('.hbs', '');
          }
        },
        files: {
          'tmp/partial_regex.js': ['test/fixtures/par_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partials_use_namespace: {
        options: {
          partialsUseNamespace: true
        },
        files: {
          'tmp/partials_use_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partials_path_regex: {
        options: {
          partialRegex: /.*/,
          partialsPathRegex: /\/partials\//
        },
        files: {
          'tmp/partials_path_regex.js': ['test/fixtures/partials/partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      unknown_helpers: {
        files: {
          'tmp/unknown_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      known_helpers: {
        options: {
          compilerOptions: {
            knownHelpers: {
              'my-helper': true
            }
          }
        },
        files: {
          'tmp/known_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      only_known_helpers: {
        options: {
          compilerOptions: {
            knownHelpers: {
              'my-helper': true,
              'another-helper': true
            },
            knownHelpersOnly: true
          }
        },
        files: {
          'tmp/only_known_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      namespace_as_function: {
        options: {
          processName: function(filename) {
            return filename.replace(/.*\/(\w+)\.hbs/, '$1');
          },
          namespace: function(filename) {
            var names = filename.replace(/.*modules\/(.*)(\/\w+\.hbs)/, '$1');
            return 'JST.' + names.split('/').join('.');
          }
        },
        files: {
          'tmp/namespace_as_function.js' : ['test/fixtures/modules/**/*.hbs']
        }
      }
    },
    // Unit tests.
    nodeunit: {
      tests: ['test/*_test.js']
    }
  });

  // Actually load this plugin's task(s).
  grunt.loadTasks('tasks');

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-internal');
  grunt.loadNpmTasks('grunt-jscs');

  // Whenever the "test" task is run, first clean the "tmp" dir, then run this
  // plugin's task(s), then test the result.
  grunt.registerTask('test', ['jshint', 'jscs', 'clean', 'handlebars', 'nodeunit']);

  // By default, lint and run all tests.
  grunt.registerTask('default', ['test', 'build-contrib']);

};
<commit_msg>Add test for amd with namespace<commit_after>/*
 * grunt-contrib-handlebars
 * http://gruntjs.com/
 *
 * Copyright (c) 2014 Tim Branyen, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      all: [
        'Gruntfile.js',
        'tasks/*.js',
        '<%= nodeunit.tests %>'
      ],
      options: {
        jshintrc: '.jshintrc'
      }
    },

    jscs: {
      src: ['tasks/**/*.js', 'test/*.js', 'Gruntfile.js'],
      options: {
        config: '.jscsrc'
      }
    },

    // Before generating any new files, remove any previously-created files.
    clean: {
      tests: ['tmp']
    },

    // Configuration to be run (and then tested).
    handlebars: {
      compile: {
        options: {
          namespace: 'JST'
        },
        files: {
          'tmp/handlebars.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      compileNode: {
        options: {
          namespace: 'JST',
          node: true
        },
        files: {
          'tmp/handlebars-node.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      nowrap: {
        options: {
          namespace: 'JST',
          wrapped: false
        },
        files: {
          'tmp/handlebarsnowrap.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      uglyfile: {
        files: {
          'tmp/uglyfile.js': ['test/fixtures/*bad-filename*']
        }
      },
      ns_nested: {
        options: {
          namespace: 'MyApp.JST.Main'
        },
        files: {
          'tmp/ns_nested.js': ['test/fixtures/basic.hbs']
        }
      },
      ns_nested_this: {
        options: {
          namespace: 'this.MyApp.JST.Main'
        },
        files: {
          'tmp/ns_nested_this.js': ['test/fixtures/basic.hbs']
        }
      },
      no_namespace: {
        options: {
          namespace: false
        },
        files: {
          'tmp/no_namespace.js': ['test/fixtures/basic.hbs']
        }
      },
      processcontent: {
        options: {
          processContent: function(content) {
            content = content.replace(/^[\x20\t]+/mg, '').replace(/[\x20\t]+$/mg, '');
            content = content.replace(/^[\r\n]+/, '').replace(/[\r\n]+$/, '');
            return content;
          }
        },
        files: {
          'tmp/processcontent.js': ['test/fixtures/has-spaces.hbs']
        }
      },
      process_ast: {
        options: {
          processAST: function(ast) {
            ast.statements.forEach(function(statement, i) {
              if (statement.type === 'partial') {
                ast.statements[i] = {type: 'content', string: 'Fooville'};
              }
            });
            return ast;
          }
        },
        files: {
          'tmp/process_ast.js': ['test/fixtures/one.hbs']
        }
      },
      amd_compile: {
        options: {
          amd: true
        },
        files: {
          'tmp/amd_compile.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_direct: {
        options: {
          amd: true,
          namespace: false
        },
        files: {
          'tmp/amd_compile_direct.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_string_path: {
        options: {
          amd: 'lib/handlebars',
          namespace: false
        },
        files: {
          'tmp/amd_compile_string_path.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_string_deps: {
        options: {
          amd: 'handlebars\', \'handlebars.helpers',
          namespace: false
        },
        files: {
          'tmp/amd_compile_string_deps.js': ['test/fixtures/amd.html']
        }
      },
      amd_compile_array: {
        options: {
          amd: ['handlebars', 'handlebars.helpers'],
          namespace: false
        },
        files: {
          'tmp/amd_compile_array.js': ['test/fixtures/amd.html']
        }
      },
      amd_namespace: {
        options: {
          amd: ['handlebars', 'handlebars.helpers'],
          namespace: 'foo'
        },
        files: {
          'tmp/amd_namespace.js': ['test/fixtures/amd.html']
        }
      },
      amd_partials_use_namespace: {
        options: {
          amd: ['handlebars'],
          partialsUseNamespace: true
        },
        files: {
          'tmp/amd_partials_use_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      amd_partials_no_namespace: {
        options: {
          amd: ['handlebars'],
          partialsUseNamespace: false
        },
        files: {
          'tmp/amd_partials_no_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      commonjs_compile: {
        options: {
          commonjs: true
        },
        files: {
          'tmp/commonjs_compile.js': ['test/fixtures/commonjs.html']
        }
      },
      commonjs_compile_direct: {
        options: {
          commonjs: true,
          namespace: false
        },
        files: {
          'tmp/commonjs_compile_direct.js': ['test/fixtures/commonjs.html']
        }
      },
      custom_separator: {
        options: {
          separator: ';;;;;'
        },
        files: {
          'tmp/custom_separator.js': ['test/fixtures/basic.hbs']
        }
      },
      processname: {
        options: {
          processName: function(filename) {
            return filename.toUpperCase();
          }
        },
        files: {
          'tmp/processname.js': ['test/fixtures/basic.hbs']
        }
      },
      process_partial_name: {
        options: {
          processPartialName: function(filepath) {
            return filepath.replace('test/fixtures/_weird_prefix_', '').replace('.hbs', '');
          }
        },
        files: {
          'tmp/process_partial_name.js': ['test/fixtures/_weird_prefix_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partial_regex: {
        options: {
          partialRegex: /^par_/,
          processPartialName: function(filepath) {
            return filepath.replace('test/fixtures/par_', '').replace('.hbs', '');
          }
        },
        files: {
          'tmp/partial_regex.js': ['test/fixtures/par_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partials_use_namespace: {
        options: {
          partialsUseNamespace: true
        },
        files: {
          'tmp/partials_use_namespace.js': ['test/fixtures/_partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      partials_path_regex: {
        options: {
          partialRegex: /.*/,
          partialsPathRegex: /\/partials\//
        },
        files: {
          'tmp/partials_path_regex.js': ['test/fixtures/partials/partial.hbs', 'test/fixtures/one.hbs']
        }
      },
      unknown_helpers: {
        files: {
          'tmp/unknown_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      known_helpers: {
        options: {
          compilerOptions: {
            knownHelpers: {
              'my-helper': true
            }
          }
        },
        files: {
          'tmp/known_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      only_known_helpers: {
        options: {
          compilerOptions: {
            knownHelpers: {
              'my-helper': true,
              'another-helper': true
            },
            knownHelpersOnly: true
          }
        },
        files: {
          'tmp/only_known_helpers.js': ['test/fixtures/uses_helpers.hbs']
        }
      },
      namespace_as_function: {
        options: {
          processName: function(filename) {
            return filename.replace(/.*\/(\w+)\.hbs/, '$1');
          },
          namespace: function(filename) {
            var names = filename.replace(/.*modules\/(.*)(\/\w+\.hbs)/, '$1');
            return 'JST.' + names.split('/').join('.');
          }
        },
        files: {
          'tmp/namespace_as_function.js' : ['test/fixtures/modules/**/*.hbs']
        }
      }
    },
    // Unit tests.
    nodeunit: {
      tests: ['test/*_test.js']
    }
  });

  // Actually load this plugin's task(s).
  grunt.loadTasks('tasks');

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-internal');
  grunt.loadNpmTasks('grunt-jscs');

  // Whenever the "test" task is run, first clean the "tmp" dir, then run this
  // plugin's task(s), then test the result.
  grunt.registerTask('test', ['jshint', 'jscs', 'clean', 'handlebars', 'nodeunit']);

  // By default, lint and run all tests.
  grunt.registerTask('default', ['test', 'build-contrib']);

};
<|endoftext|>


<commit_before>var gulp = require("gulp");
var nuget = require("gulp-nuget");
var request = require("request");
var fs = require("fs");
var rimraf = require("gulp-rimraf");
var rename = require("gulp-rename");

gulp.task('nuget-download', function(done) {
    if(fs.existsSync('nuget.exe')) {
        done();
        return;
    }

    request.get('http://nuget.org/nuget.exe')
        .pipe(fs.createWriteStream('nuget.exe'))
        .on('close', done);
});

gulp.task("default",["nuget-download"],function task()
{

  gulp.src("README.md")
  .pipe(rename("readme.txt"))
  .pipe(gulp.dest("./nugetPackage/"));

  gulp.src(["build/**","tools/**"])
  .pipe(nuget.pack(
    {
      nuspec:"NodeJsMsBuild.nuspec",
      nuget: "nuget.exe",
      version: "1.0.0"
    }))
  .pipe(gulp.dest("NodeJsMsBuild.nupkg"));
});

gulp.task("clean",function clean(cb)
{
  gulp.src(["*.exe","*.txt","node_modules/**","NodeJsMsBuild.nupkg/**","publish"],{read:false}).pipe(rimraf());
})
<commit_msg>Let's read version from nuspec file.<commit_after>var gulp = require("gulp");
var nuget = require("gulp-nuget");
var request = require("request");
var fs = require("fs");
var rimraf = require("gulp-rimraf");
var rename = require("gulp-rename");

gulp.task('nuget-download', function(done) {
    if(fs.existsSync('nuget.exe')) {
        done();
        return;
    }

    request.get('http://nuget.org/nuget.exe')
        .pipe(fs.createWriteStream('nuget.exe'))
        .on('close', done);
});

gulp.task("default",["nuget-download"],function task()
{

  gulp.src("README.md")
  .pipe(rename("readme.txt"))
  .pipe(gulp.dest("./nugetPackage/"));

  gulp.src(["build/**","tools/**"])
  .pipe(nuget.pack(
    {
      nuspec:"NodeJsMsBuild.nuspec",
      nuget: "nuget.exe",
    }))
  .pipe(gulp.dest("NodeJsMsBuild.nupkg"));
});

gulp.task("clean",function clean(cb)
{
  gulp.src(["*.exe","*.txt","node_modules/**","NodeJsMsBuild.nupkg/**","publish"],{read:false}).pipe(rimraf());
})
<|endoftext|>


<commit_before>Date.prototype.pad = function (n) {
	return (n < 10) ? '0' + n : n;
};

Date.prototype.toISODateTime = function () {
	return this.getFullYear() + '-'
		+ this.pad(this.getMonth() + 1) + '-'
		+ this.pad(this.getDate()) + 'T'
		+ this.pad(this.getHours()) + ':'
		+ this.pad(this.getMinutes()) + ':'
		+ this.pad(this.getSeconds());
};

var util = require('util');
var fs = require('fs');
var path = require('path');
var cron = require('cron');
var logger = require('../lib/logger');
var messages = require('../lib/messages');
var plugins = require('../lib/plugins');
var Yammer = require('../lib/yammer').Yammer;

var cwd = process.cwd();
var config = load_config();
var yammer_account = config.yammer[config.yammer_account];

logger.setPrefix(function () {
	var d = new Date().toISODateTime();
	return '[' + d + '] ';
});

process.on('xuncaughtException', function (ex) {

	logger.error('uncaught exception: ' + ex);
});

var y = new Yammer(yammer_account.email, yammer_account.api.consumer_key, yammer_account.api.consumer_secret, function (authorizeURI, continueCallback) {
	util.puts(authorizeURI);
	util.puts('Please visit the link above, \nauthorize the request and enter the verifier code: ');

	process.stdin.resume();
	process.stdin.setEncoding('utf8');
	process.stdin.on('data', function(chunk) {
		process.stdin.pause();
		continueCallback(chunk.toString());
	});
});

messages.init(y.profileDir());
plugins.load(y, config, messages, cron, logger, cwd + '/plugins');

y.on('error', function (error) {
	logger.error(JSON.stringify(error));
});

y.on('loggedon', function () {
	logger.info('logged on');
	y.loadUsers();
});

y.on('usersloaded', function () {
	logger.info('users loaded');
	y.pollMessages();
	y.pollPrivateMessages();
});

y.logon();

//
function load_config () {
	if (path.existsSync(cwd + '/config.json')) {
		var config_data = fs.readFileSync(cwd + '/config.json', 'utf8');
		return JSON.parse(config_data);
	}

	util.puts('Error: Configuration file not found');
	process.exit(1);
}
<commit_msg>Fixed uncaught exceptions<commit_after>Date.prototype.pad = function (n) {
	return (n < 10) ? '0' + n : n;
};

Date.prototype.toISODateTime = function () {
	return this.getFullYear() + '-'
		+ this.pad(this.getMonth() + 1) + '-'
		+ this.pad(this.getDate()) + 'T'
		+ this.pad(this.getHours()) + ':'
		+ this.pad(this.getMinutes()) + ':'
		+ this.pad(this.getSeconds());
};

var util = require('util');
var fs = require('fs');
var path = require('path');
var cron = require('cron');
var logger = require('../lib/logger');
var messages = require('../lib/messages');
var plugins = require('../lib/plugins');
var Yammer = require('../lib/yammer').Yammer;

var cwd = process.cwd();
var config = load_config();
var yammer_account = config.yammer[config.yammer_account];

logger.setPrefix(function () {
	var d = new Date().toISODateTime();
	return '[' + d + '] ';
});

process.on('uncaughtException', function (ex) {
	logger.error('uncaught exception: ' + ex);
});

var y = new Yammer(yammer_account.email, yammer_account.api.consumer_key, yammer_account.api.consumer_secret, function (authorizeURI, continueCallback) {
	util.puts(authorizeURI);
	util.puts('Please visit the link above, \nauthorize the request and enter the verifier code: ');

	process.stdin.resume();
	process.stdin.setEncoding('utf8');
	process.stdin.on('data', function(chunk) {
		process.stdin.pause();
		continueCallback(chunk.toString());
	});
});

messages.init(y.profileDir());
plugins.load(y, config, messages, cron, logger, cwd + '/plugins');

y.on('error', function (error) {
	logger.error(JSON.stringify(error));
});

y.on('loggedon', function () {
	logger.info('logged on');
	y.loadUsers();
});

y.on('usersloaded', function () {
	logger.info('users loaded');
	y.pollMessages();
	y.pollPrivateMessages();
});

y.logon();

//
function load_config () {
	if (path.existsSync(cwd + '/config.json')) {
		var config_data = fs.readFileSync(cwd + '/config.json', 'utf8');
		return JSON.parse(config_data);
	}

	util.puts('Error: Configuration file not found');
	process.exit(1);
}
<|endoftext|>


<commit_before>'use strict';

var kiwiWeb = angular.module('kiwiWeb', [
  'ui.router',
  'ngAnimate',
  'ngSanitize',
  'languages'
]);

kiwiWeb.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

  // Routing Configuration

  $urlRouterProvider.otherwise('/home');

  $stateProvider
  .state('home', {
    url: '/home',
    templateUrl: '/home/home.html',
    controller: 'HomeCtrl'
  })
  .state('services', {
    url: '/services',
    templateUrl: '/services/services/services.html',
    controller: 'ServicesCtrl'
  })
  .state('residential', {
    url: '/residential',
    templateUrl: '/services/residential/residential.html',
    controller: 'ResidentialCtrl'
  })
  .state('business', {
    url: '/business',
    templateUrl: '/services/business/business.html',
    controller: 'BusinessCtrl'
  })
  .state('dedicated', {
    url: '/dedicated',
    templateUrl: '/services/dedicated/dedicated.html',
    controller: 'DedicatedCtrl'
  })
  .state('metromesh', {
    url: '/metromesh',
    templateUrl: '/services/metromesh/metromesh.html',
    controller: 'MetroMeshCtrl'
  })
  .state('faq', {
    url: '/faq/:section',
    templateUrl: '/services/faq/faq.html',
    controller: 'FaqCtrl'
  })
  .state('sales', {
    url: '/sales/:service',
    templateUrl: '/services/sales/sales.html',
    controller: 'SalesCtrl'
  })
  .state('about', {
    url: '/about',
    templateUrl: '/company/about/about.html',
    controller: 'AboutCtrl'
  })
  .state('concesion', {
    url: '/concesion',
    templateUrl: '/company/concesion/concesion.html',
    controller: 'ConcesionCtrl'
  })
  .state('careers', {
    url: '/careers',
    templateUrl: '/company/careers/careers.html',
    controller: 'CareersCtrl'
  })
  .state('apply', {
    url: '/apply/:role',
    templateUrl: '/company/apply/apply.html',
    controller: 'ApplyCtrl'
  })
  .state('stories', {
    url: '/stories',
    templateUrl: '/company/stories/stories.html',
    controller: 'StoriesCtrl'
  })
  .state('privacy', {
    url: '/privacy',
    templateUrl: '/company/privacy/privacy.html',
    controller: 'PrivacyCtrl'
  })
  .state('unsubscribe', {
    url: '/unsubscribe',
    templateUrl: '/company/unsubscribe/unsubscribe.html',
    controller: 'UnsubscribeCtrl'
  })
  .state('contact', {
    url: '/contact',
    templateUrl: '/contact/contact.html',
    controller: 'ContactCtrl'
  })
  .state('dashboard', {
    abstract: true,
    url: '/dashboard',
    templateUrl: '/customer/dashboard/dashboard.html',
    controller: 'DashboardCtrl'
  })
  .state('dashboard.anon', {
    url: '/anon',
    views: {
      'signup' : {
        templateUrl: '/components/signup/signup.html',
        controller: 'SignUpCtrl'
      },
      'signin' : {
        templateUrl: '/components/signin/signin.html',
        controller: 'SignInCtrl'
      }
    }
  })
  .state('pay', {
    url: '/pay',
    templateUrl: '/customer/pay/pay.html',
    controller: 'PayCtrl'
  })
  .state('refer', {
    abstract: true,
    url: '/refer',
    templateUrl: '/customer/refer/refer.html',
    controller: 'ReferCtrl'
  })
  .state('refer.anon', {
    url: '/anon',
    views: {
      'signup' : {
        templateUrl: '/components/signup/signup.html',
        controller: 'SignUpCtrl'
      },
      'signin' : {
        templateUrl: '/components/signin/signin.html',
        controller: 'SignInCtrl'
      }
    }
  })
  .state('support', {
    url: '/support',
    templateUrl: '/support/support/support.html',
    controller: 'SupportCtrl'
  })
  .state('status', {
    url: '/status',
    templateUrl: '/support/status/status.html',
    controller: 'StatusCtrl'
  });
}]);
<commit_msg>Uses templateProvider to load templates from js.<commit_after>'use strict';

var kiwiWeb = angular.module('kiwiWeb', [
  'ui.router',
  'ngAnimate',
  'ngSanitize',
  'templates',
  'languages'
]);

kiwiWeb.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

  // Routing Configuration

  $urlRouterProvider.otherwise('/home');

  $stateProvider
  .state('home', {
    url: '/home',
    templateProvider: function ($templateCache) {
      return $templateCache.get('home.html');
    },
    controller: 'HomeCtrl'
  })
  .state('services', {
    url: '/services',
    templateProvider: function ($templateCache) {
      return $templateCache.get('services/services.html');
    },
    controller: 'ServicesCtrl'
  })
  .state('residential', {
    url: '/residential',
    templateProvider: function ($templateCache) {
      return $templateCache.get('residential/residential.html');
    },
    controller: 'ResidentialCtrl'
  })
  .state('business', {
    url: '/business',
    templateProvider: function ($templateCache) {
      return $templateCache.get('business/business.html');
    },
    controller: 'BusinessCtrl'
  })
  .state('dedicated', {
    url: '/dedicated',
    templateProvider: function ($templateCache) {
      return $templateCache.get('dedicated/dedicated.html');
    },
    controller: 'DedicatedCtrl'
  })
  .state('metromesh', {
    url: '/metromesh',
    templateProvider: function ($templateCache) {
      return $templateCache.get('metromesh/metromesh.html');
    },
    controller: 'MetroMeshCtrl'
  })
  .state('faq', {
    url: '/faq/:section',
    templateProvider: function ($templateCache) {
      return $templateCache.get('faq/faq.html');
    },
    controller: 'FaqCtrl'
  })
  .state('sales', {
    url: '/sales/:service',
    templateProvider: function ($templateCache) {
      return $templateCache.get('sales/sales.html');
    },
    controller: 'SalesCtrl'
  })
  .state('about', {
    url: '/about',
    templateProvider: function ($templateCache) {
      return $templateCache.get('about/about.html');
    },
    controller: 'AboutCtrl'
  })
  .state('concesion', {
    url: '/concesion',
    templateProvider: function ($templateCache) {
      return $templateCache.get('concesion/concesion.html');
    },
    controller: 'ConcesionCtrl'
  })
  .state('careers', {
    url: '/careers',
    templateProvider: function ($templateCache) {
      return $templateCache.get('careers/careers.html');
    },
    controller: 'CareersCtrl'
  })
  .state('apply', {
    url: '/apply/:role',
    templateProvider: function ($templateCache) {
      return $templateCache.get('apply/apply.html');
    },
    controller: 'ApplyCtrl'
  })
  .state('stories', {
    url: '/stories',
    templateProvider: function ($templateCache) {
      return $templateCache.get('stories/stories.html');
    },
    controller: 'StoriesCtrl'
  })
  .state('privacy', {
    url: '/privacy',
    templateProvider: function ($templateCache) {
      return $templateCache.get('privacy/privacy.html');
    },
    controller: 'PrivacyCtrl'
  })
  .state('unsubscribe', {
    url: '/unsubscribe',
    templateProvider: function ($templateCache) {
      return $templateCache.get('unsubscribe/unsubscribe.html');
    },
    controller: 'UnsubscribeCtrl'
  })
  .state('contact', {
    url: '/contact',
    templateProvider: function ($templateCache) {
      return $templateCache.get('contact.html');
    },
    controller: 'ContactCtrl'
  })
  .state('dashboard', {
    abstract: true,
    url: '/dashboard',
    templateProvider: function ($templateCache) {
      return $templateCache.get('dashboard/dashboard.html');
    },
    controller: 'DashboardCtrl'
  })
  .state('dashboard.anon', {
    url: '/anon',
    views: {
      'signup' : {
        templateProvider: function ($templateCache) {
          return $templateCache.get('signup/signup.html');
        },
        controller: 'SignUpCtrl'
      },
      'signin' : {
        templateProvider: function ($templateCache) {
          return $templateCache.get('signin/signin.html');
        },
        controller: 'SignInCtrl'
      }
    }
  })
  .state('pay', {
    url: '/pay',
    templateProvider: function ($templateCache) {
      return $templateCache.get('pay/pay.html');
    },
    controller: 'PayCtrl'
  })
  .state('refer', {
    abstract: true,
    url: '/refer',
    templateProvider: function ($templateCache) {
      return $templateCache.get('refer/refer.html');
    },
    controller: 'ReferCtrl'
  })
  .state('refer.anon', {
    url: '/anon',
    views: {
      'signup' : {
        templateProvider: function ($templateCache) {
          return $templateCache.get('signup/signup.html');
        },
        controller: 'SignUpCtrl'
      },
      'signin' : {
        templateProvider: function ($templateCache) {
          return $templateCache.get('signin/signin.html');
        },
        controller: 'SignInCtrl'
      }
    }
  })
  .state('support', {
    url: '/support',
    templateProvider: function ($templateCache) {
      return $templateCache.get('support/support.html');
    },
    controller: 'SupportCtrl'
  })
  .state('status', {
    url: '/status',
    templateProvider: function ($templateCache) {
      return $templateCache.get('status/status.html');
    },
    controller: 'StatusCtrl'
  });
}]);
<|endoftext|>


<commit_before>const browser = require("./browser")

const domIndex = exports.domIndex = function(node) {
  for (var index = 0;; index++) {
    node = node.previousSibling
    if (!node) return index
  }
}

exports.parentNode = function(node) {
  let parent = node.parentNode
  return parent && parent.nodeType == 11 ? parent.host : parent
}

exports.textRange = function(node, from, to) {
  let range = document.createRange()
  range.setEnd(node, to == null ? node.nodeValue.length : to)
  range.setStart(node, from || 0)
  return range
}

// Scans forward and backward through DOM positions equivalent to the
// given one to see if the two are in the same place (i.e. after a
// text node vs at the end of that text node)
exports.isEquivalentPosition = function(node, off, targetNode, targetOff) {
  return targetNode && (scanFor(node, off, targetNode, targetOff, -1) ||
                        scanFor(node, off, targetNode, targetOff, 1))
}

function scanFor(node, off, targetNode, targetOff, dir) {
  for (;;) {
    if (node == targetNode && off == targetOff) return true
    if (off == (dir < 0 ? 0 : nodeSize(node))) {
      let parent = node.parentNode
      if (parent.nodeType != 1 || hasBlockDesc(parent)) return false
      off = domIndex(node) + (dir < 0 ? 0 : 1)
      node = parent
    } else if (node.nodeType == 1) {
      node = node.childNodes[off + (dir < 0 ? -1 : 0)]
      off = dir < 0 ? nodeSize(node) : 0
    } else {
      return false
    }
  }
}

function nodeSize(node) {
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
}

function hasBlockDesc(dom) {
  let desc = dom.pmViewDesc
  return desc && desc.node && desc.node.isBlock
}

// Work around Chrome issue https://bugs.chromium.org/p/chromium/issues/detail?id=447523
// (isCollapsed inappropriately returns true in shadow dom)
exports.selectionCollapsed = function(domSel) {
  let collapsed = domSel.isCollapsed
  if (collapsed && browser.chrome && domSel.rangeCount && !domSel.getRangeAt(0).collapsed)
    collapsed = false
  return collapsed
}
<commit_msg>Fix bug in selection-change-avoiding check<commit_after>const browser = require("./browser")

const domIndex = exports.domIndex = function(node) {
  for (var index = 0;; index++) {
    node = node.previousSibling
    if (!node) return index
  }
}

exports.parentNode = function(node) {
  let parent = node.parentNode
  return parent && parent.nodeType == 11 ? parent.host : parent
}

exports.textRange = function(node, from, to) {
  let range = document.createRange()
  range.setEnd(node, to == null ? node.nodeValue.length : to)
  range.setStart(node, from || 0)
  return range
}

// Scans forward and backward through DOM positions equivalent to the
// given one to see if the two are in the same place (i.e. after a
// text node vs at the end of that text node)
exports.isEquivalentPosition = function(node, off, targetNode, targetOff) {
  return targetNode && (scanFor(node, off, targetNode, targetOff, -1) ||
                        scanFor(node, off, targetNode, targetOff, 1))
}

function scanFor(node, off, targetNode, targetOff, dir) {
  for (;;) {
    if (node == targetNode && off == targetOff) return true
    if (off == (dir < 0 ? 0 : nodeSize(node))) {
      let parent = node.parentNode
      if (parent.nodeType != 1 || hasBlockDesc(node)) return false
      off = domIndex(node) + (dir < 0 ? 0 : 1)
      node = parent
    } else if (node.nodeType == 1) {
      node = node.childNodes[off + (dir < 0 ? -1 : 0)]
      off = dir < 0 ? nodeSize(node) : 0
    } else {
      return false
    }
  }
}

function nodeSize(node) {
  return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
}

function hasBlockDesc(dom) {
  let desc = dom.pmViewDesc
  return desc && desc.node && desc.node.isBlock
}

// Work around Chrome issue https://bugs.chromium.org/p/chromium/issues/detail?id=447523
// (isCollapsed inappropriately returns true in shadow dom)
exports.selectionCollapsed = function(domSel) {
  let collapsed = domSel.isCollapsed
  if (collapsed && browser.chrome && domSel.rangeCount && !domSel.getRangeAt(0).collapsed)
    collapsed = false
  return collapsed
}
<|endoftext|>


<commit_before>/* global module:false */
module.exports = function(grunt) {
	var port = grunt.option('port') || 8000;
	var root = grunt.option('webdir') || grunt.option('base') || '.';

	if (!Array.isArray(root)) root = ['.', root];

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://lab.hakim.se/reveal-js\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2016 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n'
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		sass: {
			core: {
				files: {
					'css/reveal.css': 'css/reveal.scss',
				}
			},
			themes: {
				files: [
					{
						expand: true,
						cwd: 'css/theme/source',
						src: ['*.scss'],
						dest: 'css/theme',
						ext: '.css'
					}
				]
			}
		},

		autoprefixer: {
			dist: {
				src: 'css/reveal.css'
			}
		},

		cssmin: {
			compress: {
				files: {
					'css/reveal.min.css': [ 'css/reveal.css' ]
				}
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				esnext: true,
				latedef: true,
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false,
					define: false,
					exports: false
				}
			},
			files: [ 'Gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
                    hostname: '127.0.0.1',
					port: port,
                    useAvailablePort: true,
					base: root,
					livereload: true,
					open: true
				}
			},

		},

		zip: {
			'reveal-js-presentation.zip': [
				'index.html',
				'css/**',
				'js/**',
				'lib/**',
				'images/**',
				'plugin/**',
				'**.md'
			]
		},

		watch: {
			js: {
				files: [ 'Gruntfile.js', 'js/reveal.js' ],
				tasks: 'js'
			},
			theme: {
				files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ],
				tasks: 'css-themes'
			},
			css: {
				files: [ 'css/reveal.scss' ],
				tasks: 'css-core'
			},
			html: {
				files: root.map(path => path + '/*.html')
			},
			markdown: {
				files: root.map(path => path + '/*.md')
			},
			options: {
				livereload: true
			}
		},

		retire: {
			js: ['js/reveal.js', 'lib/js/*.js', 'plugin/**/*.js'],
			node: ['.'],
			options: {}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-zip' );
	grunt.loadNpmTasks( 'grunt-retire' );

	// Default task
	grunt.registerTask( 'default', [ 'css', 'js' ] );

	// JS task
	grunt.registerTask( 'js', [ 'jshint', 'uglify', 'qunit' ] );

	// Theme CSS
	grunt.registerTask( 'css-themes', [ 'sass:themes' ] );

	// Core framework CSS
	grunt.registerTask( 'css-core', [ 'sass:core', 'autoprefixer', 'cssmin' ] );

	// All CSS
	grunt.registerTask( 'css', [ 'sass', 'autoprefixer', 'cssmin' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<commit_msg>make curdir a secondary option, rather than first<commit_after>/* global module:false */
module.exports = function(grunt) {
	var port = grunt.option('port') || 8000;
	var root = grunt.option('webdir') || grunt.option('base') || '.';

	if (!Array.isArray(root)) root = [root, '.'];

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://lab.hakim.se/reveal-js\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2016 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n'
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		sass: {
			core: {
				files: {
					'css/reveal.css': 'css/reveal.scss',
				}
			},
			themes: {
				files: [
					{
						expand: true,
						cwd: 'css/theme/source',
						src: ['*.scss'],
						dest: 'css/theme',
						ext: '.css'
					}
				]
			}
		},

		autoprefixer: {
			dist: {
				src: 'css/reveal.css'
			}
		},

		cssmin: {
			compress: {
				files: {
					'css/reveal.min.css': [ 'css/reveal.css' ]
				}
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				esnext: true,
				latedef: true,
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false,
					define: false,
					exports: false
				}
			},
			files: [ 'Gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
                    hostname: '127.0.0.1',
					port: port,
                    useAvailablePort: true,
					base: root,
					livereload: true,
					open: true
				}
			},

		},

		zip: {
			'reveal-js-presentation.zip': [
				'index.html',
				'css/**',
				'js/**',
				'lib/**',
				'images/**',
				'plugin/**',
				'**.md'
			]
		},

		watch: {
			js: {
				files: [ 'Gruntfile.js', 'js/reveal.js' ],
				tasks: 'js'
			},
			theme: {
				files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ],
				tasks: 'css-themes'
			},
			css: {
				files: [ 'css/reveal.scss' ],
				tasks: 'css-core'
			},
			html: {
				files: root.map(path => path + '/*.html')
			},
			markdown: {
				files: root.map(path => path + '/*.md')
			},
			options: {
				livereload: true
			}
		},

		retire: {
			js: ['js/reveal.js', 'lib/js/*.js', 'plugin/**/*.js'],
			node: ['.'],
			options: {}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );
	grunt.loadNpmTasks( 'grunt-autoprefixer' );
	grunt.loadNpmTasks( 'grunt-zip' );
	grunt.loadNpmTasks( 'grunt-retire' );

	// Default task
	grunt.registerTask( 'default', [ 'css', 'js' ] );

	// JS task
	grunt.registerTask( 'js', [ 'jshint', 'uglify', 'qunit' ] );

	// Theme CSS
	grunt.registerTask( 'css-themes', [ 'sass:themes' ] );

	// Core framework CSS
	grunt.registerTask( 'css-core', [ 'sass:core', 'autoprefixer', 'cssmin' ] );

	// All CSS
	grunt.registerTask( 'css', [ 'sass', 'autoprefixer', 'cssmin' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-jscs');
  grunt.loadNpmTasks('grunt-simple-mocha');

  grunt.initConfig({
    jshint: {
      options: {
        node: true,
        jshintrc: true
      },
      src: ['server.js', 'routes/**/*.js', 'lib/*.js']
    },

    jscs: {
      src: ['server.js', 'routes/**/*.js','lib/*.js'],
      options: {
        config: '.jscsrc'
      }
    },

    simplemocha: {
      options: {
        timeout: 4000,
        reporter: 'tap'
      },

      all: {src: ['test/user_test.js','test/concierge_test.js','test/change_test.js','test/jobs_test.js']}

    }

  });

  grunt.registerTask('test', ['jshint', 'jscs', 'simplemocha']);
  grunt.registerTask('default',['test']);

};
<commit_msg>grunt<commit_after>module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-jscs');
  grunt.loadNpmTasks('grunt-simple-mocha');

  grunt.initConfig({
    jshint: {
      options: {
        node: true,
        jshintrc: true
      },
      src: ['server.js', 'routes/**/*.js', 'lib/*.js']
    },

    jscs: {
      src: ['server.js', 'routes/**/*.js','lib/*.js'],
      options: {
        config: '.jscsrc'
      }
    },

    simplemocha: {
      options: {
        timeout: 6000,
        reporter: 'tap'
      },

      all: {src: ['test/user_test.js','test/concierge_test.js','test/change_test.js','test/jobs_test.js']}

    }

  });

  grunt.registerTask('test', ['jshint', 'jscs', 'simplemocha']);
  grunt.registerTask('default',['test']);

};
<|endoftext|>


<commit_before>'use strict';

var watchify = require('watchify');
var browserify = require('browserify');
var babelify = require('babelify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash').assign;
var brfs = require('brfs');
var rename = require('gulp-rename');
var postcss = require('gulp-postcss');
var notify = require('gulp-notify');
var uglify = require('gulp-uglify');
var doiuse = require('doiuse');

// add custom browserify options here
var customOpts = {
  entries: ['./js/app.js'],
  debug: true,
  transform: [brfs, [babelify, {presets: ["es2015"]}]],
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts));

gulp.task('js', bundle); // so you can run `gulp js` to build the file
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', gutil.log); // output build logs to terminal

function bundle() {
  return b.bundle()
    // log errors if they happen
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    // optional, remove if you don't need to buffer file contents
    .pipe(buffer())
    // optional, remove if you dont want sourcemaps
    .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
    .pipe(uglify())
       // Add transformation tasks to the pipeline here.
    .pipe(sourcemaps.write('./')) // writes .map file
    .pipe(gulp.dest('./dist'));
}

gulp.task('postcss', function() {
  // Runs postCSS with all its plugins.
  var nano = require('gulp-cssnano');
  var plugins = [
    require('autoprefixer'),
    doiuse({
      browsers: [
        'ie >= 8',
        '> 1%'
      ],
      // ignore: ['vh'], // an optional array of features to ignore
      // ignoreFiles: ['**/normalize.css'], // an optional array of file globs to match against original source file path, to ignore
      onFeatureUsage: function (usageInfo) {
        console.log(usageInfo.message)
      }
    }),
  ];
  // Run PostCSS
  return gulp.src('./stylesheets/styles.css')
      .pipe(postcss(plugins))
      .on('error', function(error) {
        gutil.log(gutil.colors.magenta.bold('Error while compiling CSS'));
        gutil.log(gutil.colors.magenta(error.message));
      })
      .pipe(gulp.dest('./dist'))
      .pipe(rename({ suffix: '.min' }))
      .pipe(nano({ discardUnused: false }))
      .pipe(gulp.dest('./dist'))
      .pipe(function onComplete() {
        return notify({ message: 'PostCSS has finished compiling.' });
      }());
});

gulp.task('watch-styles', function watchStyles() {
  // Watches the stylesheet folders for changes and runs the 'postcss' task if a change occurs.
  gulp.watch(['./stylesheets/*.css'], ['postcss']);
  return;
});

gulp.task("default", [
  'postcss',
  'watch-styles',
  "js",
]);

gulp.task("build", [
  'postcss',
  "js",
]);<commit_msg>Separate bundle and watch.<commit_after>'use strict';

var watchify = require('watchify');
var browserify = require('browserify');
var babelify = require('babelify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash').assign;
var brfs = require('brfs');
var rename = require('gulp-rename');
var postcss = require('gulp-postcss');
var notify = require('gulp-notify');
var uglify = require('gulp-uglify');
var doiuse = require('doiuse');

// add custom browserify options here
var customOpts = {
  entries: ['./js/app.js'],
  debug: true,
  transform: [brfs, [babelify, {presets: ["es2015"]}]],
};
var opts = assign({}, watchify.args, customOpts);
var w = watchify(browserify(opts));
var b = browserify(customOpts);

gulp.task('watch-js', bundleAndWatch);
gulp.task('js', bundle);
w.on('update', bundle); // on any dep update, runs the bundler
w.on('log', gutil.log); // output build logs to terminal

function bundleAndWatch() {
  return w.bundle()
    // log errors if they happen
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    // optional, remove if you don't need to buffer file contents
    .pipe(buffer())
    // optional, remove if you dont want sourcemaps
    .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
    .pipe(uglify())
       // Add transformation tasks to the pipeline here.
    .pipe(sourcemaps.write('./')) // writes .map file
    .pipe(gulp.dest('./dist'));
}

function bundle() {
  return b.bundle()
    // log errors if they happen
    .on('error', gutil.log.bind(gutil, 'Browserify Error'))
    .pipe(source('bundle.js'))
    // optional, remove if you don't need to buffer file contents
    .pipe(buffer())
    // optional, remove if you dont want sourcemaps
    .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
    .pipe(uglify())
       // Add transformation tasks to the pipeline here.
    .pipe(sourcemaps.write('./')) // writes .map file
    .pipe(gulp.dest('./dist'));
}

gulp.task('postcss', function() {
  // Runs postCSS with all its plugins.
  var nano = require('gulp-cssnano');
  var plugins = [
    require('autoprefixer'),
    doiuse({
      browsers: [
        'ie >= 8',
        '> 1%'
      ],
      // ignore: ['vh'], // an optional array of features to ignore
      // ignoreFiles: ['**/normalize.css'], // an optional array of file globs to match against original source file path, to ignore
      onFeatureUsage: function (usageInfo) {
        console.log(usageInfo.message)
      }
    }),
  ];
  // Run PostCSS
  return gulp.src('./stylesheets/styles.css')
      .pipe(postcss(plugins))
      .on('error', function(error) {
        gutil.log(gutil.colors.magenta.bold('Error while compiling CSS'));
        gutil.log(gutil.colors.magenta(error.message));
      })
      .pipe(gulp.dest('./dist'))
      .pipe(rename({ suffix: '.min' }))
      .pipe(nano({ discardUnused: false }))
      .pipe(gulp.dest('./dist'))
      .pipe(function onComplete() {
        return notify({ message: 'PostCSS has finished compiling.' });
      }());
});

gulp.task('watch-styles', function watchStyles() {
  // Watches the stylesheet folders for changes and runs the 'postcss' task if a change occurs.
  gulp.watch(['./stylesheets/*.css'], ['postcss']);
  return;
});

gulp.task("default", [
  'postcss',
  'watch-styles',
  "watch-js",
]);

gulp.task("build", [
  'postcss',
  "js",
]);<|endoftext|>