<commit_before>"use strict";

var Class = require("./Class");

var Util = exports.Util = Class.extend({

	$serializeArray: function (a) {
		if (a == null)
			return null;
		var ret = [];
		for (var i = 0; i < a.length; ++i)
			ret[i] = a[i].serialize();
		return ret;
	},

	$serializeNullable: function (v) {
		if (v == null)
			return null;
		return v.serialize();
	},

	$repeat: function(c, n) {
		var s = "";
		for(var i = 0; i < n; ++i) {
			s += c;
		}
		return s;
	},

	// Usage: format("%1 %% %2", ["foo", "bar"]) -> "foo % bar"
	$format: function(fmt, args) {
		if(!(args instanceof Array)) {
			throw new Error("args must be an Array");
		}

		var i = 0;
		return fmt.replace(/%(\d+|%)/g, function(s, f) {
			if (f === "%") {
				return "%";
			}
			else {
				return args[parseInt(f) - 1];
			}
		});
	},

	$analyzeArgs: function (context, args) {
		var argTypes = [];
		for (var i = 0; i < args.length; ++i) {
			if (! args[i].analyze(context))
				return null;
			argTypes[i] = args[i].getType();
		}
		return argTypes;
	},

	$typesAreEqual : function (x, y) {
		if (x.length != y.length)
			return false;
		for (var i = 0; i < x.length; ++i)
			if (! x[i].equals(y))
				return false;
		return true;
	},

	$decodeStringLiteral: function (literal) {
		var matched = literal.match(/^([\'\"]).*([\'\"])$/);
		if (matched == null || matched[1] != matched[2])
			throw new Error("input string is not quoted properly: " + literal);
		var src = literal.substring(1, literal.length - 1);
		var decoded = "";
		var pos = 0, backslashAt;
		while ((backslashAt = src.indexOf("\\", pos)) != -1) {
			// copy the string before backslash
			decoded += src.substring(pos, backslashAt);
			pos = backslashAt + 1;
			// decode
			if (pos == src.length)
				throw new Error("last character within a string literal cannot be a backslash: " + literal);
			var escapeChar = src.charAt(pos++);
			switch (escapeChar) {
			case "'":
			case "\"":
			case "\\":
				decoded += escapeChar;
				break;
			case "b": decoded += "\b"; break;
			case "f": decoded += "\f"; break;
			case "n": decoded += "\n"; break;
			case "r": decoded += "\r"; break;
			case "t": decoded += "\t"; break;
			case "v": decoded += "\v"; break;
			case "u":
				var matched = src.substring(pos).match(/^([0-9A-Fa-f]{4})/);
				if (matched == null)
					throw new Error("expected four hexdigits after \\u: " + literal);
				decoded += String.fromCharCode(parseInt(matched[1], 16));
				pos += 4;
				break;
			case "0":
				if (pos == src.length || src.charAt(pos).match(/[0-9]/) == null)
					decoded += "\0";
				else
					throw new Error("found a digit after '\\0': " + literal);
				break;
			}
		}
		decoded += src.substring(pos);
		return decoded;
	},

	$resolvePath: function (path) {
		var tokens = path.split("/");
		for (var i = 0; i < tokens.length;) {
			if (tokens[i] == ".") {
				tokens.splice(i, 1);
			} else if (tokens[i] == ".." && i != 0 && tokens[i - 1] != "..") {
				if (i == 1 && tokens[0] == "") {
					tokens.splice(i, 1);
				} else {
					tokens.splice(i - 1, 2);
					i -= 1;
				}
			} else {
				i++;
			}
		}
		return tokens.join("/");
	}

});

var TemplateInstantiationRequest = exports.TemplateInstantiationRequest = Class.extend({

	initialize: function (token, className, typeArgs) {
		this._token = token;
		this._className = className;
		this._typeArgs = typeArgs;
	},

	getToken: function() {
		return this._token;
	},

	getClassName: function () {
		return this._className;
	},

	getTypeArguments: function () {
		return this._typeArgs;
	}

});

var CompileError = exports.CompileError = Class.extend({

	initialize: function () {
		switch (arguments.length) {
		case 2: // token, text
			if(token != null) {
				var token = arguments[0];
				this._filename = token.getFilename();
				this._lineNumber = token.getLineNumber();
				this._columnNumber = token.getColumnNumber();
				// FIXME: deal with visual width
				this._size = token.getValue().length;
				this._message = arguments[1];
			}
			else {
				CompileError.call(this, "(unknown)", 0, 0, arguments[1]);
				return;
			}
			break;
		case 4: // filename, lineNumber, columnNumber, text
			this._filename = arguments[0];
			this._lineNumber = arguments[1];
			this._columnNumber = arguments[2];
			this._message = arguments[3];
			this._size = 1;
			break;
		default:
			throw new Error("Unrecognized arguments for CompileError: " + JSON.stringify( Array.prototype.slice.call(arguments) ));

		}
		if(typeof(this._filename) !== "string")
			throw new Error("filename is not a string");
		if(typeof(this._lineNumber) !== "number")
			throw new Error("lineNumber is not a number");
		if(typeof(this._columnNumber) !== "number")
			throw new Error("columnNumber is not a number");
	},

	format: function (compiler) {
		if (this._filename == null) {
			return this._message + "\n";
		}
		else if (this._lineNumber === 0) {
			return Util.format("[%1] %2\n", [ this._filename, this._message ]);
		}

		var content = compiler.getFileContent([] /* ignore errors */, null, this._filename);
		var sourceLine = content.split(/^/m)[ this._lineNumber - 1 ];

		// fix visual width
		var col = this._columnNumber;
		var TAB_WIDTH = 4;
		var tabs = sourceLine.slice(0, col).match(/\t/g);
		if(tabs != null) {
			col += (TAB_WIDTH-1) * tabs.length;
		}

		sourceLine  = sourceLine.replace(/\t/g, Util.repeat(" ", TAB_WIDTH));
		sourceLine += Util.repeat(" ", col);
		sourceLine += Util.repeat("^", this._size);

		return Util.format("[%1:%2] %3\n%4\n",
						   [this._filename, this._lineNumber, this._message, sourceLine]);
	}

});

// vim: set noexpandtab:
<commit_msg>Fix src/util.js respecting ad3ea9a<commit_after>"use strict";

var Class = require("./Class");

var Util = exports.Util = Class.extend({

	$serializeArray: function (a) {
		if (a == null)
			return null;
		var ret = [];
		for (var i = 0; i < a.length; ++i)
			ret[i] = a[i].serialize();
		return ret;
	},

	$serializeNullable: function (v) {
		if (v == null)
			return null;
		return v.serialize();
	},

	$repeat: function(c, n) {
		var s = "";
		for(var i = 0; i < n; ++i) {
			s += c;
		}
		return s;
	},

	// Usage: format("%1 %% %2", ["foo", "bar"]) -> "foo % bar"
	$format: function(fmt, args) {
		if(!(args instanceof Array)) {
			throw new Error("args must be an Array");
		}

		var i = 0;
		return fmt.replace(/%(\d+|%)/g, function(s, f) {
			if (f === "%") {
				return "%";
			}
			else {
				return args[parseInt(f) - 1];
			}
		});
	},

	$analyzeArgs: function (context, args) {
		var argTypes = [];
		for (var i = 0; i < args.length; ++i) {
			if (! args[i].analyze(context))
				return null;
			argTypes[i] = args[i].getType();
		}
		return argTypes;
	},

	$typesAreEqual : function (x, y) {
		if (x.length != y.length)
			return false;
		for (var i = 0; i < x.length; ++i)
			if (! x[i].equals(y))
				return false;
		return true;
	},

	$decodeStringLiteral: function (literal) {
		var matched = literal.match(/^([\'\"]).*([\'\"])$/);
		if (matched == null || matched[1] != matched[2])
			throw new Error("input string is not quoted properly: " + literal);
		var src = literal.substring(1, literal.length - 1);
		var decoded = "";
		var pos = 0, backslashAt;
		while ((backslashAt = src.indexOf("\\", pos)) != -1) {
			// copy the string before backslash
			decoded += src.substring(pos, backslashAt);
			pos = backslashAt + 1;
			// decode
			if (pos == src.length)
				throw new Error("last character within a string literal cannot be a backslash: " + literal);
			var escapeChar = src.charAt(pos++);
			switch (escapeChar) {
			case "'":
			case "\"":
			case "\\":
				decoded += escapeChar;
				break;
			case "b": decoded += "\b"; break;
			case "f": decoded += "\f"; break;
			case "n": decoded += "\n"; break;
			case "r": decoded += "\r"; break;
			case "t": decoded += "\t"; break;
			case "v": decoded += "\v"; break;
			case "u":
				var matched = src.substring(pos).match(/^([0-9A-Fa-f]{4})/);
				if (matched == null)
					throw new Error("expected four hexdigits after \\u: " + literal);
				decoded += String.fromCharCode(parseInt(matched[1], 16));
				pos += 4;
				break;
			case "0":
				if (pos == src.length || src.charAt(pos).match(/[0-9]/) == null)
					decoded += "\0";
				else
					throw new Error("found a digit after '\\0': " + literal);
				break;
			}
		}
		decoded += src.substring(pos);
		return decoded;
	},

	$resolvePath: function (path) {
		var tokens = path.split("/");
		for (var i = 0; i < tokens.length;) {
			if (tokens[i] == ".") {
				tokens.splice(i, 1);
			} else if (tokens[i] == ".." && i != 0 && tokens[i - 1] != "..") {
				if (i == 1 && tokens[0] == "") {
					tokens.splice(i, 1);
				} else {
					tokens.splice(i - 1, 2);
					i -= 1;
				}
			} else {
				i++;
			}
		}
		return tokens.join("/");
	}

});

var TemplateInstantiationRequest = exports.TemplateInstantiationRequest = Class.extend({

	initialize: function (token, className, typeArgs) {
		this._token = token;
		this._className = className;
		this._typeArgs = typeArgs;
	},

	getToken: function() {
		return this._token;
	},

	getClassName: function () {
		return this._className;
	},

	getTypeArguments: function () {
		return this._typeArgs;
	}

});

var CompileError = exports.CompileError = Class.extend({

	initialize: function () {
		switch (arguments.length) {
		case 2: // token, text
			var token = arguments[0];
			if(token != null) {
				this._filename = token.getFilename();
				this._lineNumber = token.getLineNumber();
				this._columnNumber = token.getColumnNumber();
				// FIXME: deal with visual width
				this._size = token.getValue().length;
				this._message = arguments[1];
			}
			else {
				CompileError.call(this, null, 0, 0, arguments[1]);
				return;
			}
			break;
		case 4: // filename, lineNumber, columnNumber, text
			this._filename = arguments[0];
			this._lineNumber = arguments[1];
			this._columnNumber = arguments[2];
			this._message = arguments[3];
			this._size = 1;
			break;
		default:
			throw new Error("Unrecognized arguments for CompileError: " + JSON.stringify( Array.prototype.slice.call(arguments) ));

		}
		if(!(this._filename == null || typeof(this._filename) === "string"))
			throw new Error("filename is not a string nor null");
		if(typeof(this._lineNumber) !== "number")
			throw new Error("lineNumber is not a number");
		if(typeof(this._columnNumber) !== "number")
			throw new Error("columnNumber is not a number");
	},

	format: function (compiler) {
		if (this._filename == null) {
			return this._message + "\n";
		}
		else if (this._lineNumber === 0) {
			return Util.format("[%1] %2\n", [ this._filename, this._message ]);
		}

		var content = compiler.getFileContent([] /* ignore errors */, null, this._filename);
		var sourceLine = content.split(/^/m)[ this._lineNumber - 1 ];

		// fix visual width
		var col = this._columnNumber;
		var TAB_WIDTH = 4;
		var tabs = sourceLine.slice(0, col).match(/\t/g);
		if(tabs != null) {
			col += (TAB_WIDTH-1) * tabs.length;
		}

		sourceLine  = sourceLine.replace(/\t/g, Util.repeat(" ", TAB_WIDTH));
		sourceLine += Util.repeat(" ", col);
		sourceLine += Util.repeat("^", this._size);

		return Util.format("[%1:%2] %3\n%4\n",
						   [this._filename, this._lineNumber, this._message, sourceLine]);
	}

});

// vim: set noexpandtab:
<|endoftext|>


<commit_before>
import path from 'path';
import fs from 'mz/fs';

export function log() {
  console.log.apply(console, arguments);
}

export function title(title) {
  console.log(title.white);
}

export function error(msg) {
  console.log('error: '.red + msg);
}

export function createConfigs(dirs, script = 'start') {
  let cwd = process.cwd();
  let base = path.basename(cwd);
  return dirs.map(dir => {
    let uid  = base + '-' + dir;
    let logFile = path.join(cwd, '.goodly', uid + '.log' );
    return {
      uid: uid,
      append: true,
      watch: false,
      command: 'npm run',
      script: script,
      cwd: path.join(cwd, dir),
      logFile: logFile,
      max: 1
    };
  });
}

export async function getdirs(script = 'start') {
  let cwd = process.cwd();
  let results  = [];
  let subpaths = await fs.readdir(cwd);
  for(let subpath of subpaths) {
    let stats = await fs.stat(subpath);
    if(stats.isDirectory()) {
      let packagePath = path.join(subpath, 'package.json');
      if(await fs.exists(packagePath)) {
        let packageBytes = await fs.readFile(packagePath);
        let pack = JSON.parse(packageBytes);
        if(pack.scripts && pack.scripts[script]) {
          results.push(subpath);
        }
      }
    }
  }
  return results;
}

export async function createLogDir() {
  let exists = await fs.exists('.lightbird');
  if(!exists)
    await fs.mkdir('.lightbird');
}<commit_msg>Moved path to .lightbird<commit_after>
import path from 'path';
import fs from 'mz/fs';

export function log() {
  console.log.apply(console, arguments);
}

export function title(title) {
  console.log(title.white);
}

export function error(msg) {
  console.log('error: '.red + msg);
}

export function createConfigs(dirs, script = 'start') {
  let cwd = process.cwd();
  let base = path.basename(cwd);
  return dirs.map(dir => {
    let uid  = base + '-' + dir;
    let logFile = path.join(cwd, '.lightbird', uid + '.log' );
    return {
      uid: uid,
      append: true,
      watch: false,
      command: 'npm run',
      script: script,
      cwd: path.join(cwd, dir),
      logFile: logFile,
      max: 1
    };
  });
}

export async function getdirs(script = 'start') {
  let cwd = process.cwd();
  let results  = [];
  let subpaths = await fs.readdir(cwd);
  for(let subpath of subpaths) {
    let stats = await fs.stat(subpath);
    if(stats.isDirectory()) {
      let packagePath = path.join(subpath, 'package.json');
      if(await fs.exists(packagePath)) {
        let packageBytes = await fs.readFile(packagePath);
        let pack = JSON.parse(packageBytes);
        if(pack.scripts && pack.scripts[script]) {
          results.push(subpath);
        }
      }
    }
  }
  return results;
}

export async function createLogDir() {
  let exists = await fs.exists('.lightbird');
  if(!exists)
    await fs.mkdir('.lightbird');
}<|endoftext|>


<commit_before>var u = module.exports;

// utility functions

var FNAME = '__name__';

u.namedfunc = function(name, f) { return (f[FNAME] = name, f); };

u.name = function(f) { return f==null ? null : f[FNAME]; };

u.identity = function(x) { return x; };

u.true = u.namedfunc('true', function() { return true; });

u.false = u.namedfunc('false', function() { return false; });

u.duplicate = function(obj) {
  return JSON.parse(JSON.stringify(obj));
};

u.equal = function(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
};

u.extend = function(obj) {
  for (var x, name, i=1, len=arguments.length; i<len; ++i) {
    x = arguments[i];
    for (name in x) { obj[name] = x[name]; }
  }
  return obj;
};

u.length = function(x) {
  return x != null && x.length != null ? x.length : null;
};

u.keys = function(x) {
  var keys = [], k;
  for (k in x) keys.push(k);
  return keys;
};

u.vals = function(x) {
  var vals = [], k;
  for (k in x) vals.push(x[k]);
  return vals;
};

u.toMap = function(list, f) {
  return (f = u.$(f)) ?
    list.reduce(function(obj, x) { return (obj[f(x)] = 1, obj); }, {}) :
    list.reduce(function(obj, x) { return (obj[x] = 1, obj); }, {});
};

u.keystr = function(values) {
  // use to ensure consistent key generation across modules
  var n = values.length;
  if (!n) return '';
  for (var s=String(values[0]), i=1; i<n; ++i) {
    s += '|' + String(values[i]);
  }
  return s;
};

// type checking functions

var toString = Object.prototype.toString;

u.isObject = function(obj) {
  return obj === Object(obj);
};

u.isFunction = function(obj) {
  return toString.call(obj) === '[object Function]';
};

u.isString = function(obj) {
  return typeof value === 'string' || toString.call(obj) === '[object String]';
};

u.isArray = Array.isArray || function(obj) {
  return toString.call(obj) === '[object Array]';
};

u.isNumber = function(obj) {
  return typeof obj === 'number' || toString.call(obj) === '[object Number]';
};

u.isBoolean = function(obj) {
  return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};

u.isDate = function(obj) {
  return toString.call(obj) === '[object Date]';
};

u.isValid = function(obj) {
  return obj != null && obj === obj;
};

u.isBuffer = (typeof Buffer === 'function' && Buffer.isBuffer) || u.false;

// type coercion functions

u.number = function(s) {
  return s == null || s === '' ? null : +s;
};

u.boolean = function(s) {
  return s == null || s === '' ? null : s==='false' ? false : !!s;
};

// parse a date with optional d3.time-format format
u.date = function(s, format) {
  var d = format ? format : Date;
  return s == null || s === '' ? null : d.parse(s);
};

u.array = function(x) {
  return x != null ? (u.isArray(x) ? x : [x]) : [];
};

u.str = function(x) {
  return u.isArray(x) ? '[' + x.map(u.str) + ']'
    : u.isObject(x) ? JSON.stringify(x)
    : u.isString(x) ? ('\''+util_escape_str(x)+'\'') : x;
};

var escape_str_re = /(^|[^\\])'/g;

function util_escape_str(x) {
  return x.replace(escape_str_re, '$1\\\'');
}

// data access functions

var field_re = /\[(.*?)\]|[^.\[]+/g;

u.field = function(f) {
  return String(f).match(field_re).map(function(d) {
    return d[0] !== '[' ? d :
      d[1] !== "'" && d[1] !== '"' ? d.slice(1, -1) :
      d.slice(2, -2).replace(/\\(["'])/g, '$1');
  });
};

u.accessor = function(f) {
  var s;
  return f==null || u.isFunction(f) ? f :
    u.namedfunc(f, (s = u.field(f)).length > 1 ?
      function(x) { return s.reduce(function(x,f) { return x[f]; }, x); } :
      function(x) { return x[f]; }
    );
};

// short-cut for accessor
u.$ = u.accessor;

u.mutator = function(f) {
  var s;
  return u.isString(f) && (s=u.field(f)).length > 1 ?
    function(x, v) {
      for (var i=0; i<s.length-1; ++i) x = x[s[i]];
      x[s[i]] = v;
    } :
    function(x, v) { x[f] = v; };
};

u.$func = function(name, op) {
  return function(f) {
    f = u.$(f) || u.identity;
    var n = name + (u.name(f) ? '_'+u.name(f) : '');
    return u.namedfunc(n, function(d) { return op(f(d)); });
  };
};

u.$valid  = u.$func('valid', u.isValid);
u.$length = u.$func('length', u.length);

u.$in = function(f, values) {
  f = u.$(f);
  var map = u.isArray(values) ? u.toMap(values) : values;
  return function(d) { return !!map[f(d)]; };
};

// comparison / sorting functions

u.comparator = function(sort) {
  var sign = [];
  if (sort === undefined) sort = [];
  sort = u.array(sort).map(function(f) {
    var s = 1;
    if      (f[0] === '-') { s = -1; f = f.slice(1); }
    else if (f[0] === '+') { s = +1; f = f.slice(1); }
    sign.push(s);
    return u.accessor(f);
  });
  return function(a,b) {
    var i, n, f, x, y;
    for (i=0, n=sort.length; i<n; ++i) {
      f = sort[i]; x = f(a); y = f(b);
      if (x < y) return -1 * sign[i];
      if (x > y) return sign[i];
    }
    return 0;
  };
};

u.cmp = function(a, b) {
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  } else if (a >= b) {
    return 0;
  } else if (a === null) {
    return -1;
  } else if (b === null) {
    return 1;
  }
  return NaN;
};

u.numcmp = function(a, b) { return a - b; };

u.stablesort = function(array, sortBy, keyFn) {
  var indices = array.reduce(function(idx, v, i) {
    return (idx[keyFn(v)] = i, idx);
  }, {});

  array.sort(function(a, b) {
    var sa = sortBy(a),
        sb = sortBy(b);
    return sa < sb ? -1 : sa > sb ? 1
         : (indices[keyFn(a)] - indices[keyFn(b)]);
  });

  return array;
};

// string functions

u.pad = function(s, length, pos, padchar) {
  padchar = padchar || " ";
  var d = length - s.length;
  if (d <= 0) return s;
  switch (pos) {
    case 'left':
      return strrep(d, padchar) + s;
    case 'middle':
    case 'center':
      return strrep(Math.floor(d/2), padchar) +
         s + strrep(Math.ceil(d/2), padchar);
    default:
      return s + strrep(d, padchar);
  }
};

function strrep(n, str) {
  var s = "", i;
  for (i=0; i<n; ++i) s += str;
  return s;
}

u.truncate = function(s, length, pos, word, ellipsis) {
  var len = s.length;
  if (len <= length) return s;
  ellipsis = ellipsis !== undefined ? String(ellipsis) : '\u2026';
  var l = Math.max(0, length - ellipsis.length);

  switch (pos) {
    case 'left':
      return ellipsis + (word ? truncateOnWord(s,l,1) : s.slice(len-l));
    case 'middle':
    case 'center':
      var l1 = Math.ceil(l/2), l2 = Math.floor(l/2);
      return (word ? truncateOnWord(s,l1) : s.slice(0,l1)) +
        ellipsis + (word ? truncateOnWord(s,l2,1) : s.slice(len-l2));
    default:
      return (word ? truncateOnWord(s,l) : s.slice(0,l)) + ellipsis;
  }
};

function truncateOnWord(s, len, rev) {
  var cnt = 0, tok = s.split(truncate_word_re);
  if (rev) {
    s = (tok = tok.reverse())
      .filter(function(w) { cnt += w.length; return cnt <= len; })
      .reverse();
  } else {
    s = tok.filter(function(w) { cnt += w.length; return cnt <= len; });
  }
  return s.length ? s.join('').trim() : tok[0].slice(0, len);
}

var truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/;
<commit_msg>Compile accessor functions<commit_after>var u = module.exports;

// utility functions

var FNAME = '__name__';

u.namedfunc = function(name, f) { return (f[FNAME] = name, f); };

u.name = function(f) { return f==null ? null : f[FNAME]; };

u.identity = function(x) { return x; };

u.true = u.namedfunc('true', function() { return true; });

u.false = u.namedfunc('false', function() { return false; });

u.duplicate = function(obj) {
  return JSON.parse(JSON.stringify(obj));
};

u.equal = function(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
};

u.extend = function(obj) {
  for (var x, name, i=1, len=arguments.length; i<len; ++i) {
    x = arguments[i];
    for (name in x) { obj[name] = x[name]; }
  }
  return obj;
};

u.length = function(x) {
  return x != null && x.length != null ? x.length : null;
};

u.keys = function(x) {
  var keys = [], k;
  for (k in x) keys.push(k);
  return keys;
};

u.vals = function(x) {
  var vals = [], k;
  for (k in x) vals.push(x[k]);
  return vals;
};

u.toMap = function(list, f) {
  return (f = u.$(f)) ?
    list.reduce(function(obj, x) { return (obj[f(x)] = 1, obj); }, {}) :
    list.reduce(function(obj, x) { return (obj[x] = 1, obj); }, {});
};

u.keystr = function(values) {
  // use to ensure consistent key generation across modules
  var n = values.length;
  if (!n) return '';
  for (var s=String(values[0]), i=1; i<n; ++i) {
    s += '|' + String(values[i]);
  }
  return s;
};

// type checking functions

var toString = Object.prototype.toString;

u.isObject = function(obj) {
  return obj === Object(obj);
};

u.isFunction = function(obj) {
  return toString.call(obj) === '[object Function]';
};

u.isString = function(obj) {
  return typeof value === 'string' || toString.call(obj) === '[object String]';
};

u.isArray = Array.isArray || function(obj) {
  return toString.call(obj) === '[object Array]';
};

u.isNumber = function(obj) {
  return typeof obj === 'number' || toString.call(obj) === '[object Number]';
};

u.isBoolean = function(obj) {
  return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};

u.isDate = function(obj) {
  return toString.call(obj) === '[object Date]';
};

u.isValid = function(obj) {
  return obj != null && obj === obj;
};

u.isBuffer = (typeof Buffer === 'function' && Buffer.isBuffer) || u.false;

// type coercion functions

u.number = function(s) {
  return s == null || s === '' ? null : +s;
};

u.boolean = function(s) {
  return s == null || s === '' ? null : s==='false' ? false : !!s;
};

// parse a date with optional d3.time-format format
u.date = function(s, format) {
  var d = format ? format : Date;
  return s == null || s === '' ? null : d.parse(s);
};

u.array = function(x) {
  return x != null ? (u.isArray(x) ? x : [x]) : [];
};

u.str = function(x) {
  return u.isArray(x) ? '[' + x.map(u.str) + ']'
    : u.isObject(x) ? JSON.stringify(x)
    : u.isString(x) ? ('\''+util_escape_str(x)+'\'') : x;
};

var escape_str_re = /(^|[^\\])'/g;

function util_escape_str(x) {
  return x.replace(escape_str_re, '$1\\\'');
}

// data access functions

var field_re = /\[(.*?)\]|[^.\[]+/g;

u.field = function(f) {
  return String(f).match(field_re).map(function(d) {
    return d[0] !== '[' ? d :
      d[1] !== "'" && d[1] !== '"' ? d.slice(1, -1) :
      d.slice(2, -2).replace(/\\(["'])/g, '$1');
  });
};

u.accessor = function(f) {
  return f==null || u.isFunction(f) ? f :
    u.namedfunc(f, Function('x', 'return x[' + u.field(f).map(u.str).join('][') + ']'));
}

// short-cut for accessor
u.$ = u.accessor;

u.mutator = function(f) {
  var s;
  return u.isString(f) && (s=u.field(f)).length > 1 ?
    function(x, v) {
      for (var i=0; i<s.length-1; ++i) x = x[s[i]];
      x[s[i]] = v;
    } :
    function(x, v) { x[f] = v; };
};

u.$func = function(name, op) {
  return function(f) {
    f = u.$(f) || u.identity;
    var n = name + (u.name(f) ? '_'+u.name(f) : '');
    return u.namedfunc(n, function(d) { return op(f(d)); });
  };
};

u.$valid  = u.$func('valid', u.isValid);
u.$length = u.$func('length', u.length);

u.$in = function(f, values) {
  f = u.$(f);
  var map = u.isArray(values) ? u.toMap(values) : values;
  return function(d) { return !!map[f(d)]; };
};

// comparison / sorting functions

u.comparator = function(sort) {
  var sign = [];
  if (sort === undefined) sort = [];
  sort = u.array(sort).map(function(f) {
    var s = 1;
    if      (f[0] === '-') { s = -1; f = f.slice(1); }
    else if (f[0] === '+') { s = +1; f = f.slice(1); }
    sign.push(s);
    return u.accessor(f);
  });
  return function(a,b) {
    var i, n, f, x, y;
    for (i=0, n=sort.length; i<n; ++i) {
      f = sort[i]; x = f(a); y = f(b);
      if (x < y) return -1 * sign[i];
      if (x > y) return sign[i];
    }
    return 0;
  };
};

u.cmp = function(a, b) {
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  } else if (a >= b) {
    return 0;
  } else if (a === null) {
    return -1;
  } else if (b === null) {
    return 1;
  }
  return NaN;
};

u.numcmp = function(a, b) { return a - b; };

u.stablesort = function(array, sortBy, keyFn) {
  var indices = array.reduce(function(idx, v, i) {
    return (idx[keyFn(v)] = i, idx);
  }, {});

  array.sort(function(a, b) {
    var sa = sortBy(a),
        sb = sortBy(b);
    return sa < sb ? -1 : sa > sb ? 1
         : (indices[keyFn(a)] - indices[keyFn(b)]);
  });

  return array;
};

// string functions

u.pad = function(s, length, pos, padchar) {
  padchar = padchar || " ";
  var d = length - s.length;
  if (d <= 0) return s;
  switch (pos) {
    case 'left':
      return strrep(d, padchar) + s;
    case 'middle':
    case 'center':
      return strrep(Math.floor(d/2), padchar) +
         s + strrep(Math.ceil(d/2), padchar);
    default:
      return s + strrep(d, padchar);
  }
};

function strrep(n, str) {
  var s = "", i;
  for (i=0; i<n; ++i) s += str;
  return s;
}

u.truncate = function(s, length, pos, word, ellipsis) {
  var len = s.length;
  if (len <= length) return s;
  ellipsis = ellipsis !== undefined ? String(ellipsis) : '\u2026';
  var l = Math.max(0, length - ellipsis.length);

  switch (pos) {
    case 'left':
      return ellipsis + (word ? truncateOnWord(s,l,1) : s.slice(len-l));
    case 'middle':
    case 'center':
      var l1 = Math.ceil(l/2), l2 = Math.floor(l/2);
      return (word ? truncateOnWord(s,l1) : s.slice(0,l1)) +
        ellipsis + (word ? truncateOnWord(s,l2,1) : s.slice(len-l2));
    default:
      return (word ? truncateOnWord(s,l) : s.slice(0,l)) + ellipsis;
  }
};

function truncateOnWord(s, len, rev) {
  var cnt = 0, tok = s.split(truncate_word_re);
  if (rev) {
    s = (tok = tok.reverse())
      .filter(function(w) { cnt += w.length; return cnt <= len; })
      .reverse();
  } else {
    s = tok.filter(function(w) { cnt += w.length; return cnt <= len; });
  }
  return s.length ? s.join('').trim() : tok[0].slice(0, len);
}

var truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/;
<|endoftext|>


<commit_before>function clone(obj) {
    return JSON.parse(JSON.stringify(obj));
}

function generateCombinations(n) {
    var combs = [];
    var comb;
    var str;

    for (var i = 0; i < Math.pow(2, n); i++) {
        str = i.toString(2);
        comb = [];

        for (var j = 0; j < n; j++)
            comb.push(j < n - str.length ? 0 : +str[j - n + str.length]);

        combs.push(comb.slice(0));
    }

    return combs;
}
<commit_msg>Describe generateCombinations function<commit_after>function clone(obj) {
    return JSON.parse(JSON.stringify(obj));
}

//for n = 2 it returns
// [ 0, 0 ]
// [ 0, 1 ]
// [ 1, 0 ]
// [ 1, 1 ]
function generateCombinations(n) {
    var combs = [];
    var comb;
    var str;

    for (var i = 0; i < Math.pow(2, n); i++) {
        str = i.toString(2);
        comb = [];

        for (var j = 0; j < n; j++)
            comb.push(j < n - str.length ? 0 : +str[j - n + str.length]);

        combs.push(comb.slice(0));
    }

    return combs;
}
<|endoftext|>


<commit_before>const _ = require('lodash');
const Promise = require("bluebird");
const fs = require('fs');
const net = require('net');
const os = require('os');
const solc = require('solc');
const path = require('path');
const crypto = require('crypto');
const Tx = require('ethereumjs-tx');
const chalk = require('chalk');
const read = require('read');
const Web3 = require('web3');
const util = require('util');
const ethUtil = require('ethereumjs-util');
const salt = '2';
var web3;
Promise.config({
  longStackTraces: false
});

function getWeb3() {
  if (web3) {
    return web3;
  }

  var argv = require('yargs').argv;
  if (argv.g) {
    web3 = new Web3(new Web3.providers.IpcProvider(argv.g, net));
  } else {
    var ip = '127.0.0.1', port = 8545;
    if (argv.r) {
      var ipPort = argv.r.split(':');
      ip = ipPort[0];
      if (ipPort.length>1) {
        port = parseInt(ipPort[1], 10);
      }
    }

    web3 = new Web3(new Web3.providers.HttpProvider(`http://${ip}:${port}`));
  }

  return web3;
}

function privateToAddress(privateKey) {
  var public = ethUtil.privateToPublic(new Buffer(privateKey, 'hex'));
  return '0x' + ethUtil.publicToAddress(public).toString('hex');
}

function getNonce(address) {
  if (_.isArray(address)) {
    return Promise.map(address, getNonce).then(function(res) {
      return _.zipObject(address, res);
    });
  }

  return new Promise(function(resolve, reject) {
    getWeb3().eth.getTransactionCount(address, function(error, nonce) {
      if (error) {
        reject(error);
      } else {
        resolve(nonce);
      }
    });
  });
};

function signWithNonce(privateKey, nonce, from, to, amount, data) {
  return new Promise(function(resolve, reject) {
    var rawTx = {
      value: getWeb3().fromDecimal(amount),
      to: to,
      data: data,
      from: from,
    };

    getWeb3().eth.getGasPrice(function(error, price) {
      if (error) {
        reject(error);
        return;
      }
      getWeb3().eth.estimateGas(rawTx, function(error, gasUsage) {
        if (error) {
          reject(error);
          return;
        }
        // var forceLimit = 4700000;
        // if (gasUsage>forceLimit) {
        //   console.log(chalk.red(`Needed gas is too high: ${gasUsage}`));
        //   process.exit();
        // }
        //
        gasUsage = Math.min(gasUsage * 2, 3000000);

        var tx = new Tx({
          nonce: (getWeb3().toHex(nonce)),
          to: rawTx.to,
          value: rawTx.value,
          from: from,
          gasPrice: getWeb3().toHex(price),
          gasLimit: getWeb3().toHex(gasUsage),
          data: data
        });
        var key = new Buffer(privateKey, 'hex')
        tx.sign(key);

        resolve('0x'+tx.serialize().toString('hex'));
      });
    });
  });
};

function signTx(privateKey, from, to, amount, data) {
  return getNonce(from).then(function(nonce) {
    return signWithNonce(privateKey, nonce, from, to, amount, data);
  });
};

function sendRaw(signed) {
  return new Promise(function(resolve, reject) {
    getWeb3().eth.sendRawTransaction(signed, function(error, res) {
      if (error) {
        reject(error);
      } else {
        resolve(res);
      }
    });
  });
};

function sendTx(from, to, amount, data) {
  var rawTx = {
    value: getWeb3().fromDecimal(amount),
    to: to,
    data: data,
    from: from,
  };

  return new Promise(function(resolve, reject) {
    getWeb3().eth.estimateGas(rawTx, function(error, gasUsage) {
      if (error) {
        reject(error);
        return;
      }

      // var forceLimit = 4700000;
      // if (gasUsage>forceLimit) {
      //   console.log(chalk.red(`Needed gas is too high: ${gasUsage}`));
      //   process.exit();
      // }

      rawTx.gas = Math.min(gasUsage * 2, 3000000);

      getWeb3().eth.sendTransaction(rawTx, function(error, res) {
        if (error) {
          reject(error);
        } else {
          resolve(res);
        }
      });
    });
  });
}

function callMethod(privateKey, toAddress, value, contract, methodName, args) {
  var fromAddress = privateKey ? privateToAddress(privateKey) : getWeb3().eth.accounts[0];
  var method = contract[methodName];
  var data = method.getData.apply(method, args);
  if (methodName=='new') {
    data = '0x'+data;
  }
  return new Promise(function(resolve, reject) {
    var txId;

    getWeb3().eth.filter('latest').watch(function(error, block) {
      if (error || !txId) {
        return;
      }

      getWeb3().eth.getTransactionReceipt(txId, function(err, info) {
        if (!info) {
          return;
        }
        writeDone();
        resolve(info);
      });
    });

    if (!privateKey) {
      return step('Broadcasting tx for '+methodName, sendTx(
        fromAddress,
        toAddress,
        value,
        data
      )).then(function(id) {
        txId = id;
        console.log('Transaction:');
        console.log(chalk.green(id));
        writeLabel('Waiting to be mined');
      });
    }

    return step('Broadcasting tx for '+methodName, signTx(
      privateKey,
      fromAddress,
      toAddress,
      value,
      data
    ).then(sendRaw)).then(function(id) {
      txId = id;
      console.log('Transaction:');
      console.log(chalk.green(id));
      writeLabel('Waiting to be mined');
    });
  });
};

function resolve(fileName) {
  var cwd = path.dirname(fileName)
  var name = path.basename(fileName);
  var processed = [];

  var _resolve = function(cwd, name) {
    var extractImports = function(content) {
      var imports = [];
      var lines = content.split(/\n/);
      lines.forEach(function(line, ii) {
        var m = line.replace(/\/\/.*$/, '').match(/import\s+["']([^"']+)["']/);
        if (m) {

          lines[ii] = `import "${m[1].split('/').pop()}";`;
          imports.push(m[1]);
        }
      });
      var finalCode = lines.join('\n');

      return [finalCode, imports];
    };

    var res = {};
    var fullName = fs.realpathSync(cwd + '/' + name);
    if (processed.indexOf(fullName)!=-1) {
      return res;
    }
    processed.push(fullName);
    return readFile(fullName).then(function(content) {
      var contentImports = extractImports(content);
      res[fullName] = {content: contentImports[0], file:fullName};

      return Promise.all(contentImports[1].map(function(name) {
        var newCwd = fs.realpathSync(cwd+'/'+name).split('/').slice(0, -1).join('/');
        return _resolve(newCwd, name);
      }));
    }).then(function(items) {
      items.forEach(function(child) {
        _.extend(res, child);
      })
      return res;
    });
  }

  return _resolve(cwd, name);
};

function readFile(fileName) {
  return new Promise(function(resolve, reject) {
    fs.readFile(fileName, 'UTF-8', function(err, data) {
      if (err) {
        reject(err);
      }
      resolve(data);
    });
  })
};

function fileTime(fileName) {
  return new Promise(function(resolve, reject) {
    fs.stat(fileName, function(err, stat) {
      if (err) {
        reject(err);
      } else {
        resolve(stat.mtime.getTime());
      }
    });
  });
};

function valToPromise(val) {
  if (_.isFunction(val)) {
    return new Promise(function(resolve, reject) {
      resolve(val());
    });
  }

  return new Promise(function(resolve, reject) {
    resolve(val);
  });
};

function cache(key, val) {
  var dir = os.tmpdir();

  var exec = function(cachePath, val) {
    return valToPromise(val).then(function(val) {
      val = JSON.stringify(val);
      return new Promise(function(resolve, reject) {
        fs.writeFile(cachePath, val, function(err) {
          if (err) {
            reject(err);
            return;
          }
          fs.chmodSync(cachePath, '777');
          resolve(val);
        });
      });
    });
  };

  var hash = crypto.createHash('sha256');
  hash.update(salt + key);
  var cacheFile = hash.digest('hex');
  var cachePath = dir+"/"+cacheFile;

  if (arguments[2]===true) {
    return exec(cachePath, val).then(function(res) {
      return JSON.parse(res);
    })
  }

  return readFile(cachePath).catch(function(err) {
    return exec(cachePath, val);
  }).then(function(res) {
    try {
      return JSON.parse(res);
    } catch(e) {
      console.log(chalk.red(`Failed reading cache file for ${key}:`));
      return cache(key, val, true);
    }
  });
};

var getContract = function(fileName, incContract) {
  fileName = fs.realpathSync(fileName);

  var fileMaxTime = function(files) {
    return Promise.map(files, fileTime).then(function(times) {
      return Math.max.apply(Math, times);
    });
  };

  var build = function() {
    return resolve(fileName).then(function(res) {
      var contractName = fileName.split('/').pop().replace(/\.sol$/, '');
      var input = {};
      var files = [];
      _.each(res, function(item, name) {
        input[name.split('/').pop()] = item.content;
        files.push(item.file);
      });

      return fileMaxTime(files).then(function(maxTime) {
        var output = solc.compile({sources: input}, 1);
        if (output.errors) {
          throw new Error(output.errors.join('\n'));
        }
        var contract = output.contracts[`${fileName.split('/').pop()}:${contractName}`];
        var byteCode = contract.bytecode;
        var jsonAbi = contract.interface;
        var abi = JSON.parse(jsonAbi);

        return {
          files: files,
          name: contractName,
          time: maxTime,
          byteCode: byteCode,
          abi: abi
        };
      });
    })
  };

  return cache(fileName, build).then(function(res) {
    var decorate = function(res) {
      var props = ['name', 'byteCode', 'abi'];
      return _.extend(_.pick(res, props), {
        contract: (incContract===undefined || incContract) ? getWeb3().eth.contract(res.abi) : null
      });
    };

    return fileMaxTime(res.files).then(function(time) {
      if (time==res.time) {
        return decorate(res);
      }

      return build().then(function(res) {
        return cache(fileName, res, true).then(function() {
          return decorate(res);
        });
      });
    })
  });
};

function prepareArgs(inputs, args) {
  return inputs.map(function(param, index) {
    var v = args[index];
    if (v===undefined) {
      throw new Error("Missing arg: "+param.name);
    }

    if (/\[\]$/.test(param.type)) {
      if (_.isString(v)) {
        try {
          v = JSON.parse(v);
        } catch (err) {
          throw new Error("Failed parsing arg: "+param.name);
        }
      }
    }

    return {
      name: param.name,
      value: v
    };
  });
};

var writeLabel = function(label) {
  process.stdout.write(label+' ... ');
};

var writeDone = function() {
  console.log(chalk.green('done'));
};

var writeFailed = function() {
  console.log(chalk.red('failed'));
};

var step = function(label, cb) {
  writeLabel(label);
  return valToPromise(cb).then(function(res) {
    writeDone();
    return res;
  }).catch(function(err) {
    writeFailed();
    throw err;
  });
};

var confirm = function(question) {
  console.log(question);
  return new Promise(function(resolve, reject) {
    read({ prompt: chalk.blue("[yes]/no: ") }, function(er, answer) {
      if (answer=='yes' || answer=='y') {
        resolve();
      } else {
        reject('canceled');
      }
    });
  });
};

var printException = function(err) {
  if (_.isString(err)) {
    console.log(chalk.red('Failed: '+err));
  } else {
    process.stdout.write(chalk.styles.red.open);
    console.log(err.stack);
    process.stdout.write(chalk.styles.red.close);
  }
};

function watchTxs(txToWatch, afterMined) {
  return new Promise(function(resolve, reject) {
    var blockFilter = getWeb3().eth.filter('latest');
    blockFilter.watch(function(error, block) {
      if (error) {
        return;
      }

      var checkTx = function(txId) {
        getWeb3().eth.getTransactionReceipt(txId, function(err, info) {
          if (!info) {
            return;
          }

          if (afterMined[txId]) {
            afterMined[txId](info);
            _.remove(txToWatch, function(id) { return id==txId; });

            if (txToWatch.length==0) {
              blockFilter.stopWatching();
              resolve();
            }
          }
        });
      };

      _.each(txToWatch, checkTx);
    });
  });
}

var exports = module.exports = {
  getContract: getContract,
  prepareArgs: prepareArgs,
  step: step,
  confirm: confirm,
  callMethod: callMethod,
  printException: printException,
  readFile: readFile,
  getNonce: getNonce,
  signWithNonce: signWithNonce,
  signTx: signTx,
  privateToAddress: privateToAddress,
  sendRaw: sendRaw,
  watchTxs: watchTxs,
  writeLabel: writeLabel,
  writeDone: writeDone,
  toWei : function(...args) {
    return getWeb3().toWei(...args);
  },
  read: function(options) {
    return new Promise(function(resolve, reject) {
      read(options, function(err, ret) {
        if (err) {
          reject(err);
        } else {
          resolve(ret);
        }
      })
    });
  },
  getBalance: function(addr) {
    return new Promise(function(resolve, reject) {
      getWeb3().eth.getBalance(addr, function(err, res) {
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    });
  }
};
<commit_msg>Bump cache<commit_after>const _ = require('lodash');
const Promise = require("bluebird");
const fs = require('fs');
const net = require('net');
const os = require('os');
const solc = require('solc');
const path = require('path');
const crypto = require('crypto');
const Tx = require('ethereumjs-tx');
const chalk = require('chalk');
const read = require('read');
const Web3 = require('web3');
const util = require('util');
const ethUtil = require('ethereumjs-util');
const salt = '3';
var web3;
Promise.config({
  longStackTraces: false
});

function getWeb3() {
  if (web3) {
    return web3;
  }

  var argv = require('yargs').argv;
  if (argv.g) {
    web3 = new Web3(new Web3.providers.IpcProvider(argv.g, net));
  } else {
    var ip = '127.0.0.1', port = 8545;
    if (argv.r) {
      var ipPort = argv.r.split(':');
      ip = ipPort[0];
      if (ipPort.length>1) {
        port = parseInt(ipPort[1], 10);
      }
    }

    web3 = new Web3(new Web3.providers.HttpProvider(`http://${ip}:${port}`));
  }

  return web3;
}

function privateToAddress(privateKey) {
  var public = ethUtil.privateToPublic(new Buffer(privateKey, 'hex'));
  return '0x' + ethUtil.publicToAddress(public).toString('hex');
}

function getNonce(address) {
  if (_.isArray(address)) {
    return Promise.map(address, getNonce).then(function(res) {
      return _.zipObject(address, res);
    });
  }

  return new Promise(function(resolve, reject) {
    getWeb3().eth.getTransactionCount(address, function(error, nonce) {
      if (error) {
        reject(error);
      } else {
        resolve(nonce);
      }
    });
  });
};

function signWithNonce(privateKey, nonce, from, to, amount, data) {
  return new Promise(function(resolve, reject) {
    var rawTx = {
      value: getWeb3().fromDecimal(amount),
      to: to,
      data: data,
      from: from,
    };

    getWeb3().eth.getGasPrice(function(error, price) {
      if (error) {
        reject(error);
        return;
      }
      getWeb3().eth.estimateGas(rawTx, function(error, gasUsage) {
        if (error) {
          reject(error);
          return;
        }
        // var forceLimit = 4700000;
        // if (gasUsage>forceLimit) {
        //   console.log(chalk.red(`Needed gas is too high: ${gasUsage}`));
        //   process.exit();
        // }
        //
        gasUsage = Math.min(gasUsage * 2, 3000000);

        var tx = new Tx({
          nonce: (getWeb3().toHex(nonce)),
          to: rawTx.to,
          value: rawTx.value,
          from: from,
          gasPrice: getWeb3().toHex(price),
          gasLimit: getWeb3().toHex(gasUsage),
          data: data
        });
        var key = new Buffer(privateKey, 'hex')
        tx.sign(key);

        resolve('0x'+tx.serialize().toString('hex'));
      });
    });
  });
};

function signTx(privateKey, from, to, amount, data) {
  return getNonce(from).then(function(nonce) {
    return signWithNonce(privateKey, nonce, from, to, amount, data);
  });
};

function sendRaw(signed) {
  return new Promise(function(resolve, reject) {
    getWeb3().eth.sendRawTransaction(signed, function(error, res) {
      if (error) {
        reject(error);
      } else {
        resolve(res);
      }
    });
  });
};

function sendTx(from, to, amount, data) {
  var rawTx = {
    value: getWeb3().fromDecimal(amount),
    to: to,
    data: data,
    from: from,
  };

  return new Promise(function(resolve, reject) {
    getWeb3().eth.estimateGas(rawTx, function(error, gasUsage) {
      if (error) {
        reject(error);
        return;
      }

      // var forceLimit = 4700000;
      // if (gasUsage>forceLimit) {
      //   console.log(chalk.red(`Needed gas is too high: ${gasUsage}`));
      //   process.exit();
      // }

      rawTx.gas = Math.min(gasUsage * 2, 3000000);

      getWeb3().eth.sendTransaction(rawTx, function(error, res) {
        if (error) {
          reject(error);
        } else {
          resolve(res);
        }
      });
    });
  });
}

function callMethod(privateKey, toAddress, value, contract, methodName, args) {
  var fromAddress = privateKey ? privateToAddress(privateKey) : getWeb3().eth.accounts[0];
  var method = contract[methodName];
  var data = method.getData.apply(method, args);
  if (methodName=='new') {
    data = '0x'+data;
  }
  return new Promise(function(resolve, reject) {
    var txId;

    getWeb3().eth.filter('latest').watch(function(error, block) {
      if (error || !txId) {
        return;
      }

      getWeb3().eth.getTransactionReceipt(txId, function(err, info) {
        if (!info) {
          return;
        }
        writeDone();
        resolve(info);
      });
    });

    if (!privateKey) {
      return step('Broadcasting tx for '+methodName, sendTx(
        fromAddress,
        toAddress,
        value,
        data
      )).then(function(id) {
        txId = id;
        console.log('Transaction:');
        console.log(chalk.green(id));
        writeLabel('Waiting to be mined');
      });
    }

    return step('Broadcasting tx for '+methodName, signTx(
      privateKey,
      fromAddress,
      toAddress,
      value,
      data
    ).then(sendRaw)).then(function(id) {
      txId = id;
      console.log('Transaction:');
      console.log(chalk.green(id));
      writeLabel('Waiting to be mined');
    });
  });
};

function resolve(fileName) {
  var cwd = path.dirname(fileName)
  var name = path.basename(fileName);
  var processed = [];

  var _resolve = function(cwd, name) {
    var extractImports = function(content) {
      var imports = [];
      var lines = content.split(/\n/);
      lines.forEach(function(line, ii) {
        var m = line.replace(/\/\/.*$/, '').match(/import\s+["']([^"']+)["']/);
        if (m) {

          lines[ii] = `import "${m[1].split('/').pop()}";`;
          imports.push(m[1]);
        }
      });
      var finalCode = lines.join('\n');

      return [finalCode, imports];
    };

    var res = {};
    var fullName = fs.realpathSync(cwd + '/' + name);
    if (processed.indexOf(fullName)!=-1) {
      return res;
    }
    processed.push(fullName);
    return readFile(fullName).then(function(content) {
      var contentImports = extractImports(content);
      res[fullName] = {content: contentImports[0], file:fullName};

      return Promise.all(contentImports[1].map(function(name) {
        var newCwd = fs.realpathSync(cwd+'/'+name).split('/').slice(0, -1).join('/');
        return _resolve(newCwd, name);
      }));
    }).then(function(items) {
      items.forEach(function(child) {
        _.extend(res, child);
      })
      return res;
    });
  }

  return _resolve(cwd, name);
};

function readFile(fileName) {
  return new Promise(function(resolve, reject) {
    fs.readFile(fileName, 'UTF-8', function(err, data) {
      if (err) {
        reject(err);
      }
      resolve(data);
    });
  })
};

function fileTime(fileName) {
  return new Promise(function(resolve, reject) {
    fs.stat(fileName, function(err, stat) {
      if (err) {
        reject(err);
      } else {
        resolve(stat.mtime.getTime());
      }
    });
  });
};

function valToPromise(val) {
  if (_.isFunction(val)) {
    return new Promise(function(resolve, reject) {
      resolve(val());
    });
  }

  return new Promise(function(resolve, reject) {
    resolve(val);
  });
};

function cache(key, val) {
  var dir = os.tmpdir();

  var exec = function(cachePath, val) {
    return valToPromise(val).then(function(val) {
      val = JSON.stringify(val);
      return new Promise(function(resolve, reject) {
        fs.writeFile(cachePath, val, function(err) {
          if (err) {
            reject(err);
            return;
          }
          fs.chmodSync(cachePath, '777');
          resolve(val);
        });
      });
    });
  };

  var hash = crypto.createHash('sha256');
  hash.update(salt + key);
  var cacheFile = hash.digest('hex');
  var cachePath = dir+"/"+cacheFile;

  if (arguments[2]===true) {
    return exec(cachePath, val).then(function(res) {
      return JSON.parse(res);
    })
  }

  return readFile(cachePath).catch(function(err) {
    return exec(cachePath, val);
  }).then(function(res) {
    try {
      return JSON.parse(res);
    } catch(e) {
      console.log(chalk.red(`Failed reading cache file for ${key}:`));
      return cache(key, val, true);
    }
  });
};

var getContract = function(fileName, incContract) {
  fileName = fs.realpathSync(fileName);

  var fileMaxTime = function(files) {
    return Promise.map(files, fileTime).then(function(times) {
      return Math.max.apply(Math, times);
    });
  };

  var build = function() {
    return resolve(fileName).then(function(res) {
      var contractName = fileName.split('/').pop().replace(/\.sol$/, '');
      var input = {};
      var files = [];
      _.each(res, function(item, name) {
        input[name.split('/').pop()] = item.content;
        files.push(item.file);
      });

      return fileMaxTime(files).then(function(maxTime) {
        var output = solc.compile({sources: input}, 1);
        if (output.errors) {
          throw new Error(output.errors.join('\n'));
        }
        var contract = output.contracts[`${fileName.split('/').pop()}:${contractName}`];
        var byteCode = contract.bytecode;
        var jsonAbi = contract.interface;
        var abi = JSON.parse(jsonAbi);

        return {
          files: files,
          name: contractName,
          time: maxTime,
          byteCode: byteCode,
          abi: abi
        };
      });
    })
  };

  return cache(fileName, build).then(function(res) {
    var decorate = function(res) {
      var props = ['name', 'byteCode', 'abi'];
      return _.extend(_.pick(res, props), {
        contract: (incContract===undefined || incContract) ? getWeb3().eth.contract(res.abi) : null
      });
    };

    return fileMaxTime(res.files).then(function(time) {
      if (time==res.time) {
        return decorate(res);
      }

      return build().then(function(res) {
        return cache(fileName, res, true).then(function() {
          return decorate(res);
        });
      });
    })
  });
};

function prepareArgs(inputs, args) {
  return inputs.map(function(param, index) {
    var v = args[index];
    if (v===undefined) {
      throw new Error("Missing arg: "+param.name);
    }

    if (/\[\]$/.test(param.type)) {
      if (_.isString(v)) {
        try {
          v = JSON.parse(v);
        } catch (err) {
          throw new Error("Failed parsing arg: "+param.name);
        }
      }
    }

    return {
      name: param.name,
      value: v
    };
  });
};

var writeLabel = function(label) {
  process.stdout.write(label+' ... ');
};

var writeDone = function() {
  console.log(chalk.green('done'));
};

var writeFailed = function() {
  console.log(chalk.red('failed'));
};

var step = function(label, cb) {
  writeLabel(label);
  return valToPromise(cb).then(function(res) {
    writeDone();
    return res;
  }).catch(function(err) {
    writeFailed();
    throw err;
  });
};

var confirm = function(question) {
  console.log(question);
  return new Promise(function(resolve, reject) {
    read({ prompt: chalk.blue("[yes]/no: ") }, function(er, answer) {
      if (answer=='yes' || answer=='y') {
        resolve();
      } else {
        reject('canceled');
      }
    });
  });
};

var printException = function(err) {
  if (_.isString(err)) {
    console.log(chalk.red('Failed: '+err));
  } else {
    process.stdout.write(chalk.styles.red.open);
    console.log(err.stack);
    process.stdout.write(chalk.styles.red.close);
  }
};

function watchTxs(txToWatch, afterMined) {
  return new Promise(function(resolve, reject) {
    var blockFilter = getWeb3().eth.filter('latest');
    blockFilter.watch(function(error, block) {
      if (error) {
        return;
      }

      var checkTx = function(txId) {
        getWeb3().eth.getTransactionReceipt(txId, function(err, info) {
          if (!info) {
            return;
          }

          if (afterMined[txId]) {
            afterMined[txId](info);
            _.remove(txToWatch, function(id) { return id==txId; });

            if (txToWatch.length==0) {
              blockFilter.stopWatching();
              resolve();
            }
          }
        });
      };

      _.each(txToWatch, checkTx);
    });
  });
}

var exports = module.exports = {
  getContract: getContract,
  prepareArgs: prepareArgs,
  step: step,
  confirm: confirm,
  callMethod: callMethod,
  printException: printException,
  readFile: readFile,
  getNonce: getNonce,
  signWithNonce: signWithNonce,
  signTx: signTx,
  privateToAddress: privateToAddress,
  sendRaw: sendRaw,
  watchTxs: watchTxs,
  writeLabel: writeLabel,
  writeDone: writeDone,
  toWei : function(...args) {
    return getWeb3().toWei(...args);
  },
  read: function(options) {
    return new Promise(function(resolve, reject) {
      read(options, function(err, ret) {
        if (err) {
          reject(err);
        } else {
          resolve(ret);
        }
      })
    });
  },
  getBalance: function(addr) {
    return new Promise(function(resolve, reject) {
      getWeb3().eth.getBalance(addr, function(err, res) {
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    });
  }
};
<|endoftext|>


<commit_before>var mongoose = require('./mongoose'),
    Q = require('q'),
    colors = require('colors'),
    http = require('http'),
    parser = require('./parser.js'),
    jsdom = require('jsdom'),
    jquery = require('jquery'),
    iconv = require('iconv-lite');

var baseUrl = 'http://cnes.datasus.gov.br/';

var self = {
    doGet: function(url) {
        var deferred = Q.defer();

        http.get(url, function(res) {
            var data = '';
            res.setEncoding('binary');

            res.on('data', function(chunk) {
                return data += chunk;
            });

            res.on('end', function() {
                var utf8String = iconv.decode(new Buffer(data), "UTF-8");
                jsdom.env(utf8String, function(errors, htmlDoc) {
                    var $ = jquery(htmlDoc);
                    deferred.resolve($, url);
                });
            });

            res.on('error', function(err) {
                deferred.reject(err);
            });
        });

        return deferred.promise;
    },

    downloadAndParse: function(parserFunction, url) {
        var deferred = Q.defer();

        self.doGet(url)
            .then(function(html) {
                var result = parserFunction(html, url);
                deferred.resolve(result);
            }).catch(function(err) {
                deferred.reject(err);
            });

        return deferred.promise;
    },

    downloadStates: function(url) {
        var deferred = Q.defer();
        console.log('Baixando estados: ', url);

        self.downloadAndParse(parser.parseStates, url)
            .then(function(states) {
                deferred.resolve(states);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });

        return deferred.promise;
    },

    downloadCities: function(url) {
        var deferred = Q.defer();
        console.log('Baixando Cidades: ', url);

        self.downloadAndParse(parser.parseCities, url)
            .then(function(cities) {
                deferred.resolve(cities);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });
        return deferred.promise;
    },

    downloadEntitiesUrls: function(url) {
        var deferred = Q.defer();
        console.log('Baixando Entidades da cidade: ', url);

        self.downloadAndParse(parser.getEntitiesUrlsFromCity, url)
            .then(function(entitiesUrls) {
                deferred.resolve(entitiesUrls);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });
        return deferred.promise;
    },

    downloadEntity: function(url) {
        var deferred = Q.defer();
        self.downloadAndParse(parser.parseEntityData, url)
            .then(function(entity) {
                deferred.resolve(entity);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });

        return deferred.promise;
    },

    processStates: function() {
        self.downloadStates
            .then(function(states) {
                Mongo.save(states, models.State);
                var statesLength = states.length;

                _.forEach(states, function(state, key) {
                    console.log(key, ' de ', statesLength);
                    downloadCities(state.url)
                        .then(processCities);
                });
            });
    },

    processCities: function(cities) {
        Mongo.save(cities, models.City);

        _.forEach(cities, function(city, key) {
            self.downloadEntitiesUrls(city.url)
                .then(self.processEntities);
        });
    },

    processEntities: function(entitiesUrls) {

        //Mongo.getCursor...

        self.downloadEntity()
            .then(function(states) {
                Mongo.save(states, models.State);
                var statesLength = states.length;

                _.forEach(states, function(state, key) {
                    console.log(key, ' de ', statesLength);
                    downloadCities(state.url)
                        .then(processCities);
                });
            });
    },

    initializeUrls: function() {
        var cities = Mongo.getAllCities(function(citiesCursor) {
            var processItem = function(item, done) {
                var downloadReference;
                var onFinish = function() {
                    item.done = 'true';
                    Mongo.update_city(item);
                    processItem = null;
                    downloadReference = null;
                    global.gc();
                    done();
                };
                downloadReference = downloadEntitiesUrls(item.url, onFinish);
            };
            mongoProcessing(citiesCursor, processItem, 10, function(err) {
                if (err) {
                    console.error('on noes, an error', err);
                    process.exit(1);
                }
            });
        });
    }
};

module.exports = self;
<commit_msg>added sync 'for' iteration<commit_after>var Mongo = require('./mongoose'),
    models = Mongo.models,
    Q = require('q'),
    _ = require('lodash'),
    colors = require('colors'),
    http = require('http'),
    parser = require('./parser.js'),
    jsdom = require('jsdom'),
    jquery = require('jquery'),
    iconv = require('iconv-lite');

var baseUrl = 'http://cnes.datasus.gov.br/';

var self = {
    doGet: function(url) {
        var deferred = Q.defer();

        http.get(url, function(res) {
            var data = '';
            res.setEncoding('binary');

            res.on('data', function(chunk) {
                return data += chunk;
            });

            res.on('end', function() {
                var utf8String = iconv.decode(new Buffer(data), "UTF-8");
                jsdom.env(utf8String, function(errors, htmlDoc) {
                    var $ = jquery(htmlDoc);
                    deferred.resolve($, url);
                });
            });

            res.on('error', function(err) {
                deferred.reject(err);
            });
        });

        return deferred.promise;
    },

    downloadAndParse: function(parserFunction, url) {
        var deferred = Q.defer();

        self.doGet(url)
            .then(function(html) {
                var result = parserFunction(html, url);
                deferred.resolve(result);
            }).catch(function(err) {
                deferred.reject(err);
            });

        return deferred.promise;
    },

    downloadStates: function(url) {
        var deferred = Q.defer();
        console.log('Baixando estados: ', url);

        self.downloadAndParse(parser.parseStates, url)
            .then(function(states) {
                deferred.resolve(states);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });

        return deferred.promise;
    },

    downloadCities: function(url) {
        var deferred = Q.defer();
        console.log('Baixando Cidades: ', url);

        self.downloadAndParse(parser.parseCities, url)
            .then(function(cities) {
                deferred.resolve(cities);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });
        return deferred.promise;
    },

    downloadEntitiesUrls: function(url) {
        var deferred = Q.defer();
        console.log('Baixando Entidades da cidade: ', url);

        self.downloadAndParse(parser.getEntitiesUrlsFromCity, url)
            .then(function(entitiesUrls) {
                deferred.resolve(entitiesUrls);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });
        return deferred.promise;
    },

    downloadEntity: function(url) {
        var deferred = Q.defer();
        self.downloadAndParse(parser.parseEntityData, url)
            .then(function(entity) {
                deferred.resolve(entity);
            })
            .catch(function(err) {
                console.log(err);
                deferred.reject(err);
            });

        return deferred.promise;
    },

    processStates: function() {
        var statesUrl = baseUrl + 'Lista_Tot_Es_Estado.asp';

        self.downloadStates(statesUrl)
            .then(function(states) {
                Mongo.save(states, models.State);
                var statesLength = states.length;

                self.forSync(states, function(state, done) {
                    self.downloadCities(state.url)
                        .then(function(cities) {
                            self.processCities(cities)
                            done();
                        });
                });
            });
    },

    processCities: function(cities) {
        Mongo.save(cities, models.City);

        self.forSync(cities, function(city, done) {
            self.downloadEntitiesUrls(city.url)
                .then(function(entitiesUrls) {
                    self.processEntitiesUrl(entitiesUrls)
                    done();
                });
        });
    },

    processEntitiesUrl: function(entitiesUrls) {
        Mongo.save(entitiesUrls, models.EntityToDownload);
    },

    processEntities: function() {
        self.paginateDatabaseAsStream(models.EntityToDownload, function(entityToDownload, done) {
            self.downloadEntity(entityToDownload.url)
                .then(function(entity) {
                    Mongo.save(entity, models.Entity);
                    done();
                });
        });
    },

    makeIterator: function(array) {
        var nextIndex = 0;

        return {
            next: function() {
                return nextIndex < array.length ? {
                    value: array[nextIndex++],
                    done: false
                } : {
                    done: true
                };
            }
        }
    },

    forSync: function(array, processFunction) {
        var cursor = self.makeIterator(array);

        var process = function(cursor) {
            var value = cursor.next();

            processFunction(value.value, function() {
                return process(cursor);
            });
        };

        process(cursor);
    },

    /*
        Return a Stream in order to make it iterable and reducing memory consumption.

        @param ModelObject - The model that will be searched on database.
        @param processFunction - the function that will receive the database document, the function should
            receive as a second parameter the `done` function that has to be called at the end. In case
            of exceptions you must have to pass it into the `done` function.

    */
    paginateDatabaseAsStream: function(ModelObject, processFunction) {
        Mongo.count(ModelObject, function(total) {
            var stream = Mongo.find(ModelObject);

            stream.on('data', function(doc) {
                stream.pause();

                var message = count + " of " + total;
                console.log(message.green);

                try {
                    processFunction(doc, function(err) {
                        if (err) console.log(err);
                        stream.resume();
                        count++;
                    });
                } catch (e) {
                    console.log(e);
                    stream.resume();
                    count++;
                }
            });
        });
    },

    initialize: function() {
        self.processStates();
        //self.processEntities();
    }
};

self.initialize();

module.exports = self;
<|endoftext|>


<commit_before>
var baseView = {
    make: function (opts) {
        var instance = function () {
            // XXX Is there any use in this?
        };
        _.extend(instance, this);
        instance['initialize'](opts);
        return instance;
    },
    'extend': function (subclass) {
        return _.extend({}, this, subclass);
    },

    '$': function(selector) {
        return this['$el'].find(selector);
    },

    isView: true,

    'initialize': function (opts) {
        var self = this;
        uniqueId(self);
        _.extend(self, opts);
        self['$el'] = $(self['el']).data('view', self);
        self.priority = self.parentView ? self.parentView.priority - 1 : BASE_PRIORITY_VIEW;
        self.scope = autorun(self.render, self, self.priority, 'view_' + self.Name,
                             self.onScopeExecute, self, true);
    },

    onScopeExecute: function (scope) {
        log(INFO, this, 'lookups', scope.lookups);
    },

    /**
     * View.destroy
     *
     * Destroys this view, removing all bindings and sub-views (recursively).
     */
    destroy: function (destroyRoot) {
        var self = this;
        log(VERBOSE, self, 'destroy', 'due to re-render of ' + destroyRoot.Name);
        self.destroyed = true;
        self.scope.destroy();
        _.each(self.subViews || [], function (view) {
            view.destroy(self);
        });
        self['destroyDOM'](self.$el);
    },

    /**
     * View.render
     *
     * This function is called at View init, and again whenever any model properties that this View
     * depended on are changed.
     */
    render: function () {
        var self = this;
        // This view may get a reset call at the same instant that another
        // view gets created to replace it.
        if (!self.destroyed) {
            /**
             * Move all this view's children to another temporary DOM element.  This will be used as the
             * pseudo-parent element for the destroyDOM call.
             */
            if (self.templateId) {
                /**
                 * If the DOM fragment to be removed has an active (focused) element, we attempt
                 * to restore that focus after refreshing this DOM fragment.  We also attempt
                 * to restore the selection start/end, which only works in Webkit/Gecko right
                 * now; see the URL below for possible IE compatibility.
                 */
                var activeElement = document.activeElement;
                var activeElementSelector, activeElementIndex, selectionStart, selectionEnd;
                if (_.contains($(activeElement).parents(), self.el)) {
                    // XXX this could be improved to pick up on IDs/classes/attributes or something?
                    activeElementSelector = 'input';
                    activeElementIndex = _.indexOf(self.$(activeElementSelector), activeElement);
                    // XXX for IE compatibility, this might work:
                    // http://the-stickman.com/web-development/javascript/ ...
                    // finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
                    selectionStart = activeElement.selectionStart;
                    selectionEnd = activeElement.selectionEnd;
                }

                var $old = $('<div>').append(this.$el.children());
                var newHtml = renderTemplate(self.templateId, self.rootStr);
                log(INFO, self, 'newhtml', newHtml);
                self.$el.html(newHtml);

                /**
                 * Execute the "fragment ready" callback.
                 */
                self['ready']();
                self['postReady']();

                /**
                 * (Re-)create sub-views for each descendent element with a tbone attribute.
                 * On re-renders, the pre-existing list of sub-views is passed to render, which
                 * attempts to pair already-rendered views with matching elements in this view's
                 * newly re-rendered template.  Matching views are transferred to the new DOM
                 * hierarchy without disruption.
                 */
                var oldSubViews = self.subViews || [];
                self.subViews = render(self.$('[tbone]'), self, oldSubViews);
                var obsoleteSubViews = _.difference(oldSubViews, self.subViews);
                /**
                 * Destroy all of the sub-views that were not reused.
                 */
                _.each(obsoleteSubViews, function (view) {
                    view.destroy(self);
                });
                /**
                 * Call destroyDOM with the the pseudo-parent created above.  This DOM fragment contains all
                 * of the previously-rendered (if any) DOM structure of this view and subviews, minus any
                 * subviews that are being reused (which have already been moved to the new parent).
                 */
                self['destroyDOM']($old);

                /**
                 * If we saved it above, restore the active element focus and selection.
                 */
                if (activeElementSelector) {
                    var newActiveElement = self.$(activeElementSelector)[activeElementIndex];
                    $(newActiveElement).focus();
                    if (selectionStart != null && selectionEnd != null) {
                        newActiveElement.selectionStart = selectionStart;
                        newActiveElement.selectionEnd = selectionEnd;
                    }
                }
            } else {
                self['ready']();
                self['postReady']();
            }
            self['postRender']();
            viewRenders++;
        }
    },

    /**
     * View.ready
     *
     * The "template-ready" callback.  This is the restricted tbone equivalent of document-ready.
     * It is the recommended means of adding interactivity/data/whatever to Views.
     *
     * At the moment this callback is executed, subviews are neither rendered nor are they
     * attached to the DOM fragment.  If you need to interact with subviews, use postRender.
     */
    'ready': noop,

    /**
     * View.postReady
     *
     * This is the same as ready, except that it executes after ready.  The typical use case is
     * to override this in your base template to provide automatic application-wide helpers,
     * such as activating a tooltip library, and to use View.ready for specific view logic.
     */
    'postReady': noop,

    /**
     * View.postRender
     *
     * The "fragment-updated" callback.  This is executed whenever this view is re-rendered,
     * and after all sub-views (recursively) have rendered.
     *
     * Note that because we optimistically re-use sub-views, this may be called multiple times
     * with the same sub-view DOM fragments.  Ensure that anything you do to DOM elements in
     * sub-views is idempotent.
     */
    'postRender': noop,

    /**
     * View.destroyDOM
     *
     * The "document-destroy" callback.  Use this to do cleanup on removal of old HTML, e.g.
     * destroying associated tooltips.
     *
     * Note: Destroy contents of the $el parameter, not this.$el!  (XXX make this less error-prone)
     *
     * @param  {!jQuery} $el jQuery selection of DOM fragment to destroy
     */
    'destroyDOM': function ($el) { },

    /**
     * If a root attribute was specified, use that as the root object for this view's
     * render, both in templating automatically as well as available via this.root in
     * `ready` and `postRender` callbacks.
     */
    root: function () {
        return tbone['query'](DONT_GET_DATA, this.rootStr || '');
    },

    /**
     * Perform a safe lookup on the view's root, if any.  If there's no root, returns null.
     **/
    // This should be rewritten to memoize the rootStr part of view lookups.
    // This will need to work around the existing warning feature in the lookup
    // function for cases where the root is actually a property of a model.
    // Sublookups from there don't reference a model, and while this is all right
    // (because the lookup of rootStr has bound the view already to that model),
    // there could be some subtleties about how that connection is made and passed
    // around... and this would be a great thing to implement alongside passing
    // *data* to subviews through template references, e.g. ${id(data)}.
    'query': function (query, value) {
        query = (this.rootStr ? this.rootStr + '.' : '') + (query || '');
        return arguments.length === 2 ? tbone(query, value) : tbone(query);
    },

    // deprecated
    'lookup': function (query) {
        return this['query'](query);
    },

    'parentRoot': function () {
        return this.parentView && this.parentView.root();
    },

    'parent': function () {
        return this.parentView;
    }
};

/**
 * Use to find key/value pairs in tbone attributes on render.
 * @type {RegExp}
 * @const
 */
var rgxTBoneAttribute = /[^\w.]*([\w.]+)[^\w.]+([\w.]+)/g;

/**
 * tbone.render
 *
 * Render an array of HTML elements into Views.  This reads the tbone attribute generates a View
 * for each element accordingly.
 *
 * @param  {Array.<DOMElement>}     $els     elements to render templates from
 * @param  {Backbone.View=}         parent   parent view
 * @param  {Array.<Backbone.View>=} subViews (internal) sub-views created previously; these are used
 *                                           to avoid redundantly regenerating unchanged views.
 * @return {Array.<Backbone.View>}           views created (and/or substituted from subViews)
 */
function render($els, parent, subViews) {
    var subViewMap = {};
    _.each(subViews || [], function (subView) {
        (subViewMap[subView.origOuterHTML] = subViewMap[subView.origOuterHTML] || []).push(subView);
    });
    return _.map($els, function (el) {
        var $this = $(el);
        var outerHTML = el.outerHTML;
        if (subViewMap[outerHTML] && subViewMap[outerHTML].length) {
            /**
             * If we have a pre-rendered view available with matching outerHTML (i.e. nothing in
             * the parent template has changed for this subview's root element), then just swap
             * the pre-existing element in place along with its undisturbed associated View.
             */
            var subView = subViewMap[outerHTML].shift();
            log(VERBOSE, parent || 'render', 'reuse', subView);
            $this.replaceWith(subView.el);
            return subView;
        } else {
            /**
             * Otherwise, read the tbone attribute from the element and use it to instantiate
             * a new View.
             */
            var props = {};
            ($this.attr('tbone') || '').replace(rgxTBoneAttribute, function(__, prop, value) {
                props[prop] = value;
            });
            var inlineTemplateId = props['inline'];
            if (inlineTemplateId) {
                /**
                 * XXX what's the best way to get the original html back?
                 */
                var origTemplateHtml = $this.html()
                    .replace(/</g, '<')
                    .replace(/>/g, '>')
                    .replace(/&/g, '&');
                addTemplate(inlineTemplateId, origTemplateHtml);
            }
            var templateId = inlineTemplateId || props['tmpl'];
            var viewId = props['view'];
            var rootStr = props['root'];

            /**
             * Use either the view or template attributes as the `name` of the view.
             */
            var name = viewId || templateId;
            if (!name) {
                error('No view or template was specified for this element: ', el);
            }
            if (templateId && !templates[templateId]) {
                error('Could not find template for ' + templateId + '.  If you don\'t want to ' +
                      'use a template, use the view attribute instead.');
            }
            /**
             * Add a class matching the view name for styling convenience.
             */
            $this.addClass(name);

            /**
             * Find the corresponding view matching the name (`viewId` or `templateId`) to the
             * name passed to `createView.`  If there is no view matching that name, then use
             * the default view.  You can set the default view using `tbone.defaultView().`
             * @type {function(new:Backbone.View, Object)}
             */
            var myView = views[name] || defaultView;

            return myView.make({
                Name: name,
                origOuterHTML: outerHTML,
                'el': el,
                templateId: templateId,
                parentView: parent,
                rootStr: rootStr
            });
        }
    });
}

var defaultView = baseView;
function __defaultView(view) {
    if (view) {
        defaultView = view;
    }
    return defaultView;
}

/**
 * tbone.createView
 *
 * Create a new view, inheriting from another view (or the default view).
 *
 * This is the primary method you should use to add JS logic to your application. e.g.:
 *
 * tbone.createView('widget', function () {
 *     this.$('span').text('42');
 *     this.$('a[href]').click(function () {
 *         tbone.set('selected.widget', $(this).attr('id'));
 *         return false;
 *     })
 * });
 *
 * The function above whenever a template renders an element with a tbone attribute
 * of "widget", and this.$ will be scoped to that view.
 *
 * @param  {String=}                       name Name for the view.
 * @param  {new(Backbone.View, Object=)=}  base Base view to extend.
 * @param  {Function}                      fn   convenience parameter for specifying ready
 *                                              function.
 * @param  {Object}                        opts additional prototype properties
 * @return {new(Backbone.View, Object=)}
 */
function createView(name, base, fn, opts) {
    var args = [].slice.call(arguments);
    var arg = args.shift();
    if (typeof arg === 'string') {
        name = arg;
        arg = args.shift();
    } else {
        name = 'v' + nextId++;
    }
    if (arg && arg.extend) {
        base = arg;
        arg = args.shift();
    } else {
        base = defaultView;
    }
    if (typeof arg === 'function') {
        fn = arg;
        arg = args.shift();
    } else {
        fn = null;
    }
    opts = arg || {};
    opts.Name = name;
    var baseReady = base['ready'];
    if (fn) {
        opts['ready'] = baseReady === noop ? fn : function () {
            baseReady.call(this);
            fn.call(this);
        };
    }
    return views[name] = base.extend(opts);
}
<commit_msg>Don't make views callable, for now... will it ever be useful?<commit_after>
var baseView = {
    make: function (opts) {
        var instance = {};
        _.extend(instance, this);
        instance['initialize'](opts);
        return instance;
    },
    'extend': function (subclass) {
        return _.extend({}, this, subclass);
    },

    '$': function(selector) {
        return this['$el'].find(selector);
    },

    isView: true,

    'initialize': function (opts) {
        var self = this;
        uniqueId(self);
        _.extend(self, opts);
        self['$el'] = $(self['el']).data('view', self);
        self.priority = self.parentView ? self.parentView.priority - 1 : BASE_PRIORITY_VIEW;
        self.scope = autorun(self.render, self, self.priority, 'view_' + self.Name,
                             self.onScopeExecute, self, true);
    },

    onScopeExecute: function (scope) {
        log(INFO, this, 'lookups', scope.lookups);
    },

    /**
     * View.destroy
     *
     * Destroys this view, removing all bindings and sub-views (recursively).
     */
    destroy: function (destroyRoot) {
        var self = this;
        log(VERBOSE, self, 'destroy', 'due to re-render of ' + destroyRoot.Name);
        self.destroyed = true;
        self.scope.destroy();
        _.each(self.subViews || [], function (view) {
            view.destroy(self);
        });
        self['destroyDOM'](self.$el);
    },

    /**
     * View.render
     *
     * This function is called at View init, and again whenever any model properties that this View
     * depended on are changed.
     */
    render: function () {
        var self = this;
        // This view may get a reset call at the same instant that another
        // view gets created to replace it.
        if (!self.destroyed) {
            /**
             * Move all this view's children to another temporary DOM element.  This will be used as the
             * pseudo-parent element for the destroyDOM call.
             */
            if (self.templateId) {
                /**
                 * If the DOM fragment to be removed has an active (focused) element, we attempt
                 * to restore that focus after refreshing this DOM fragment.  We also attempt
                 * to restore the selection start/end, which only works in Webkit/Gecko right
                 * now; see the URL below for possible IE compatibility.
                 */
                var activeElement = document.activeElement;
                var activeElementSelector, activeElementIndex, selectionStart, selectionEnd;
                if (_.contains($(activeElement).parents(), self.el)) {
                    // XXX this could be improved to pick up on IDs/classes/attributes or something?
                    activeElementSelector = 'input';
                    activeElementIndex = _.indexOf(self.$(activeElementSelector), activeElement);
                    // XXX for IE compatibility, this might work:
                    // http://the-stickman.com/web-development/javascript/ ...
                    // finding-selection-cursor-position-in-a-textarea-in-internet-explorer/
                    selectionStart = activeElement.selectionStart;
                    selectionEnd = activeElement.selectionEnd;
                }

                var $old = $('<div>').append(this.$el.children());
                var newHtml = renderTemplate(self.templateId, self.rootStr);
                log(INFO, self, 'newhtml', newHtml);
                self.$el.html(newHtml);

                /**
                 * Execute the "fragment ready" callback.
                 */
                self['ready']();
                self['postReady']();

                /**
                 * (Re-)create sub-views for each descendent element with a tbone attribute.
                 * On re-renders, the pre-existing list of sub-views is passed to render, which
                 * attempts to pair already-rendered views with matching elements in this view's
                 * newly re-rendered template.  Matching views are transferred to the new DOM
                 * hierarchy without disruption.
                 */
                var oldSubViews = self.subViews || [];
                self.subViews = render(self.$('[tbone]'), self, oldSubViews);
                var obsoleteSubViews = _.difference(oldSubViews, self.subViews);
                /**
                 * Destroy all of the sub-views that were not reused.
                 */
                _.each(obsoleteSubViews, function (view) {
                    view.destroy(self);
                });
                /**
                 * Call destroyDOM with the the pseudo-parent created above.  This DOM fragment contains all
                 * of the previously-rendered (if any) DOM structure of this view and subviews, minus any
                 * subviews that are being reused (which have already been moved to the new parent).
                 */
                self['destroyDOM']($old);

                /**
                 * If we saved it above, restore the active element focus and selection.
                 */
                if (activeElementSelector) {
                    var newActiveElement = self.$(activeElementSelector)[activeElementIndex];
                    $(newActiveElement).focus();
                    if (selectionStart != null && selectionEnd != null) {
                        newActiveElement.selectionStart = selectionStart;
                        newActiveElement.selectionEnd = selectionEnd;
                    }
                }
            } else {
                self['ready']();
                self['postReady']();
            }
            self['postRender']();
            viewRenders++;
        }
    },

    /**
     * View.ready
     *
     * The "template-ready" callback.  This is the restricted tbone equivalent of document-ready.
     * It is the recommended means of adding interactivity/data/whatever to Views.
     *
     * At the moment this callback is executed, subviews are neither rendered nor are they
     * attached to the DOM fragment.  If you need to interact with subviews, use postRender.
     */
    'ready': noop,

    /**
     * View.postReady
     *
     * This is the same as ready, except that it executes after ready.  The typical use case is
     * to override this in your base template to provide automatic application-wide helpers,
     * such as activating a tooltip library, and to use View.ready for specific view logic.
     */
    'postReady': noop,

    /**
     * View.postRender
     *
     * The "fragment-updated" callback.  This is executed whenever this view is re-rendered,
     * and after all sub-views (recursively) have rendered.
     *
     * Note that because we optimistically re-use sub-views, this may be called multiple times
     * with the same sub-view DOM fragments.  Ensure that anything you do to DOM elements in
     * sub-views is idempotent.
     */
    'postRender': noop,

    /**
     * View.destroyDOM
     *
     * The "document-destroy" callback.  Use this to do cleanup on removal of old HTML, e.g.
     * destroying associated tooltips.
     *
     * Note: Destroy contents of the $el parameter, not this.$el!  (XXX make this less error-prone)
     *
     * @param  {!jQuery} $el jQuery selection of DOM fragment to destroy
     */
    'destroyDOM': function ($el) { },

    /**
     * If a root attribute was specified, use that as the root object for this view's
     * render, both in templating automatically as well as available via this.root in
     * `ready` and `postRender` callbacks.
     */
    root: function () {
        return tbone['query'](DONT_GET_DATA, this.rootStr || '');
    },

    /**
     * Perform a safe lookup on the view's root, if any.  If there's no root, returns null.
     **/
    // This should be rewritten to memoize the rootStr part of view lookups.
    // This will need to work around the existing warning feature in the lookup
    // function for cases where the root is actually a property of a model.
    // Sublookups from there don't reference a model, and while this is all right
    // (because the lookup of rootStr has bound the view already to that model),
    // there could be some subtleties about how that connection is made and passed
    // around... and this would be a great thing to implement alongside passing
    // *data* to subviews through template references, e.g. ${id(data)}.
    'query': function (query, value) {
        query = (this.rootStr ? this.rootStr + '.' : '') + (query || '');
        return arguments.length === 2 ? tbone(query, value) : tbone(query);
    },

    // deprecated
    'lookup': function (query) {
        return this['query'](query);
    },

    'parentRoot': function () {
        return this.parentView && this.parentView.root();
    },

    'parent': function () {
        return this.parentView;
    }
};

/**
 * Use to find key/value pairs in tbone attributes on render.
 * @type {RegExp}
 * @const
 */
var rgxTBoneAttribute = /[^\w.]*([\w.]+)[^\w.]+([\w.]+)/g;

/**
 * tbone.render
 *
 * Render an array of HTML elements into Views.  This reads the tbone attribute generates a View
 * for each element accordingly.
 *
 * @param  {Array.<DOMElement>}     $els     elements to render templates from
 * @param  {Backbone.View=}         parent   parent view
 * @param  {Array.<Backbone.View>=} subViews (internal) sub-views created previously; these are used
 *                                           to avoid redundantly regenerating unchanged views.
 * @return {Array.<Backbone.View>}           views created (and/or substituted from subViews)
 */
function render($els, parent, subViews) {
    var subViewMap = {};
    _.each(subViews || [], function (subView) {
        (subViewMap[subView.origOuterHTML] = subViewMap[subView.origOuterHTML] || []).push(subView);
    });
    return _.map($els, function (el) {
        var $this = $(el);
        var outerHTML = el.outerHTML;
        if (subViewMap[outerHTML] && subViewMap[outerHTML].length) {
            /**
             * If we have a pre-rendered view available with matching outerHTML (i.e. nothing in
             * the parent template has changed for this subview's root element), then just swap
             * the pre-existing element in place along with its undisturbed associated View.
             */
            var subView = subViewMap[outerHTML].shift();
            log(VERBOSE, parent || 'render', 'reuse', subView);
            $this.replaceWith(subView.el);
            return subView;
        } else {
            /**
             * Otherwise, read the tbone attribute from the element and use it to instantiate
             * a new View.
             */
            var props = {};
            ($this.attr('tbone') || '').replace(rgxTBoneAttribute, function(__, prop, value) {
                props[prop] = value;
            });
            var inlineTemplateId = props['inline'];
            if (inlineTemplateId) {
                /**
                 * XXX what's the best way to get the original html back?
                 */
                var origTemplateHtml = $this.html()
                    .replace(/</g, '<')
                    .replace(/>/g, '>')
                    .replace(/&/g, '&');
                addTemplate(inlineTemplateId, origTemplateHtml);
            }
            var templateId = inlineTemplateId || props['tmpl'];
            var viewId = props['view'];
            var rootStr = props['root'];

            /**
             * Use either the view or template attributes as the `name` of the view.
             */
            var name = viewId || templateId;
            if (!name) {
                error('No view or template was specified for this element: ', el);
            }
            if (templateId && !templates[templateId]) {
                error('Could not find template for ' + templateId + '.  If you don\'t want to ' +
                      'use a template, use the view attribute instead.');
            }
            /**
             * Add a class matching the view name for styling convenience.
             */
            $this.addClass(name);

            /**
             * Find the corresponding view matching the name (`viewId` or `templateId`) to the
             * name passed to `createView.`  If there is no view matching that name, then use
             * the default view.  You can set the default view using `tbone.defaultView().`
             * @type {function(new:Backbone.View, Object)}
             */
            var myView = views[name] || defaultView;

            return myView.make({
                Name: name,
                origOuterHTML: outerHTML,
                'el': el,
                templateId: templateId,
                parentView: parent,
                rootStr: rootStr
            });
        }
    });
}

var defaultView = baseView;
function __defaultView(view) {
    if (view) {
        defaultView = view;
    }
    return defaultView;
}

/**
 * tbone.createView
 *
 * Create a new view, inheriting from another view (or the default view).
 *
 * This is the primary method you should use to add JS logic to your application. e.g.:
 *
 * tbone.createView('widget', function () {
 *     this.$('span').text('42');
 *     this.$('a[href]').click(function () {
 *         tbone.set('selected.widget', $(this).attr('id'));
 *         return false;
 *     })
 * });
 *
 * The function above whenever a template renders an element with a tbone attribute
 * of "widget", and this.$ will be scoped to that view.
 *
 * @param  {String=}                       name Name for the view.
 * @param  {new(Backbone.View, Object=)=}  base Base view to extend.
 * @param  {Function}                      fn   convenience parameter for specifying ready
 *                                              function.
 * @param  {Object}                        opts additional prototype properties
 * @return {new(Backbone.View, Object=)}
 */
function createView(name, base, fn, opts) {
    var args = [].slice.call(arguments);
    var arg = args.shift();
    if (typeof arg === 'string') {
        name = arg;
        arg = args.shift();
    } else {
        name = 'v' + nextId++;
    }
    if (arg && arg.extend) {
        base = arg;
        arg = args.shift();
    } else {
        base = defaultView;
    }
    if (typeof arg === 'function') {
        fn = arg;
        arg = args.shift();
    } else {
        fn = null;
    }
    opts = arg || {};
    opts.Name = name;
    var baseReady = base['ready'];
    if (fn) {
        opts['ready'] = baseReady === noop ? fn : function () {
            baseReady.call(this);
            fn.call(this);
        };
    }
    return views[name] = base.extend(opts);
}
<|endoftext|>