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

'use strict'

module.exports = {
  returnMonth: month,
  month2Args: month2Args
}

// Getting current month
function month () {
  let date = new Date();
  let currentMonth = date.getMonth();
  // will return a value 0 - 11, have to had 1
  currentMonth = currentMonth +1 ;

  return currentMonth;
};

function month2Args (month, year) {
  // Requiring the zellers module
  const zellers = require('../zellers');
  // String of month header
  const secondLine ='Su Mo Tu We Th Fr Sa';
  // Object to hold month names and number of days in the month
  const monthObj = {
      1: ["January", 31],
      2: ["February", 28],
      3: ["March", 31],
      4: ["April", 30],
      5: ["May", 31],
      6: ["June", 30],
      7: ["July", 31],
      8: ["August", 31],
      9: ["September", 30],
      10: ["October", 31],
      11: ["November", 30],
      12: ["December", 31]
  };

  if (year < 1753) {
    console.log("Please choose a year more recent than 1753");
  } else if (year > 9999) {
      console.log("Please choose a year less than 9999");
  } else {
      // Assigning month1 input to m
      let m = month;
      // Assigning year input to y
      let y = year;
      // Getting the starting day for the month
      let h = zellers.getDay(y, m, 1);
      // Checking for leap year
      let leapYear = zellers.isLeapYear(y);
      // Variable to hold number of days in a month
      let monthLength = 0;
      // Making day adjustment for leap year
      if (m===2 && leapYear === true) {
        monthLength = monthObj[m][1] + 1;
      } else {
        monthLength = monthObj[m][1]
      }

      // Gets month and year for monthObj
      let monthAndYear = `${monthObj[m][0]} ${y}`;
      // Calculates first row spaces
      let firstRowSpaces = (20 - monthAndYear.length) / 2;
      // Used for additional spacing
      let space = " ";
      // Building first row
      let firstRow = space.repeat(firstRowSpaces)+monthAndYear;
      //Outputs Months and year
      //console.log(firstRow);

      // Adjusting h to fit into designated space
      h === 0 ? h = 6 : h = h - 1;
      //Variable to hold days
      let days = '';
      //Variable to hold count
      let count = 0;
      // Add space before h
      for (let i = 0; i < h; i++) {
        days += "   ";
        count += 1;
      }

      // loop to generate date numbers
      for (var j = 1; j <= monthLength; j++) {
        days += (j<10) ? " "+j+" " : j+" ";
        count += 1;

      if (count % 7===0) {
        days = days.slice(0, -1);
        days += "\n";
      }
    }

      // SLicing unneeded space
      days = days.slice(0, -1);

      if (days.match(/\n/g).length === 4) {
        days += "\n";
      } else if (days.match(/\n/g).length === 3) {
        days += "\n\n";
      }

      let calendar = `${firstRow}\n${secondLine}\n${days}`;
      console.log(calendar);
      return calendar;

  }

}

<commit_msg>Made inputs from node-webserver app integers<commit_after>#!/usr/bin/env node

'use strict'

module.exports = {
  returnMonth: month,
  month2Args: month2Args
}

// Getting current month
function month () {
  let date = new Date();
  let currentMonth = date.getMonth();
  // will return a value 0 - 11, have to had 1
  currentMonth = currentMonth +1 ;

  return currentMonth;
};

function month2Args (month, year) {
  // Requiring the zellers module
  const zellers = require('../zellers');
  // String of month header
  const secondLine ='Su Mo Tu We Th Fr Sa';
  // Object to hold month names and number of days in the month
  const monthObj = {
      1: ["January", 31],
      2: ["February", 28],
      3: ["March", 31],
      4: ["April", 30],
      5: ["May", 31],
      6: ["June", 30],
      7: ["July", 31],
      8: ["August", 31],
      9: ["September", 30],
      10: ["October", 31],
      11: ["November", 30],
      12: ["December", 31]
  };

  if (year < 1753) {
    console.log("Please choose a year more recent than 1753");
  } else if (year > 9999) {
      console.log("Please choose a year less than 9999");
  } else {
      // Assigning month1 input to m
      let m = parseInt(month);
      // Assigning year input to y
      let y = parseInt(year);
      // Getting the starting day for the month
      let h = zellers.getDay(y, m, 1);
      // Checking for leap year
      let leapYear = zellers.isLeapYear(y);
      // Variable to hold number of days in a month
      let monthLength = 0;
      // Making day adjustment for leap year
      if (m===2 && leapYear === true) {
        monthLength = monthObj[m][1] + 1;
      } else {
        monthLength = monthObj[m][1]
      }

      // Gets month and year for monthObj
      let monthAndYear = `${monthObj[m][0]} ${y}`;
      // Calculates first row spaces
      let firstRowSpaces = (20 - monthAndYear.length) / 2;
      // Used for additional spacing
      let space = " ";
      // Building first row
      let firstRow = space.repeat(firstRowSpaces)+monthAndYear;
      //Outputs Months and year
      //console.log(firstRow);

      // Adjusting h to fit into designated space
      h === 0 ? h = 6 : h = h - 1;
      //Variable to hold days
      let days = '';
      //Variable to hold count
      let count = 0;
      // Add space before h
      for (let i = 0; i < h; i++) {
        days += "   ";
        count += 1;
      }

      // loop to generate date numbers
      for (var j = 1; j <= monthLength; j++) {
        days += (j<10) ? " "+j+" " : j+" ";
        count += 1;

      if (count % 7===0) {
        days = days.slice(0, -1);
        days += "\n";
      }
    }

      // SLicing unneeded space
      days = days.slice(0, -1);

      if (days.match(/\n/g).length === 4) {
        days += "\n";
      } else if (days.match(/\n/g).length === 3) {
        days += "\n\n";
      }

      let calendar = `${firstRow}\n${secondLine}\n${days}`;
      console.log(calendar);
      return calendar;

  }

}

<|endoftext|>


<commit_before>'use strict';

let fs = require('fs'),
    readline = require('readline'),
    path = require('path'),
    _ = require('lodash'),
    emojiStrip = require('emoji-strip'),
    natural = require('natural'),
    tokenizer = new natural.WordTokenizer(),
    helpers = require('./helpers'),
    config = require('../config');

module.exports = (topic, n, date, onData, onError, onEnd) => {
    onData = onData || ((d) => {});
    onError = onError || ((e) => {});
    onEnd = onEnd || ((t) => {});
    let tokenDump = path.join(__dirname, "../", helpers.tokenDumpName(topic, date)),
        tokens = [],
        afinData = fs.readFileSync(path.join(__dirname, "../", config.paths.afin), {encoding: "utf-8"}).split("\n"),
        afin = {},
        temp;
        for (let i = afinData.length - 1; i >= 0; i--) {
          temp = afinData[i].split("\t");
          afin[temp[0]] = parseInt(temp[1]);
        }

    fs.stat(tokenDump, function(err, stat) {
        if (err) {
            return onError(err);
        }
        const tokenDumpRLStream = readline.createInterface({
            input: fs.createReadStream(tokenDump)
        });

        tokenDumpRLStream
            .on('line', (tweet) => {
                let t = JSON.parse(tweet);
                tokens = tokens.concat(t.tok);
            })
            .on('close', () => {

                try {
                    let ngrams = natural.NGrams.ngrams(tokens, parseInt(n)),
                        temp = {};

                    ngrams = Array.prototype.filter.call(ngrams.map(function(v) {
                        let flag = false;
                        v.forEach(function(word){
                            if('undefined' !== typeof afin[word]) {
                                flag = true;
                                return;
                            }
                        });
                        if(flag){
                            return v.join(" ");
                        }
                    }), (v) => { return v; });

                    ngrams.forEach(function(v) {
                        if('undefined' == typeof temp[v]) {
                            temp[v] = {name: v, count: 0};
                        }
                        temp[v].count += 1;
                    });

                    let all_grams = _.orderBy(_.values(temp), ['count'], ['desc']);

                    let top = [],
                        master = all_grams[0],
                        slave = all_grams[1],
                        master_index = 0,
                        slave_index = 1,
                        i = 0;

                    while(i < 5) {
                        var dist = parseInt(natural.JaroWinklerDistance(master.name, slave.name)*100);
                        if(dist >= 50) {
                            master.count += parseInt(slave.count);
                            slave_index++;
                            slave = all_grams[slave_index];
                            if('undefined' === typeof slave) {
                                top.push(master);
                                break;
                            }
                        } else {
                            top.push(master);
                            master = slave;
                            master_index = slave_index;
                            slave_index++;
                            slave = all_grams[slave_index];
                            i++;
                            if('undefined' === typeof slave){
                                break;
                            }
                        }
                    }
                    let others = all_grams.slice(master_index, all_grams.length);
                    top.push({name: "Others", count: others.reduce((o, v) => { return o += v.count; }, 0)});
                    onEnd(top);
                } catch(err) {
                    onError(err);
                }
            });
    });
}
<commit_msg>Moar LET!!<commit_after>'use strict';

let fs = require('fs'),
    readline = require('readline'),
    path = require('path'),
    _ = require('lodash'),
    emojiStrip = require('emoji-strip'),
    natural = require('natural'),
    tokenizer = new natural.WordTokenizer(),
    helpers = require('./helpers'),
    config = require('../config');

module.exports = (topic, n, date, onData, onError, onEnd) => {
    onData = onData || ((d) => {});
    onError = onError || ((e) => {});
    onEnd = onEnd || ((t) => {});
    let tokenDump = path.join(__dirname, "../", helpers.tokenDumpName(topic, date)),
        tokens = [],
        afinData = fs.readFileSync(path.join(__dirname, "../", config.paths.afin), {encoding: "utf-8"}).split("\n"),
        afin = {},
        temp;
        for (let i = afinData.length - 1; i >= 0; i--) {
          temp = afinData[i].split("\t");
          afin[temp[0]] = parseInt(temp[1]);
        }

    fs.stat(tokenDump, function(err, stat) {
        if (err) {
            return onError(err);
        }
        const tokenDumpRLStream = readline.createInterface({
            input: fs.createReadStream(tokenDump)
        });

        tokenDumpRLStream
            .on('line', (tweet) => {
                let t = JSON.parse(tweet);
                tokens = tokens.concat(t.tok);
            })
            .on('close', () => {

                try {
                    let ngrams = natural.NGrams.ngrams(tokens, parseInt(n)),
                        temp = {};

                    ngrams = Array.prototype.filter.call(ngrams.map(function(v) {
                        let flag = false;
                        v.forEach(function(word){
                            if('undefined' !== typeof afin[word]) {
                                flag = true;
                                return;
                            }
                        });
                        if(flag){
                            return v.join(" ");
                        }
                    }), (v) => { return v; });

                    ngrams.forEach(function(v) {
                        if('undefined' == typeof temp[v]) {
                            temp[v] = {name: v, count: 0};
                        }
                        temp[v].count += 1;
                    });

                    let all_grams = _.orderBy(_.values(temp), ['count'], ['desc']);

                    let top = [],
                        master = all_grams[0],
                        slave = all_grams[1],
                        master_index = 0,
                        slave_index = 1,
                        i = 0;

                    while(i < 5) {
                        let dist = parseInt(natural.JaroWinklerDistance(master.name, slave.name)*100);
                        if(dist >= 50) {
                            master.count += parseInt(slave.count);
                            slave_index++;
                            slave = all_grams[slave_index];
                            if('undefined' === typeof slave) {
                                top.push(master);
                                break;
                            }
                        } else {
                            top.push(master);
                            master = slave;
                            master_index = slave_index;
                            slave_index++;
                            slave = all_grams[slave_index];
                            i++;
                            if('undefined' === typeof slave){
                                break;
                            }
                        }
                    }
                    let others = all_grams.slice(master_index, all_grams.length);
                    top.push({name: "Others", count: others.reduce((o, v) => { return o += v.count; }, 0)});
                    onEnd(top);
                } catch(err) {
                    onError(err);
                }
            });
    });
}
<|endoftext|>


<commit_before>var express = require('express');
var co = require('co');
var wrapper = require('co-express');
var app = wrapper(express());
var fs = require('co-fs');
var _ = require('lodash');

app.get("/nlp", function (req, res) {

});

/**
 * Find a plugin by file name in ./tasks/{{name}}
 */
function findPlugin(name) {
    console.log("\t\t./tasks/" + name + ".js");
    return require("./tasks/" + name + ".js");
}

/**
 * Creates a rest endpoint for a url, config, and a func to call
 */
function registerRestTask(url, config, func) {
    app.get(url, function (req, res) {
        func(null, _.extend({}, config, req.query)).then(function (data) {
            if (config.format !== "plain") {
                res.set('Content-Type', 'application/json');
            }
            res.send(data);
        }).fail(function (err) {
            if (config.format !== "plain") {
                res.set('Content-Type', 'application/json');
            }
            res.send(err);
        });
    });
}

/**
 * Registers a global plugin from config and generates rest endpoints
 */
function registerGlobal(authStore, pluginName, plugin) {
    var pluginInstance = findPlugin(plugin.plugin);
    var taskList = [];
    pluginInstance({
        registerTask: function (taskName, help, func) {
            console.log("global plugin registering: " + taskName + " @ " + pluginName + "/" + taskName);
            registerRestTask("/" + pluginName + "/" + taskName, _.extend({}, plugin.config, authStore[pluginName]), func);
            taskList.push({name: taskName, help: help});
        }
    });
    return taskList;
}

/**
 * Registers a project from config and generates rest endpoints
 */
function registerProject(authStore, projectName, project) {
    console.log("Discovering project: " + project.displayName + " - " + project.description);

    var pluginList = [];

    for (var pluginName in project.plugins) {
        var pluginConfig = project.plugins[pluginName];

        var taskList = [];

        console.log("\tRegistering plugin: " + projectName + "/" + pluginName);

        var pluginInstance = findPlugin(pluginConfig.plugin);

        pluginInstance({
            registerTask: function (taskName, help, func) {
                console.log("\t\t\tplugin registering task: " + taskName + " @ " + projectName + "/" + pluginName + "/" + taskName);
                registerRestTask("/" + projectName + "/" + pluginName + "/" + taskName, _.extend({}, pluginConfig.config, authStore[projectName][pluginName]), func);
                taskList.push({name: taskName, help: help});
            }
        });

        pluginList.push({name: pluginName, tasks: taskList});
    }

    return pluginList;
}

/**
 * Takes a list of global plugins and a list of projects and generates rest endpoints for help
 */
function createGlobalHelp(globalList, projectList) {
    if (projectList && projectList.length) {
        projectList.forEach(function (project) {
            app.get("/" + project.name, function (req, res) {
                res.set("Content-Type", "application/json");
                res.send(project);
            });
        });
    }
    app.get("/", function (req, res) {
        res.set("Content-Type", "application/json");
        res.send({global: globalList || [], projects: projectList || []});
    });
}

// initialize
co(function *() {
    var nigelConfig = JSON.parse(yield fs.readFile("./nigel.json"));
    var authStore = JSON.parse(yield fs.readFile("./auth.json"));

    var projectList = [];
    var globalList = [];

    if (nigelConfig.projects) {
        var projects = nigelConfig.projects;
        for (var projName in projects) {
            var pluginList = registerProject(authStore, projName, projects[projName]);
            projectList.push({name: projName, plugins: pluginList});
        }
    }

    if (nigelConfig.plugins) {
        for (var pluginAlias in nigelConfig.plugins) {
            var taskList = registerGlobal(authStore, pluginAlias, nigelConfig.plugins[pluginAlias]);
            globalList.push({name: pluginAlias, tasks: taskList});
        }
    }

    createGlobalHelp(globalList, projectList);

    app.listen(3000);
    console.log("Listening on port 3000");
})();

<commit_msg>jsonp<commit_after>var express = require('express');
var co = require('co');
var wrapper = require('co-express');
var app = wrapper(express());
var fs = require('co-fs');
var _ = require('lodash');

app.get("/nlp", function (req, res) {

});

/**
 * Find a plugin by file name in ./tasks/{{name}}
 */
function findPlugin(name) {
    console.log("\t\t./tasks/" + name + ".js");
    return require("./tasks/" + name + ".js");
}

/**
 * Creates a rest endpoint for a url, config, and a func to call
 */
function registerRestTask(url, config, func) {
    app.get(url, function (req, res) {
        func(null, _.extend({}, config, req.query)).then(function (data) {
            if (config.format !== "plain") {
                res.set('Content-Type', 'application/json');
            }
            if (req.query.callback) {
                res.jsonp(data);
            } else {
                res.send(data);
            }
        }).fail(function (err) {
            if (config.format !== "plain") {
                res.set('Content-Type', 'application/json');
            }
            if (req.query.callback) {
                res.jsonp(err);
            } else {
                res.send(err);
            }
        });
    });
}

/**
 * Registers a global plugin from config and generates rest endpoints
 */
function registerGlobal(authStore, pluginName, plugin) {
    var pluginInstance = findPlugin(plugin.plugin);
    var taskList = [];
    pluginInstance({
        registerTask: function (taskName, help, func) {
            console.log("global plugin registering: " + taskName + " @ " + pluginName + "/" + taskName);
            registerRestTask("/" + pluginName + "/" + taskName, _.extend({}, plugin.config, authStore[pluginName]), func);
            taskList.push({name: taskName, help: help});
        }
    });
    return taskList;
}

/**
 * Registers a project from config and generates rest endpoints
 */
function registerProject(authStore, projectName, project) {
    console.log("Discovering project: " + project.displayName + " - " + project.description);

    var pluginList = [];

    for (var pluginName in project.plugins) {
        var pluginConfig = project.plugins[pluginName];

        var taskList = [];

        console.log("\tRegistering plugin: " + projectName + "/" + pluginName);

        var pluginInstance = findPlugin(pluginConfig.plugin);

        pluginInstance({
            registerTask: function (taskName, help, func) {
                console.log("\t\t\tplugin registering task: " + taskName + " @ " + projectName + "/" + pluginName + "/" + taskName);
                registerRestTask("/" + projectName + "/" + pluginName + "/" + taskName, _.extend({}, pluginConfig.config, authStore[projectName][pluginName]), func);
                taskList.push({name: taskName, help: help});
            }
        });

        pluginList.push({name: pluginName, tasks: taskList});
    }

    return pluginList;
}

/**
 * Takes a list of global plugins and a list of projects and generates rest endpoints for help
 */
function createGlobalHelp(globalList, projectList) {
    if (projectList && projectList.length) {
        projectList.forEach(function (project) {
            app.get("/" + project.name, function (req, res) {
                res.set("Content-Type", "application/json");
                res.send(project);
            });
        });
    }
    app.get("/", function (req, res) {
        res.set("Content-Type", "application/json");
        res.send({global: globalList || [], projects: projectList || []});
    });
}

// initialize
co(function *() {
    var nigelConfig = JSON.parse(yield fs.readFile("./nigel.json"));
    var authStore = JSON.parse(yield fs.readFile("./auth.json"));

    var projectList = [];
    var globalList = [];

    if (nigelConfig.projects) {
        var projects = nigelConfig.projects;
        for (var projName in projects) {
            var pluginList = registerProject(authStore, projName, projects[projName]);
            projectList.push({name: projName, plugins: pluginList});
        }
    }

    if (nigelConfig.plugins) {
        for (var pluginAlias in nigelConfig.plugins) {
            var taskList = registerGlobal(authStore, pluginAlias, nigelConfig.plugins[pluginAlias]);
            globalList.push({name: pluginAlias, tasks: taskList});
        }
    }

    createGlobalHelp(globalList, projectList);

    app.listen(3000);
    console.log("Listening on port 3000");
})();

<|endoftext|>


<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 * @module deep
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "./promise", "./utils", "./compiler", "./logs", "./protocol"], function(require, prom, utils, compiler, logs, proto) {

	var nodes = {
		val: function(s) {
			if (!s)
				return s;
			if (s._deep_array_)
				return s.map(function(e) {
					return e.value;
				});
			if (s._deep_query_node_)
				return s.value;
			return s;
		},
		paths: function(nodes) {
			if (nodes._deep_query_node_)
				return [nodes.path];
			return nodes.map(function(e) {
				return e.path;
			});
		},
		schemas: function(nodes) {
			if (nodes._deep_query_node_)
				return [nodes.schema];
			return nodes.map(function(e) {
				return e.schema;
			});
		},
		map: function(node, transformer) {
			if(!node)
				return transformer(node);
			if (node._deep_query_node_) {
				var r = transformer(node);
				if (r && (r.then || r.promise))
					return deep.when(r)
						.done(function(r) {
							node.set(r);
						});
				node.set(r);
				return r;
			}
			if(!node.forEach)
				return tansformer(node);
			return prom.all(node.map(transformer))
			.done(function(res) {
				var count = 0;
				//if(node._deep_array_)
					node.forEach(function(n) {
						n.set(res[count++]);
					});
			});
		},
		asyncUps:function(s, objects){
			return proto.getAll(objects)
			.done(function(objects){
				return nodes.ups(s, objects);
			});
		},
		ups:function(s, objects) {
			if(s._deep_query_node_)
				objects.forEach(function(object) {
					s.set(compiler.aup(object, s.value));
				});
			else if(s._deep_array_)
				s.forEach(function(result) {
					objects.forEach(function(object) {
						result.set(compiler.aup(object, result.value));
					});
				});
			else
				objects.forEach(function(object) {
					s = compiler.aup(object, s);
				});
			return s;
		},
		asyncBottoms:function(s, objects){
			return proto.getAll(objects)
			.done(function(objects){
				return nodes.bottoms(s, objects);
			});
		},
		bottoms:function(s, objects) {
			if(s._deep_query_node_)
				objects.forEach(function(object) {
					s.set(compiler.abottom(object, s.value));
				});
			else if(s._deep_array_)
				s.forEach(function(result) {
					objects.forEach(function(object) {
						result.set(compiler.abottom(object, result.value));
					});
				});
			else
				objects.forEach(function(object) {
					s = compiler.abottom(object, s);
				});
			return s;
		}
	};

	/**
	 * create a DeepQuery entry that hold info of objet node (path, value, ancestor, etc)
	 * @method create
	 * @param  {[type]} key
	 * @param  {[type]} ancestor
	 * @return {[type]}
	 */
	nodes.create = function(key, ancestor) {
		var path = ancestor.path;
		if (!ancestor.key)
			path += key;
		else
			path += "/" + key;

		var schema = null;
		if (ancestor.schema)
			schema = utils.retrieveFullSchemaByPath(ancestor.schema, key, "/");
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		return {
			_deep_query_node_: true,
			root: ancestor.root || ancestor,
			value: ancestor.value[key],
			path: path,
			paths: ancestor.paths.concat(key),
			key: key,
			ancestor: ancestor,
			schema: schema,
			depth: ancestor.depth + 1,
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
			set:function(value){
				this.value = value;
				if(this.ancestor)
					this.ancestor.value[this.key] = value;
			}
		};
	};

	nodes.clone = function(node) {
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		var clone = utils.shallowCopy(node);
		if (clone.paths)
			clone.paths = clone.paths.slice();
		return clone;
	};

	/**
	 * create a root DeepQuery node
	 *
	 * @static
	 * @method root
	 * @param  {Object} obj
	 * @param  {Object} schema
	 * @return {Object} a DeepQuery root node
	 */
	nodes.root = function(obj, schema, options) {
		options = options || {};
		if (obj && obj._deep_undefined_)
			obj = undefined;
		var node = {
			_deep_query_node_: true,
			value: obj,
			path: "/",
			paths: [],
			uri: options.uri || null,
			key: null,
			ancestor: null,
			schema: schema,
			depth: 0,
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
			set:function(value){
				this.value = value;
				if(this.ancestor)
					this.ancestor.value[this.key] = value;
			}
		};
		//node.root = node;
		return node;
	};
	return nodes;
})<commit_msg>re add print in nodes<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 * @module deep
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "./promise", "./utils", "./compiler", "./logs", "./protocol"], function(require, prom, utils, compiler, logs, proto) {

	var nodes = {
		val: function(s) {
			if (!s)
				return s;
			if (s._deep_array_)
				return s.map(function(e) {
					return e.value;
				});
			if (s._deep_query_node_)
				return s.value;
			return s;
		},
		paths: function(nodes) {
			if (nodes._deep_query_node_)
				return [nodes.path];
			return nodes.map(function(e) {
				return e.path;
			});
		},
		schemas: function(nodes) {
			if (nodes._deep_query_node_)
				return [nodes.schema];
			return nodes.map(function(e) {
				return e.schema;
			});
		},
		map: function(node, transformer) {
			if(!node)
				return transformer(node);
			if (node._deep_query_node_) {
				var r = transformer(node);
				if (r && (r.then || r.promise))
					return deep.when(r)
						.done(function(r) {
							node.set(r);
						});
				node.set(r);
				return r;
			}
			if(!node.forEach)
				return tansformer(node);
			return prom.all(node.map(transformer))
			.done(function(res) {
				var count = 0;
				//if(node._deep_array_)
					node.forEach(function(n) {
						n.set(res[count++]);
					});
			});
		},
		asyncUps:function(s, objects){
			return proto.getAll(objects)
			.done(function(objects){
				return nodes.ups(s, objects);
			});
		},
		ups:function(s, objects) {
			if(s._deep_query_node_)
				objects.forEach(function(object) {
					s.set(compiler.aup(object, s.value));
				});
			else if(s._deep_array_)
				s.forEach(function(result) {
					objects.forEach(function(object) {
						result.set(compiler.aup(object, result.value));
					});
				});
			else
				objects.forEach(function(object) {
					s = compiler.aup(object, s);
				});
			return s;
		},
		asyncBottoms:function(s, objects){
			return proto.getAll(objects)
			.done(function(objects){
				return nodes.bottoms(s, objects);
			});
		},
		bottoms:function(s, objects) {
			if(s._deep_query_node_)
				objects.forEach(function(object) {
					s.set(compiler.abottom(object, s.value));
				});
			else if(s._deep_array_)
				s.forEach(function(result) {
					objects.forEach(function(object) {
						result.set(compiler.abottom(object, result.value));
					});
				});
			else
				objects.forEach(function(object) {
					s = compiler.abottom(object, s);
				});
			return s;
		},
		print: function(node) {
			/*for(var i in node)
			{
				if(i == "root" || i == "ancestor")
					node[i] = "[_deep_query_node_]";
			}*/

			var oldRoot = node.root,
				oldAncestor = node.ancestor;
			if (node.root)
				node.root = "[_deep_query_node_]";
			if (node.ancestor)
				node.ancestor = "[_deep_query_node_]";
			console.log("print node : ", node.path)
			var r = JSON.stringify(node);
			if (oldRoot)
				node.root = oldRoot;
			if (oldAncestor)
				node.ancestor = oldAncestor;
			return r;
 		}
	};

	/**
	 * create a DeepQuery entry that hold info of objet node (path, value, ancestor, etc)
	 * @method create
	 * @param  {[type]} key
	 * @param  {[type]} ancestor
	 * @return {[type]}
	 */
	nodes.create = function(key, ancestor) {
		var path = ancestor.path;
		if (!ancestor.key)
			path += key;
		else
			path += "/" + key;

		var schema = null;
		if (ancestor.schema)
			schema = utils.retrieveFullSchemaByPath(ancestor.schema, key, "/");
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		return {
			_deep_query_node_: true,
			root: ancestor.root || ancestor,
			value: ancestor.value[key],
			path: path,
			paths: ancestor.paths.concat(key),
			key: key,
			ancestor: ancestor,
			schema: schema,
			depth: ancestor.depth + 1,
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
			set:function(value){
				this.value = value;
				if(this.ancestor)
					this.ancestor.value[this.key] = value;
			}
		};
	};

	nodes.clone = function(node) {
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		var clone = utils.shallowCopy(node);
		if (clone.paths)
			clone.paths = clone.paths.slice();
		return clone;
	};

	/**
	 * create a root DeepQuery node
	 *
	 * @static
	 * @method root
	 * @param  {Object} obj
	 * @param  {Object} schema
	 * @return {Object} a DeepQuery root node
	 */
	nodes.root = function(obj, schema, options) {
		options = options || {};
		if (obj && obj._deep_undefined_)
			obj = undefined;
		var node = {
			_deep_query_node_: true,
			value: obj,
			path: "/",
			paths: [],
			uri: options.uri || null,
			key: null,
			ancestor: null,
			schema: schema,
			depth: 0,
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
			set:function(value){
				this.value = value;
				if(this.ancestor)
					this.ancestor.value[this.key] = value;
			}
		};
		//node.root = node;
		return node;
	};

	var stringify = function(key, value) {
		if (value && value._deep_query_node_)
			return nodes.print(value);
		if (typeof value === 'function')
			return 'Function';
		if (value instanceof Date)
			return value.toString();
		if (value instanceof RegExp)
			return value.toString();
		if (typeof jQuery !== 'undefined' && value instanceof jQuery)
			return "jquery:" + value.selector;
		return value;
	};
	utils.stringify = function(value, nospace) {
		return JSON.stringify(value, stringify, nospace ? null : ' ');
	};

	return nodes;
})<|endoftext|>


<commit_before>'use strict';

const debug     = require('debug')('oplog'),
      Timestamp = require('mongodb').Timestamp;

module.exports = require('tofactory')(Oplog);

function Oplog(spec) {
    let dbName, oplogCol, collections, onUpdate, operations;
    const noop = function () {};

    init();

    return {
        listen
    };

    function init() {
        const db = spec.db.db('local');

        collections = spec.collections || [];
        dbName = spec.db.databaseName;
        db.on('error', function (e) {console.log('error:', e)});
        db.on('timeout', function (e) {console.log('timeout:', e)});
        db.on('reconnect', function (e) {console.log('reconnect:', e)});
        db.on('parseError', function (e) {console.log('parseError:', e)});
        db.on('close', function (e) {console.log('close:', e)});
        oplogCol = db.collection("oplog.rs");

        operations = {
            i: spec.onInsert || noop,
            u: updateOp,
            d: spec.onRemove || noop
        };

        onUpdate = spec.onUpdate || noop;
    }

    function listen(next) {
        // Find the highest timestamp
        const cursor = oplogCol.find({});

        cursor.project({ts: true});
        cursor.sort({$natural: -1});
        cursor.limit(1);
        cursor.next(function (err, result) {
            let lastOplogTime, queryForTime, tstamp, cursor, stream,
                queryCursor, nsRegex;

            if (err) return next(err);

            lastOplogTime = result.ts;

            // If there isn't one found, get one from the local clock
            if (lastOplogTime) {
                queryForTime = {
                    $gt: lastOplogTime
                };
            } else {
                tstamp = new Timestamp(0, Math.floor(new Date().getTime() / 1000));
                queryForTime = {
                    $gt: tstamp
                };
            }

            nsRegex = `^${dbName}.(${collections.join('|')})$`;
            debug('regex:', nsRegex);

            // Create a cursor for tailing and set it to await data
            queryCursor = {
                ts: queryForTime,
                ns: {$regex: nsRegex}
            };

            cursor = oplogCol.find(queryCursor,
                {numberOfRetries: Number.MAX_VALUE})
                .addCursorFlag('tailable', true)
                .addCursorFlag('awaitData', true)
                .addCursorFlag('oplogReplay', true);

            // Wrap that cursor in a Node Stream
            stream = cursor.stream();
            next();

            // And when data arrives at that stream, print it out
            stream.on('data', function(doc) {
                operations[doc.op](doc.ns.split('.')[1], doc.o, doc);
            });

            stream.on('error', function (e) {
                console.log('stream error:', e);
                throw new Error('Stream Error');
            });

            stream.on('end', function (e) {
                console.log('stream end:', e);
                throw new Error('Stream Ended');
            });

            stream.on('close', function (e) {
                console.log('stream close:', e);
                throw new Error('Stream Closed');
            });
        });
    }

    function updateOp(collection, data, doc) {
        let _id;
        const operator = Object.keys(data)[0];

        if (operator[0] === '$') {
            _id = doc.o2._id;
            switch (operator) {
                case '$set':
                    data = data.$set;
                    break;
                default:
                    data = undefined;
            }
        } else {
            _id = data._id;
        }

        onUpdate(collection, data, _id);
    }
}

<commit_msg>added condition in calling callback<commit_after>'use strict';

const debug     = require('debug')('oplog'),
      Timestamp = require('mongodb').Timestamp;

module.exports = require('tofactory')(Oplog);

function Oplog(spec) {
    let dbName, oplogCol, collections, onUpdate, operations;
    const noop = function () {};

    init();

    return {
        listen
    };

    function init() {
        const db = spec.db.db('local');

        collections = spec.collections || [];
        dbName = spec.db.databaseName;
        db.on('error', function (e) {console.log('error:', e)});
        db.on('timeout', function (e) {console.log('timeout:', e)});
        db.on('reconnect', function (e) {console.log('reconnect:', e)});
        db.on('parseError', function (e) {console.log('parseError:', e)});
        db.on('close', function (e) {console.log('close:', e)});
        oplogCol = db.collection("oplog.rs");

        operations = {
            i: spec.onInsert || noop,
            u: updateOp,
            d: spec.onRemove || noop
        };

        onUpdate = spec.onUpdate || noop;
    }

    function listen(next) {
        // Find the highest timestamp
        const cursor = oplogCol.find({});

        cursor.project({ts: true});
        cursor.sort({$natural: -1});
        cursor.limit(1);
        cursor.next(function (err, result) {
            let lastOplogTime, queryForTime, tstamp, cursor, stream,
                queryCursor, nsRegex;

            if (err) return next(err);

            lastOplogTime = result.ts;

            // If there isn't one found, get one from the local clock
            if (lastOplogTime) {
                queryForTime = {
                    $gt: lastOplogTime
                };
            } else {
                tstamp = new Timestamp(0, Math.floor(new Date().getTime() / 1000));
                queryForTime = {
                    $gt: tstamp
                };
            }

            nsRegex = `^${dbName}.(${collections.join('|')})$`;
            debug('regex:', nsRegex);

            // Create a cursor for tailing and set it to await data
            queryCursor = {
                ts: queryForTime,
                ns: {$regex: nsRegex}
            };

            cursor = oplogCol.find(queryCursor,
                {numberOfRetries: Number.MAX_VALUE})
                .addCursorFlag('tailable', true)
                .addCursorFlag('awaitData', true)
                .addCursorFlag('oplogReplay', true);

            // Wrap that cursor in a Node Stream
            stream = cursor.stream();
            next && next();

            // And when data arrives at that stream, print it out
            stream.on('data', function(doc) {
                operations[doc.op](doc.ns.split('.')[1], doc.o, doc);
            });

            stream.on('error', function (e) {
                console.log('stream error:', e);
                throw new Error('Stream Error');
            });

            stream.on('end', function (e) {
                console.log('stream end:', e);
                throw new Error('Stream Ended');
            });

            stream.on('close', function (e) {
                console.log('stream close:', e);
                throw new Error('Stream Closed');
            });
        });
    }

    function updateOp(collection, data, doc) {
        let _id;
        const operator = Object.keys(data)[0];

        if (operator[0] === '$') {
            _id = doc.o2._id;
            switch (operator) {
                case '$set':
                    data = data.$set;
                    break;
                default:
                    data = undefined;
            }
        } else {
            _id = data._id;
        }

        onUpdate(collection, data, _id);
    }
}
<|endoftext|>


<commit_before>'use strict'

// Match the start of the next non-text token
let nonTextStartRegex = /<(!DOCTYPE |!--|%=|%-|%(?!%)|\/|(?!%%))/g,
	// Match the end of the current ejs-* token
	ejsEndRegex = /%>/g,
	// Match the end of the current doctype or close-tag
	tagEndRegex = />/g,
	// Match the end of the current comment tag
	commentEndRegex = /-->/g,
	// Match the closing tag
	tagCloseRegex = /^([a-z][^\s\/>]*)\s*>/i,
	// Valid tag names
	tagNameRegex = /^[a-z][^\s\/>]*/i,
	// Find the next start of attribute, ejs or tag end
	tagOpenContentStartRegex = /^\s*(<%=|<%-|<%(?!%)|>|\/>|[^\s\/>"'<=]+)/,
	// Match static attribute values, or the start of a dynamic quoted one
	// This can generate false negatives, for example, in:
	// '="a<b" >' even tough the value is fixed (no ejs), will be read as a dynamic one
	// But this will be fixed by inspecting the we only got one text part
	attributeValueRegex = /^\s*=\s*("|'|[^\s>"'<=`]+|"[^"<]*"|'[^'<]*')/,
	// Find the next start of ejs in attribute value or its end
	nonTextValueStartDoubleRegex = /<%=|<%(?!%)|"/g,
	nonTextValueStartSingleRegex = /<%=|<%(?!%)|'/g,
	assert = require('assert')

/**
 * @typedef {Object} Token
 * @property {string} type - one of: text, ejs-eval, ejs-escaped, ejs-raw, comment, tag-close, doctype, tag-open
 * @property {SourcePoint} start - inclusive
 * @property {SourcePoint} end - non inclusive
 * @property {?string} content - present for types: text, ejs-eval, ejs-escaped, ejs-raw, comment, doctype
 * @property {?string} name - present for types: tag-close, tag-open
 * @property {?boolean} selfClose - present for type: tag-open
 * @property {?Array<Attribute>} attributes - present for type: tag-open
 */

/**
 * @typedef {Object} SourcePoint
 * @property {number} pos - zero-indexed position in the original source
 * @property {number} line - one-indexed
 * @property {number} column - one-indexed
 */

/**
 * @typedef {Object} Attribute
 * @property {string} type - one of: attribute-simple, attribute, ejs-escaped, ejs-eval
 * @property {?string} name - present for types: attribute-simple, attribute
 * @property {?string} value - Empty string if a boolean attribute. Present for type: attribute-simple
 * @property {?string} quote - used value quote (either empty, ' or "). Present for types: attribute-simple, attribute
 * @property {?string} content - present for types: ejs-escaped, ejs-eval
 * @property {?Array<ValuePart>} parts - present for type: attribute
 * @property {SourcePoint} start - inclusive. Present for types: ejs-escaped, ejs-eval
 * @property {SourcePoint} end - non inclusive. Present for types: ejs-escaped, ejs-eval
 */

/**
 * @typedef {Object} ValuePart
 * @property {string} type - one of: text, ejs-escaped, ejs-eval
 * @property {string} content
 * @property {SourcePoint} start - inclusive. Present for types: ejs-escaped, ejs-eval
 * @property {SourcePoint} end - non inclusive. Present for types: ejs-escaped, ejs-eval
 */

/**
 * @param {string} source
 * @param {Object} [options]
 * @param {boolean} [allowEJSInOpenTags=false]
 * @returns {Array<Token>}
 */
module.exports = function parse(source, options) {
	let pos = 0,
		line = 1,
		column = 1,
		tokens = []

	options = options || {}

	while (pos < source.length) {
		let match = exec(nonTextStartRegex)

		if (!match) {
			// All remaining is text
			let start = getSourcePoint()
			advanceTo(source.length)
			tokens.push(createContentToken('text', start))
			break
		}

		if (match.index !== pos) {
			// Emit text
			let start = getSourcePoint()
			advanceTo(match.index)
			tokens.push(createContentToken('text', start))
		}

		advanceTo(nonTextStartRegex.lastIndex)
		if (match[1] === '!DOCTYPE ') {
			tokens.push(readSimpleToken('doctype', tagEndRegex))
		} else if (match[1] === '!--') {
			tokens.push(readSimpleToken('comment', commentEndRegex))
		} else if (match[1] === '%=') {
			tokens.push(readSimpleToken('ejs-escaped', ejsEndRegex))
		} else if (match[1] === '%-') {
			tokens.push(readSimpleToken('ejs-raw', ejsEndRegex))
		} else if (match[1] === '%') {
			tokens.push(readSimpleToken('ejs-eval', ejsEndRegex))
		} else if (match[1] === '/') {
			tokens.push(readCloseTag())
		} else if (match[1] === '') {
			tokens.push(readOpenTag())
		}
	}

	return tokens

	/**
	 * Read a single token that has a known end
	 * @param {string} type
	 * @param {RegExp} endRegex
	 * @returns {Token}
	 */
	function readSimpleToken(type, endRegex) {
		let match = exec(endRegex)
		if (!match) {
			throwSyntaxError(`Unterminated ${type}`)
		}
		let start = getSourcePoint()
		advanceTo(match.index)
		let token = createContentToken(type, start)
		advanceTo(endRegex.lastIndex)
		return token
	}

	/**
	 * Read a close tag token
	 * @returns {Token}
	 */
	function readCloseTag() {
		let start = getSourcePoint(),
			match = tagCloseRegex.exec(source.substr(pos))
		if (!match) {
			throwSyntaxError('Invalid close tag')
		}
		advanceTo(pos + match[1].length)
		let end = getSourcePoint()
		advanceTo(pos - match[1].length + match[0].length)
		return {
			type: 'tag-close',
			start,
			end,
			name: match[1]
		}
	}

	/**
	 * Read an open tag token
	 * @returns {Token}
	 */
	function readOpenTag() {
		// Read tag name
		let start = getSourcePoint(),
			match = tagNameRegex.exec(source.substr(pos))
		if (!match) {
			throwSyntaxError('Invalid open tag')
		}
		let tagName = match[0]
		advanceTo(pos + tagName.length)

		// Keep reading content
		let selfClose = false,
			attributes = []
		while (true) {
			// Match using anchored regex
			let match = tagOpenContentStartRegex.exec(source.substr(pos))
			if (!match) {
				throwSyntaxError('Invalid open tag')
			}

			advanceTo(pos + match[0].length)
			if (match[1] === '<%-') {
				throwSyntaxError('EJS unescaped tags are not allowed here')
			} else if (match[1] === '<%=') {
				if (!options.allowEJSInOpenTags) {
					throwSyntaxError('EJS escaped tags are not allowed here')
				}
				attributes.push(readSimpleToken('ejs-escaped', ejsEndRegex))
			} else if (match[1] === '<%') {
				if (!options.allowEJSInOpenTags) {
					throwSyntaxError('EJS eval tags are not allowed here')
				}
				attributes.push(readSimpleToken('ejs-eval', ejsEndRegex))
			} else if (match[1] === '>') {
				break
			} else if (match[1] === '/>') {
				selfClose = true
				break
			} else {
				// Attribute start
				attributes.push(readAttribute(match[1]))
			}
		}

		return {
			type: 'tag-open',
			start: start,
			end: getSourcePoint(),
			name: tagName,
			selfClose: selfClose,
			attributes: attributes
		}
	}

	/**
	 * @param {string} name
	 * @returns {Attribute}
	 */
	function readAttribute(name) {
		// Read value (anchored match)
		let match = attributeValueRegex.exec(source.substr(pos)),
			quote = ''

		if (match) {
			// The quote used quote is the first char in the matched value
			quote = match[1][0]
			if (quote !== '"' && quote !== '\'') {
				// Unquoted value
				quote = ''
			}

			advanceTo(pos + match[0].length)
			if (match[1] === '"' || match[1] === '\'') {
				// Quoted value
				let parts = readValueParts(match[1])
				if (!parts.length || (parts.length === 1 && parts[0].type === 'text')) {
					// A simple quoted value that appeared not to be
					// Example: 'attr="a<b"'
					// Since the regex optimizes for quoted values without '<'
					return {
						type: 'attribute-simple',
						name: name,
						value: parts.length ? parts[0].content : '',
						quote: quote
					}
				}
				return {
					type: 'attribute',
					name: name,
					parts: parts,
					quote: quote
				}
			}
		}

		// Missing, unquoted or simple quote value
		return {
			type: 'attribute-simple',
			name: name,
			value: (match && match[1]) || '',
			quote: quote
		}
	}

	/**
	 * @param {string} q - one of: ', "
	 * @returns {Array<ValueParts>}
	 */
	function readValueParts(q) {
		let regex = q === '"' ? nonTextValueStartDoubleRegex : nonTextValueStartSingleRegex,
			parts = []
		while (true) {
			let match = exec(regex)
			if (!match) {
				throwSyntaxError('Invalid quoted attribute value')
			}

			if (match.index !== pos) {
				// Emit text
				parts.push({
					type: 'text',
					content: source.substring(pos, match.index)
				})
			}

			advanceTo(regex.lastIndex)
			if (match[0] === '<%=') {
				parts.push(readSimpleToken('ejs-escaped', ejsEndRegex))
			} else if (match[0] === '<%') {
				parts.push(readSimpleToken('ejs-eval', ejsEndRegex))
			} else {
				// End quote
				return parts
			}
		}
	}

	/**
	 * Advance reading position, updating `pos`, `line` and `column`
	 * @param {number} newPos - must be greater or equal to current pos
	 */
	function advanceTo(newPos) {
		let n = newPos - pos
		assert(n >= 0)
		while (n--) {
			if (source[pos] === '\n') {
				column = 1
				line++
			} else {
				column++
			}
			pos += 1
		}
	}

	/**
	 * Execute a regex from the current position
	 * @param {RegExp} regex
	 * @returns {?Array}
	 */
	function exec(regex) {
		regex.lastIndex = pos
		return regex.exec(source)
	}

	/**
	 * Return current source position
	 * @returns {SourcePoint}
	 */
	function getSourcePoint() {
		return {
			pos: pos,
			line: line,
			column: column
		}
	}

	/**
	 * Create a simple, content-oriented token up to current position
	 * @param {string} type - one of: text, ejs-eval, ejs-escaped, ejs-raw, comment, doctype
	 * @param {SourcePoint} start
	 * @returns {Token}
	 */
	function createContentToken(type, start) {
		let end = getSourcePoint()
		return {
			type,
			start,
			end,
			content: source.substring(start.pos, end.pos)
		}
	}

	/**
	 * Throw a syntax error in the current position
	 * @param {string} message
	 * @throws {SyntaxError}
	 */
	function throwSyntaxError(message) {
		let lines = source.split('\n'),
			curr = getSourcePoint(),
			fromLine = Math.max(1, curr.line - 2) - 1,
			toLine = Math.min(lines.length, curr.line + 2),
			snippet = lines.slice(fromLine, toLine).map((line, i) => {
				if (i === curr.line - 1 - fromLine) {
					return `\t >> | ${line}`
				}
				return `\t    | ${line}`
			}).join('\n')
		let err = new SyntaxError(`${message}\n${snippet}`)
		err.pos = getSourcePoint()
		throw err
	}
}<commit_msg>Disallow <%- in attribute values<commit_after>'use strict'

// Match the start of the next non-text token
let nonTextStartRegex = /<(!DOCTYPE |!--|%=|%-|%(?!%)|\/|(?!%%))/g,
	// Match the end of the current ejs-* token
	ejsEndRegex = /%>/g,
	// Match the end of the current doctype or close-tag
	tagEndRegex = />/g,
	// Match the end of the current comment tag
	commentEndRegex = /-->/g,
	// Match the closing tag
	tagCloseRegex = /^([a-z][^\s\/>]*)\s*>/i,
	// Valid tag names
	tagNameRegex = /^[a-z][^\s\/>]*/i,
	// Find the next start of attribute, ejs or tag end
	tagOpenContentStartRegex = /^\s*(<%=|<%-|<%(?!%)|>|\/>|[^\s\/>"'<=]+)/,
	// Match static attribute values, or the start of a dynamic quoted one
	// This can generate false negatives, for example, in:
	// '="a<b" >' even tough the value is fixed (no ejs), will be read as a dynamic one
	// But this will be fixed by inspecting the we only got one text part
	attributeValueRegex = /^\s*=\s*("|'|[^\s>"'<=`]+|"[^"<]*"|'[^'<]*')/,
	// Find the next start of ejs in attribute value or its end
	nonTextValueStartDoubleRegex = /<%=|<%-|<%(?!%)|"/g,
	nonTextValueStartSingleRegex = /<%=|<%-|<%(?!%)|'/g,
	assert = require('assert')

/**
 * @typedef {Object} Token
 * @property {string} type - one of: text, ejs-eval, ejs-escaped, ejs-raw, comment, tag-close, doctype, tag-open
 * @property {SourcePoint} start - inclusive
 * @property {SourcePoint} end - non inclusive
 * @property {?string} content - present for types: text, ejs-eval, ejs-escaped, ejs-raw, comment, doctype
 * @property {?string} name - present for types: tag-close, tag-open
 * @property {?boolean} selfClose - present for type: tag-open
 * @property {?Array<Attribute>} attributes - present for type: tag-open
 */

/**
 * @typedef {Object} SourcePoint
 * @property {number} pos - zero-indexed position in the original source
 * @property {number} line - one-indexed
 * @property {number} column - one-indexed
 */

/**
 * @typedef {Object} Attribute
 * @property {string} type - one of: attribute-simple, attribute, ejs-escaped, ejs-eval
 * @property {?string} name - present for types: attribute-simple, attribute
 * @property {?string} value - Empty string if a boolean attribute. Present for type: attribute-simple
 * @property {?string} quote - used value quote (either empty, ' or "). Present for types: attribute-simple, attribute
 * @property {?string} content - present for types: ejs-escaped, ejs-eval
 * @property {?Array<ValuePart>} parts - present for type: attribute
 * @property {SourcePoint} start - inclusive. Present for types: ejs-escaped, ejs-eval
 * @property {SourcePoint} end - non inclusive. Present for types: ejs-escaped, ejs-eval
 */

/**
 * @typedef {Object} ValuePart
 * @property {string} type - one of: text, ejs-escaped, ejs-eval
 * @property {string} content
 * @property {SourcePoint} start - inclusive. Present for types: ejs-escaped, ejs-eval
 * @property {SourcePoint} end - non inclusive. Present for types: ejs-escaped, ejs-eval
 */

/**
 * @param {string} source
 * @param {Object} [options]
 * @param {boolean} [allowEJSInOpenTags=false]
 * @returns {Array<Token>}
 */
module.exports = function parse(source, options) {
	let pos = 0,
		line = 1,
		column = 1,
		tokens = []

	options = options || {}

	while (pos < source.length) {
		let match = exec(nonTextStartRegex)

		if (!match) {
			// All remaining is text
			let start = getSourcePoint()
			advanceTo(source.length)
			tokens.push(createContentToken('text', start))
			break
		}

		if (match.index !== pos) {
			// Emit text
			let start = getSourcePoint()
			advanceTo(match.index)
			tokens.push(createContentToken('text', start))
		}

		advanceTo(nonTextStartRegex.lastIndex)
		if (match[1] === '!DOCTYPE ') {
			tokens.push(readSimpleToken('doctype', tagEndRegex))
		} else if (match[1] === '!--') {
			tokens.push(readSimpleToken('comment', commentEndRegex))
		} else if (match[1] === '%=') {
			tokens.push(readSimpleToken('ejs-escaped', ejsEndRegex))
		} else if (match[1] === '%-') {
			tokens.push(readSimpleToken('ejs-raw', ejsEndRegex))
		} else if (match[1] === '%') {
			tokens.push(readSimpleToken('ejs-eval', ejsEndRegex))
		} else if (match[1] === '/') {
			tokens.push(readCloseTag())
		} else if (match[1] === '') {
			tokens.push(readOpenTag())
		}
	}

	return tokens

	/**
	 * Read a single token that has a known end
	 * @param {string} type
	 * @param {RegExp} endRegex
	 * @returns {Token}
	 */
	function readSimpleToken(type, endRegex) {
		let match = exec(endRegex)
		if (!match) {
			throwSyntaxError(`Unterminated ${type}`)
		}
		let start = getSourcePoint()
		advanceTo(match.index)
		let token = createContentToken(type, start)
		advanceTo(endRegex.lastIndex)
		return token
	}

	/**
	 * Read a close tag token
	 * @returns {Token}
	 */
	function readCloseTag() {
		let start = getSourcePoint(),
			match = tagCloseRegex.exec(source.substr(pos))
		if (!match) {
			throwSyntaxError('Invalid close tag')
		}
		advanceTo(pos + match[1].length)
		let end = getSourcePoint()
		advanceTo(pos - match[1].length + match[0].length)
		return {
			type: 'tag-close',
			start,
			end,
			name: match[1]
		}
	}

	/**
	 * Read an open tag token
	 * @returns {Token}
	 */
	function readOpenTag() {
		// Read tag name
		let start = getSourcePoint(),
			match = tagNameRegex.exec(source.substr(pos))
		if (!match) {
			throwSyntaxError('Invalid open tag')
		}
		let tagName = match[0]
		advanceTo(pos + tagName.length)

		// Keep reading content
		let selfClose = false,
			attributes = []
		while (true) {
			// Match using anchored regex
			let match = tagOpenContentStartRegex.exec(source.substr(pos))
			if (!match) {
				throwSyntaxError('Invalid open tag')
			}

			advanceTo(pos + match[0].length)
			if (match[1] === '<%-') {
				throwSyntaxError('EJS unescaped tags are not allowed here')
			} else if (match[1] === '<%=') {
				if (!options.allowEJSInOpenTags) {
					throwSyntaxError('EJS escaped tags are not allowed here')
				}
				attributes.push(readSimpleToken('ejs-escaped', ejsEndRegex))
			} else if (match[1] === '<%') {
				if (!options.allowEJSInOpenTags) {
					throwSyntaxError('EJS eval tags are not allowed here')
				}
				attributes.push(readSimpleToken('ejs-eval', ejsEndRegex))
			} else if (match[1] === '>') {
				break
			} else if (match[1] === '/>') {
				selfClose = true
				break
			} else {
				// Attribute start
				attributes.push(readAttribute(match[1]))
			}
		}

		return {
			type: 'tag-open',
			start: start,
			end: getSourcePoint(),
			name: tagName,
			selfClose: selfClose,
			attributes: attributes
		}
	}

	/**
	 * @param {string} name
	 * @returns {Attribute}
	 */
	function readAttribute(name) {
		// Read value (anchored match)
		let match = attributeValueRegex.exec(source.substr(pos)),
			quote = ''

		if (match) {
			// The quote used quote is the first char in the matched value
			quote = match[1][0]
			if (quote !== '"' && quote !== '\'') {
				// Unquoted value
				quote = ''
			}

			advanceTo(pos + match[0].length)
			if (match[1] === '"' || match[1] === '\'') {
				// Quoted value
				let parts = readValueParts(match[1])
				if (!parts.length || (parts.length === 1 && parts[0].type === 'text')) {
					// A simple quoted value that appeared not to be
					// Example: 'attr="a<b"'
					// Since the regex optimizes for quoted values without '<'
					return {
						type: 'attribute-simple',
						name: name,
						value: parts.length ? parts[0].content : '',
						quote: quote
					}
				}
				return {
					type: 'attribute',
					name: name,
					parts: parts,
					quote: quote
				}
			}
		}

		// Missing, unquoted or simple quote value
		return {
			type: 'attribute-simple',
			name: name,
			value: (match && match[1]) || '',
			quote: quote
		}
	}

	/**
	 * @param {string} q - one of: ', "
	 * @returns {Array<ValueParts>}
	 */
	function readValueParts(q) {
		let regex = q === '"' ? nonTextValueStartDoubleRegex : nonTextValueStartSingleRegex,
			parts = []
		while (true) {
			let match = exec(regex)
			if (!match) {
				throwSyntaxError('Invalid quoted attribute value')
			}

			if (match.index !== pos) {
				// Emit text
				parts.push({
					type: 'text',
					content: source.substring(pos, match.index)
				})
			}

			advanceTo(regex.lastIndex)
			if (match[0] === '<%-') {
				throwSyntaxError('Invalid quoted attribute value')
			} else if (match[0] === '<%=') {
				parts.push(readSimpleToken('ejs-escaped', ejsEndRegex))
			} else if (match[0] === '<%') {
				parts.push(readSimpleToken('ejs-eval', ejsEndRegex))
			} else {
				// End quote
				return parts
			}
		}
	}

	/**
	 * Advance reading position, updating `pos`, `line` and `column`
	 * @param {number} newPos - must be greater or equal to current pos
	 */
	function advanceTo(newPos) {
		let n = newPos - pos
		assert(n >= 0)
		while (n--) {
			if (source[pos] === '\n') {
				column = 1
				line++
			} else {
				column++
			}
			pos += 1
		}
	}

	/**
	 * Execute a regex from the current position
	 * @param {RegExp} regex
	 * @returns {?Array}
	 */
	function exec(regex) {
		regex.lastIndex = pos
		return regex.exec(source)
	}

	/**
	 * Return current source position
	 * @returns {SourcePoint}
	 */
	function getSourcePoint() {
		return {
			pos: pos,
			line: line,
			column: column
		}
	}

	/**
	 * Create a simple, content-oriented token up to current position
	 * @param {string} type - one of: text, ejs-eval, ejs-escaped, ejs-raw, comment, doctype
	 * @param {SourcePoint} start
	 * @returns {Token}
	 */
	function createContentToken(type, start) {
		let end = getSourcePoint()
		return {
			type,
			start,
			end,
			content: source.substring(start.pos, end.pos)
		}
	}

	/**
	 * Throw a syntax error in the current position
	 * @param {string} message
	 * @throws {SyntaxError}
	 */
	function throwSyntaxError(message) {
		let lines = source.split('\n'),
			curr = getSourcePoint(),
			fromLine = Math.max(1, curr.line - 2) - 1,
			toLine = Math.min(lines.length, curr.line + 2),
			snippet = lines.slice(fromLine, toLine).map((line, i) => {
				if (i === curr.line - 1 - fromLine) {
					return `\t >> | ${line}`
				}
				return `\t    | ${line}`
			}).join('\n')
		let err = new SyntaxError(`${message}\n${snippet}`)
		err.pos = getSourcePoint()
		throw err
	}
}<|endoftext|>


<commit_before>/*
  Module Dependencies
*/
var htmlparser = require('htmlparser2'),
    _ = require('underscore'),
    isTag = require('./utils').isTag,
    camelCase = require('./utils').camelCase;

/*
  Parser
*/
exports = module.exports = function(strOrDom, options) {
  var dom = evaluate(strOrDom, options);

  // Generic root element
  var root = {
    type: 'root',
    name: 'root',
    parent: null,
    prev: null,
    next: null,
    children: []
  };

  // Update the dom using the root
  update(dom, root);

  return root;
};

var evaluate = exports.evaluate = function(strOrDom, options) {
  // options = options || $.fn.options;

  var dom;

  if (typeof strOrDom === 'string') {

    var handler = new htmlparser.DomHandler(options),
        parser = new htmlparser.Parser(handler, options);

    parser.write(content);
    parser.done();

    dom = handler.dom;

  } else {
    dom = strOrDom;
  }

  _.forEach(dom, parseData);

  return dom;
};

/*
  Update the dom structure, for one changed layer
*/
var update = exports.update = function(arr, parent) {
  // normalize
  if (!Array.isArray(arr)) arr = [arr];

  // Update parent
  if (parent) {
    parent.children = arr;
  } else {
    parent = null;
  }

  // Update neighbors
  for (var i = 0; i < arr.length; i++) {
    var node = arr[i];

    // Cleanly remove existing nodes from their previous structures.
    var oldSiblings = node.parent && node.parent.children;
    if (oldSiblings && oldSiblings !== arr) {
      oldSiblings.splice(oldSiblings.indexOf(node), 1);
      if (node.prev) {
        node.prev.next = node.next;
      }
      if (node.next) {
        node.next.prev = node.prev;
      }
    }

    node.prev = arr[i - 1] || null;
    node.next = arr[i + 1] || null;

    if (parent && parent.type === 'root') {
      node.root = parent;
      node.parent = null;
    } else {
      delete node.root;
      node.parent = parent;
    }
  }

  return parent;
};

/**
 * Extract element data according to `data-*` element attributes and store in
 * a key-value hash on the element's `data` attribute. Repeat for any and all
 * descendant elements.
 *
 * @param  {Object} elem Element
 */
var parseData = exports.parseData = function(elem) {
  if (elem.data === undefined) elem.data = {};
  var value;
  for (var key in elem.attribs) {
    if (key.substr(0, 5) === 'data-') {
      value = elem.attribs[key];
      key = key.slice(5);
      key = camelCase(key);
      elem.data[key] = value;
    }
  }

  _.forEach(elem.children, parseData);
};

// module.exports = $.extend(exports);
<commit_msg>fix 'content undefined'<commit_after>/*
  Module Dependencies
*/
var htmlparser = require('htmlparser2'),
    _ = require('underscore'),
    isTag = require('./utils').isTag,
    camelCase = require('./utils').camelCase;

/*
  Parser
*/
exports = module.exports = function(strOrDom, options) {
  var dom = evaluate(strOrDom, options);

  // Generic root element
  var root = {
    type: 'root',
    name: 'root',
    parent: null,
    prev: null,
    next: null,
    children: []
  };

  // Update the dom using the root
  update(dom, root);

  return root;
};

var evaluate = exports.evaluate = function(strOrDom, options) {
  // options = options || $.fn.options;

  var dom;

  if (typeof strOrDom === 'string') {

    var handler = new htmlparser.DomHandler(options),
        parser = new htmlparser.Parser(handler, options);

    parser.write(strOrDom);
    parser.done();

    dom = handler.dom;

  } else {
    dom = strOrDom;
  }

  _.forEach(dom, parseData);

  return dom;
};

/*
  Update the dom structure, for one changed layer
*/
var update = exports.update = function(arr, parent) {
  // normalize
  if (!Array.isArray(arr)) arr = [arr];

  // Update parent
  if (parent) {
    parent.children = arr;
  } else {
    parent = null;
  }

  // Update neighbors
  for (var i = 0; i < arr.length; i++) {
    var node = arr[i];

    // Cleanly remove existing nodes from their previous structures.
    var oldSiblings = node.parent && node.parent.children;
    if (oldSiblings && oldSiblings !== arr) {
      oldSiblings.splice(oldSiblings.indexOf(node), 1);
      if (node.prev) {
        node.prev.next = node.next;
      }
      if (node.next) {
        node.next.prev = node.prev;
      }
    }

    node.prev = arr[i - 1] || null;
    node.next = arr[i + 1] || null;

    if (parent && parent.type === 'root') {
      node.root = parent;
      node.parent = null;
    } else {
      delete node.root;
      node.parent = parent;
    }
  }

  return parent;
};

/**
 * Extract element data according to `data-*` element attributes and store in
 * a key-value hash on the element's `data` attribute. Repeat for any and all
 * descendant elements.
 *
 * @param  {Object} elem Element
 */
var parseData = exports.parseData = function(elem) {
  if (elem.data === undefined) elem.data = {};
  var value;
  for (var key in elem.attribs) {
    if (key.substr(0, 5) === 'data-') {
      value = elem.attribs[key];
      key = key.slice(5);
      key = camelCase(key);
      elem.data[key] = value;
    }
  }

  _.forEach(elem.children, parseData);
};

// module.exports = $.extend(exports);
<|endoftext|>


<commit_before>"use strict";

function partial(func) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function partialFunc() {
    return func.apply(null, args.concat(Array.prototype.slice.call(arguments, 0)));
  };
}

function clone(item) {
	// clone our object, since valid source comes from a json this wont break.
	return JSON.parse(JSON.stringify(item));
}

function flattenIncluded(included, resource) {
	var object = resolveAttributes(included[resource.type][resource.id]);
	var result = resolveRelationships(included, object)
	return result;
}

function resolveData(included, data) {
	if(isIncluded(included, data)) {
		return flattenIncluded(included, data);
	} else {
		return data;
	}
}

function resolveRelationshipData(included, links) {
	if(!links.data) {
		return links;
	} else if(Array.isArray(links.data)) {
		return links.data.map(partial(resolveData, included));
	} else {
		return resolveData(included, links.data);
	}
}

function isIncluded(included, resource) {
	return !(!included[resource.type] || !included[resource.type][resource.id]);
}

function resolveRelationships(included, resource) {
	if(!resource.relationships) {
		return resource;
	}

	Object.keys(resource.relationships).forEach(function(attribute) {
		resource[attribute] = resolveRelationshipData(included, resource.relationships[attribute]);
	});

	delete resource.relationships;
	return resource;
}

function resolveAttributes(resource) {
	for (var name in resource.attributes) {
		if( resource.attributes.hasOwnProperty( name ) ) {
			resource[name] = resource.attributes[name];
		}
	}

	delete resource.attributes;
	return resource;
}

function error(object) {

	var VALID_KEYS = {
		data: true,
		errors: true,
		meta: true,
		jsonapi: true,
		links: true,
		included: true,
	}

	if(!object) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object was undefined' }
		];
	}

	var invalid = Object.keys(object).filter(function(key) { return VALID_KEYS[key]?false:true; });

	if(invalid.length > 0) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object contains the following invalid key\'s ' + invalid.join(', ') }
		];
	}

	if(!object.data && !object.errors && !object.meta) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object MUST contain at least one of the following top-level members: data, errors, meta' }
		];
	}

	if(object.data && object.errors) {
		return [
			{ status: '400', title: 'parse error', detail: 'top-level members data and errors MUST NOT coexist in the same object' }
		];
	}

	return false;
}

function parseResource(included, item) {
	return resolveRelationships(included, resolveAttributes(clone(item)));
}

module.exports = function(object) {

	// add and expose propper jsonapi validation
	var errors = error(object);
	if(errors) {
		return { errors : errors }
	}

	var included = (object.included || []).reduce(function groupByType(result, resource, index, context) {
		var collection = result[resource.type] || {};
		collection[resource.id] = resource;
		result[resource.type] = collection;
		return result;
	}, {});

	if(object.data instanceof Array) {
		return {
			data :	object.data.map(partial(parseResource, included))
		}
	}
	return {
		data: partial(parseResource, included)(object.data);
	}
};

<commit_msg>fixed typo<commit_after>"use strict";

function partial(func) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function partialFunc() {
    return func.apply(null, args.concat(Array.prototype.slice.call(arguments, 0)));
  };
}

function clone(item) {
	// clone our object, since valid source comes from a json this wont break.
	return JSON.parse(JSON.stringify(item));
}

function flattenIncluded(included, resource) {
	var object = resolveAttributes(included[resource.type][resource.id]);
	var result = resolveRelationships(included, object)
	return result;
}

function resolveData(included, data) {
	if(isIncluded(included, data)) {
		return flattenIncluded(included, data);
	} else {
		return data;
	}
}

function resolveRelationshipData(included, links) {
	if(!links.data) {
		return links;
	} else if(Array.isArray(links.data)) {
		return links.data.map(partial(resolveData, included));
	} else {
		return resolveData(included, links.data);
	}
}

function isIncluded(included, resource) {
	return !(!included[resource.type] || !included[resource.type][resource.id]);
}

function resolveRelationships(included, resource) {
	if(!resource.relationships) {
		return resource;
	}

	Object.keys(resource.relationships).forEach(function(attribute) {
		resource[attribute] = resolveRelationshipData(included, resource.relationships[attribute]);
	});

	delete resource.relationships;
	return resource;
}

function resolveAttributes(resource) {
	for (var name in resource.attributes) {
		if( resource.attributes.hasOwnProperty( name ) ) {
			resource[name] = resource.attributes[name];
		}
	}

	delete resource.attributes;
	return resource;
}

function error(object) {

	var VALID_KEYS = {
		data: true,
		errors: true,
		meta: true,
		jsonapi: true,
		links: true,
		included: true,
	}

	if(!object) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object was undefined' }
		];
	}

	var invalid = Object.keys(object).filter(function(key) { return VALID_KEYS[key]?false:true; });

	if(invalid.length > 0) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object contains the following invalid key\'s ' + invalid.join(', ') }
		];
	}

	if(!object.data && !object.errors && !object.meta) {
		return [
			{ status: '400', title: 'parse error', detail: 'supplied object MUST contain at least one of the following top-level members: data, errors, meta' }
		];
	}

	if(object.data && object.errors) {
		return [
			{ status: '400', title: 'parse error', detail: 'top-level members data and errors MUST NOT coexist in the same object' }
		];
	}

	return false;
}

function parseResource(included, item) {
	return resolveRelationships(included, resolveAttributes(clone(item)));
}

module.exports = function(object) {

	// add and expose propper jsonapi validation
	var errors = error(object);
	if(errors) {
		return { errors : errors }
	}

	var included = (object.included || []).reduce(function groupByType(result, resource, index, context) {
		var collection = result[resource.type] || {};
		collection[resource.id] = resource;
		result[resource.type] = collection;
		return result;
	}, {});

	if(object.data instanceof Array) {
		return {
			data :	object.data.map(partial(parseResource, included))
		}
	}
	return {
		data: partial(parseResource, included)(object.data)
	}
};

<|endoftext|>


<commit_before>'use strict';

var util = require('../util');
var vinylFS = require('vinyl-fs');
var tornado = require('tornado-repl');

var Stack = tornado.Stack;
var Runtime = tornado.Runtime;

// String to function mapping failed, handle it
//
Stack.prototype.onHandleNotFound = function(next){
  var path = util.color.cyan(next.match || next.path);
  console.log('%s found for `%s`', util.color.red('No task'), path);
  console.log('Set one with `task(\'%s\', [Function])`', path);
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Errors anywhere in the stack
//
Stack.prototype.onHandleError = function(error, next){
  if(!(error instanceof Error)){ return next(); }

  util.log('Error in \'%s\' after %s\n%s',
    util.color.cyan(next.match),
    util.color.time(next.time),
    error.stack
  );

  next.error = error;
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Make a logger that looks similar to gulp's
//
Stack.prototype.onHandle = function(next){
  if(next.error || !this.log || /^-/.test(next.match)){
    return ; // ^ skip logging for errors, silent or flags
  }

  var host = '';
  if(this.host){
    host = '(' + util.color.green(this.host.path) + ') ';
  }

  if(!this.time){
    util.log('%sStarted \'%s\' in %s',
      host,
      util.color.cyan(this.path),
      util.color.bold(this.wait ? 'series' : 'parallel')
    );
    this.time = process.hrtime();
  } else if(next.time && next.match && next.match !== this.path){
    util.log('%sFinished \'%s\' after %s',
      host, util.color.cyan(next.match), util.color.time(next.time)
    );
  }

  if(!next.time){ next.time = process.hrtime(); }
  if(this.queue || this.time.end){ return ; }

  this.time.end = util.color.time(this.time);
  util.log('%sFinished \'%s\' after %s',
    host, util.color.cyan(this.path), this.time.end
  );
};

/*
# runtime.src
```js
function src(string|array glob[, object opt])
```
Same as [vinylFs.src][p-vinly-fs-src]
*/

Runtime.prototype.src = vinylFS.src;

/*
# runtime.dest
```js
function dest(string|function folder[, object opt])
```
Same as [vinylFs.dest][p-vinly-fs-dest]
*/
Runtime.prototype.dest = vinylFS.dest;

/*
# runtime.task
```js
function task(string name[, string|array dep, function handle])
```
_arguments_
 - are same as [gulp.task][m-gulp-task] aside of `dep`
 - `dep` type array or string, tasks to run before this one
 if given as a string they must be space separated

_returns_ differences with [gulp.task][m-gulp-task]
 - the function of the task `name`, if it was set before
 - this otherwise (just as [gulp.task][m-gulp-task])
*/

Runtime.prototype.task = function(name, deps, handle){
  var handleType = util.type(handle || deps);

  if(typeof name !== 'string'){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n'+
      '- Tasks require a string `name`'
    );
  } else if(!handleType.match(/function|array|string/)){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n' +
      'New tasks need at lest 2 arguments\n'+
      ' - deps: dependencies to run before this one'+
      ' - handle: function that runs the task\n'+
      '\n'
    );
  }

  var error;
  // TODO: checking handle completion should be more robust
  //
  handle = handleType.function || function(next){ next(); };
  if(!handle.length && !/return/.test(handle.toString())){
    name = util.color.cyan(name);
    error = new Error(
      'task `'+name+'` doesn\'t give a hint of its completion\n'+
      'Meaning: it doesn\'t return nor uses a callback\n'+
      'Solution:\n'+
      ' - return a stream, promise or observable\n'+
      ' - use the callback that is passed as first argument\n--'
    );
    util.color.callersPath(error);
    throw error;
  }

  if(!handle.name && !handle.displayName){
    handle.displayName = name;
  }

  var depsType = util.type(deps);
  if(!depsType.match(/array|string/) || !deps.length){
    return this.set(name, handle);
  }
  deps = depsType.array || deps.trim().split(/[ ]+/);
  this.set(name);
  // prevent circular dependencies
  // TODO: this might to much overhead, think of a better solution
  //
  deps.forEach(function isDepCircular(dep){
    var node = this.get(dep, {ref: true});
    if(node.path !== name){ return ; }

    dep = util.color.red(dep);
    name = util.color.cyan(name);
    regex = util.color.yellow(regex);

    var regex = RegExp(this.regex.filter(function(re){
      return re.path === name;
    })[0].source);

    var error = new Error(
      'task `'+name+'` dependency `'+dep+'` is circular\n'+
      'Meaning: if is run it will call itself\n'+
      'Solution: rewrite it so they don\'t match\n'+
      '- path: ' + name + '\n'+
      '- deps: ' + JSON.stringify(deps) + '\n'+
      '- regex: ' + regex +'\n--'
    );
    // mark caller's file path on the stack
    util.color.callersPath(error, 2);
    throw error;
  }, this);

  return this.set(name, {
    dep: deps,
    handle: this.stack(deps.join(' '), handle, {wait: true})
  });
};

/*
# runtime.watch
```js
function watch(string|array glob[, string|array|object opt, function cb])
```

Same as [gulp.watch][m-gulp-watch] (run tasks when files change).
Additional features:
  - Files can be reloaded on the `require.cache`
  - Tasks can be run in series directly if so specified

_arguments_ differences with [gulp.watch][m-gulp-watch]
 - `tasks` can be a string, space separated tasks will be dispatched as written
 - when `opt` is an object, it can have non mandatory properties below
  - `opt.wait` type boolean, if `opt.tasks` should run in series or not
  - `opt.tasks` type array|string, tasks to run after change and before reload
  - `opt.reload` type boolean, wether or not to reload a file

_defaults_
 - `opt.wait` to `false`
 - `opt.reload` to `undefined`

_returns_
 - a watcher, same as [gulp.watch][m-gulp-watch]

> Notes:
- callback arguments are (a gaze event, the `opt`s given)
- Only files with a registered `require.extension` will be reloaded
- When a file is deleted on disk, the reload will be skipped but the
callback is still invoked

```javascript
var gulp = require('gulp-runtime').create();

var watcher;
watcher = gulp.watch('app/*.js', 'minify browserSync');

gulp.watch('app/*.*', {
  wait: true,  // run tasks after change in series
  tasks: 'sass css jsx browserSync',
  reload: true // will only reload app/*.js files (server, db, etc)
}, function(ev, opt){
  var reloaded = opt.reload && ev.type !== 'delete';
  console.log('%s was %s', ev.path, ev.type, reloaded ? 'and reloaded' : '');
});
```
**/
Runtime.prototype.watch = function(glob, opt, cb){
  cb = util.type(cb || opt).function;
  opt = util.type(opt).plainObject || {tasks: opt};

  var tasks = util.type(opt.tasks);
  if(tasks.string || tasks.array){
    tasks = tasks.string || tasks.array.join(' ');
    if(cb || opt.reload){
      opt.onHandleEnd = function(next, ev){
        if(this.queue || this.host){ return ; }
        if(util.shouldReloadFile(opt, ev)){
          delete require.cache[ev.path];
          require(ev.path);
        }
        if(cb){ cb(ev, opt); }
      };
    }
    return vinylFS.watch(glob, this.stack(tasks, opt));
  }

  return vinylFS.watch(glob, opt, cb);
};

// export back tornado
//
exports = module.exports = tornado;
<commit_msg>keep logging here<commit_after>'use strict';

var util = require('../util');
var vinylFS = require('vinyl-fs');
var tornado = require('tornado-repl');

var Stack = tornado.Stack;
var Runtime = tornado.Runtime;

// String to function mapping failed, handle it
//
Stack.prototype.onHandleNotFound = function(next){
  var path = util.color.cyan(next.match || next.path);
  console.log('%s found for `%s`', util.color.red('No task'), path);
  console.log('Set one with `task(\'%s\', [Function])`', path);
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Errors anywhere in the stack
//
Stack.prototype.onHandleError = function(error, next){
  if(!(error instanceof Error)){ return next(); }

  util.log('Error in \'%s\' after %s\n%s',
    util.color.cyan(next.match),
    util.color.time(next.time),
    error.stack
  );

  next.error = error;
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Make a logger that looks similar to gulp's
//
Stack.prototype.onHandle = function(next){
  if(next.error || !this.log || /^-/.test(next.match)){
    return ; // ^ skip logging for errors, silent or flags
  }

  if(this.configFile === void 0){
    this.configFile = util.configFile();
    this.runtime.set({configFile: this.configFile});
    if(this.configFile instanceof Error){ return ; }
    util.log('Using', util.color.file(this.configFile));
  }

  var host = '';
  if(this.host){
    host = '(' + util.color.green(this.host.path) + ') ';
  }

  if(!this.time){
    util.log('%sStarted \'%s\' in %s',
      host,
      util.color.cyan(this.path),
      util.color.bold(this.wait ? 'series' : 'parallel')
    );
    this.time = process.hrtime();
  } else if(next.time && next.match && next.match !== this.path){
    util.log('%sFinished \'%s\' after %s',
      host, util.color.cyan(next.match), util.color.time(next.time)
    );
  }

  if(!next.time){ next.time = process.hrtime(); }
  if(this.queue || this.time.end){ return ; }

  this.time.end = util.color.time(this.time);
  util.log('%sFinished \'%s\' after %s',
    host, util.color.cyan(this.path), this.time.end
  );
};

/*
# runtime.src
```js
function src(string|array glob[, object opt])
```
Same as [vinylFs.src][p-vinly-fs-src]
*/

Runtime.prototype.src = vinylFS.src;

/*
# runtime.dest
```js
function dest(string|function folder[, object opt])
```
Same as [vinylFs.dest][p-vinly-fs-dest]
*/
Runtime.prototype.dest = vinylFS.dest;

/*
# runtime.task
```js
function task(string name[, string|array dep, function handle])
```
_arguments_
 - are same as [gulp.task][m-gulp-task] aside of `dep`
 - `dep` type array or string, tasks to run before this one
 if given as a string they must be space separated

_returns_ differences with [gulp.task][m-gulp-task]
 - the function of the task `name`, if it was set before
 - this otherwise (just as [gulp.task][m-gulp-task])
*/

Runtime.prototype.task = function(name, deps, handle){
  var handleType = util.type(handle || deps);

  if(typeof name !== 'string'){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n'+
      '- Tasks require a string `name`'
    );
  } else if(!handleType.match(/function|array|string/)){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n' +
      'New tasks need at lest 2 arguments\n'+
      ' - deps: dependencies to run before this one'+
      ' - handle: function that runs the task\n'+
      '\n'
    );
  }

  var error;
  // TODO: checking handle completion should be more robust
  //
  handle = handleType.function || function(next){ next(); };
  if(!handle.length && !/return/.test(handle.toString())){
    name = util.color.cyan(name);
    error = new Error(
      'task `'+name+'` doesn\'t give a hint of its completion\n'+
      'Meaning: it doesn\'t return nor uses a callback\n'+
      'Solution:\n'+
      ' - return a stream, promise or observable\n'+
      ' - use the callback that is passed as first argument\n--'
    );
    util.color.callersPath(error);
    throw error;
  }

  if(!handle.name && !handle.displayName){
    handle.displayName = name;
  }

  var depsType = util.type(deps);
  if(!depsType.match(/array|string/) || !deps.length){
    return this.set(name, handle);
  }
  deps = depsType.array || deps.trim().split(/[ ]+/);
  this.set(name);
  // prevent circular dependencies
  // TODO: this might to much overhead, think of a better solution
  //
  deps.forEach(function isDepCircular(dep){
    var node = this.get(dep, {ref: true});
    if(node.path !== name){ return ; }

    dep = util.color.red(dep);
    name = util.color.cyan(name);
    regex = util.color.yellow(regex);

    var regex = RegExp(this.regex.filter(function(re){
      return re.path === name;
    })[0].source);

    var error = new Error(
      'task `'+name+'` dependency `'+dep+'` is circular\n'+
      'Meaning: if is run it will call itself\n'+
      'Solution: rewrite it so they don\'t match\n'+
      '- path: ' + name + '\n'+
      '- deps: ' + JSON.stringify(deps) + '\n'+
      '- regex: ' + regex +'\n--'
    );
    // mark caller's file path on the stack
    util.color.callersPath(error, 2);
    throw error;
  }, this);

  return this.set(name, {
    dep: deps,
    handle: this.stack(deps.join(' '), handle, {wait: true})
  });
};

/*
# runtime.watch
```js
function watch(string|array glob[, string|array|object opt, function cb])
```

Same as [gulp.watch][m-gulp-watch] (run tasks when files change).
Additional features:
  - Files can be reloaded on the `require.cache`
  - Tasks can be run in series directly if so specified

_arguments_ differences with [gulp.watch][m-gulp-watch]
 - `tasks` can be a string, space separated tasks will be dispatched as written
 - when `opt` is an object, it can have non mandatory properties below
  - `opt.wait` type boolean, if `opt.tasks` should run in series or not
  - `opt.tasks` type array|string, tasks to run after change and before reload
  - `opt.reload` type boolean, wether or not to reload a file

_defaults_
 - `opt.wait` to `false`
 - `opt.reload` to `undefined`

_returns_
 - a watcher, same as [gulp.watch][m-gulp-watch]

> Notes:
- callback arguments are (a gaze event, the `opt`s given)
- Only files with a registered `require.extension` will be reloaded
- When a file is deleted on disk, the reload will be skipped but the
callback is still invoked

```javascript
var gulp = require('gulp-runtime').create();

var watcher;
watcher = gulp.watch('app/*.js', 'minify browserSync');

gulp.watch('app/*.*', {
  wait: true,  // run tasks after change in series
  tasks: 'sass css jsx browserSync',
  reload: true // will only reload app/*.js files (server, db, etc)
}, function(ev, opt){
  var reloaded = opt.reload && ev.type !== 'delete';
  console.log('%s was %s', ev.path, ev.type, reloaded ? 'and reloaded' : '');
});
```
**/
Runtime.prototype.watch = function(glob, opt, cb){
  cb = util.type(cb || opt).function;
  opt = util.type(opt).plainObject || {tasks: opt};

  var tasks = util.type(opt.tasks);
  if(tasks.string || tasks.array){
    tasks = tasks.string || tasks.array.join(' ');
    if(cb || opt.reload){
      opt.onHandleEnd = function(next, ev){
        if(this.queue || this.host){ return ; }
        if(util.shouldReloadFile(opt, ev)){
          delete require.cache[ev.path];
          require(ev.path);
        }
        if(cb){ cb(ev, opt); }
      };
    }
    return vinylFS.watch(glob, this.stack(tasks, opt));
  }

  return vinylFS.watch(glob, opt, cb);
};

// export back tornado
//
exports = module.exports = tornado;
<|endoftext|>