<commit_before>(function(InputMaskModule) {

var _id = 0,
    _templates = {};

function Template(grammar, mask, name) {
	this.grammar = grammar || null;

	if (mask) {
		this.setMask(mask);
	}

	this.name = name || String(++_id);
}

Template.defaultGrammar = new InputMaskModule.Grammar();

Template.getByElement = function(element) {
	var name = element.getAttribute("data-mask-name"),
	    mask = element.getAttribute("data-mask");

	if (name) {
		if (_templates[name]) {
			return _templates[name];
		}
		else {
			throw new Error("No instance of InputMaskModule.Template found for name: " + name);
		}
	}
	else if (mask) {
		if (!_templates[mask]) {
			_templates[mask] = new Template(Template.defaultGrammar, mask);
		}

		return _templates[mask];
	}
	else {
		throw new Error("Missing one of data-mask or data-mask-name on " + element.nodeName);
	}
};

Template.register = function(name, mask) {
	if (_templates[name]) {
		throw new Error("A template named '" + name + "' has already been registered");
	}

	_templates[name] = new Template(Template.defaultGrammar, mask, name);

	return _templates[name];
};

Template.prototype = {

	DIRECTION_BACKWARDS: -1,
	DIRECTION_FORWARDS: 1,

	grammar: null,

	mask: "",

	maskChars: null,

	name: null,

	constructor: Template,

	addCharacter: function(start, newChar, text) {
		var chars = text.split(""),
		    actualStart = this._nextCharIndex(start, this.DIRECTION_FORWARDS, this.grammar.anyOrPlaceholder, chars),
		    i = actualStart,
		    charCount = chars.length,
		    selection = { text: text, start: actualStart, end: actualStart, length: 0 };

		if (actualStart < 0) {
			selection.start = selection.end = start;
		}
		else if (this.isValidCharAt(newChar, actualStart)) {
			chars[actualStart] = newChar;
			i = this._nextCharIndex(actualStart + 1, this.DIRECTION_FORWARDS, this.grammar.anyOrPlaceholder, chars);

			if (i > -1) {
				selection.text = chars.join("");
				selection.start = selection.end = i;
			}
			else if (actualStart === charCount - 1) {
				selection.text = chars.join("");
				selection.start = selection.end = charCount;
			}
		}

		return selection;
	},

	getMaskedValue: function(value) {
		var maskChars = this.mask.split(""),
		    valueChars = value.split(""),
		    i = 0, length = maskChars.length,
		    mc, vc, maskedValue = "",
		    grammar = this.grammar,
		    placeholder = grammar.placeholder;

		for (i; i < length; i++) {
			mc = maskChars[i];
			vc = valueChars[i] || "";

			if (mc === grammar.charMarker) {
				maskedValue += grammar.chars.test(vc) ? vc : placeholder;
			}
			else if (mc === grammar.digitMarker) {
				maskedValue += grammar.digits.test(vc) ? vc : placeholder;
			}
			else {
				maskedValue += vc || mc;
			}
		}

		return maskedValue;
	},

	getPlaceholder: function() {
		return this.grammar.placeholder;
	},

	isEmptyValue: function(value) {
		var emptyValue = this.mask
			.replace(new RegExp(this.grammar.digitMarker, "g"), this.grammar.placeholder)
			.replace(new RegExp(this.grammar.charMarker, "g"), this.grammar.placeholder);

		return emptyValue === value;
	},

	isNonFiller: function(c) {
		return this.grammar.any.test(c);
	},

	isChar: function(c) {
		return this.grammar.chars.test(c);
	},

	isDigit: function(c) {
		return this.grammar.digits.test(c);
	},

	isFiller: function(c) {
		return this.grammar.filler.test(c);
	},

	isPlaceholder: function(c) {
		return this.grammar.placeholder === c;
	},

	isSameCharClass: function(a, b) {
		return (a >= 0 && b < this.maskChars.length)
		       ? this.maskChars[a] === this.maskChars[b]
		       : false;
	},

	isValidCharAt: function(c, index) {
		if (index < 0 || index >= this.maskChars.length) {
			return false;
		}
		else {
			return this.maskChars[index].test(c);
		}
	},

	_nextCharIndex: function(start, direction, type, chars) {
		var index = -1,
		    i = start,
		    length = chars.length;

		if (direction === 1) {
			for (i; i < length; i++) {
				if (type.test(chars[i])) {
					index = i;
					break;
				}
			}
		}
		else {
			while (i--) {
				if (type.test(chars[i])) {
					index = i;
					break;
				}
			}
		}

		return index;
	},

	removeNextChar: function(start, text) {
		return this._removeChars(start, 0, this.DIRECTION_FORWARDS, text);
	},

	removePrevChar: function(start, text) {
		return this._removeChars(start, 0, this.DIRECTION_BACKWARDS, text);
	},

	removeCharRange: function(start, end, text) {
		return this._removeChars(start, end - start, this.DIRECTION_FORWARDS, text);
	},

	_removeChars: function(start, count, direction, text) {
		start = (start < 0) ? 0 : start;
		count = (count < 0) ? 0 : count;
		direction = direction < 0 ? -1 : 1;

		var selection = { text: null, start: start, end: start, length: 0 },
		    chars = text.split("");

		if (count === 0) {
			this._shiftCharacters(selection, start, direction, chars);
		}
		else {
			this._removeCharacterRange(selection, start, count, chars);
		}

		selection.text = chars.join("");

		return selection;
	},

	_removeCharacterRange: function(selection, start, count, chars) {
		var actualStart = this._nextCharIndex(start, 1, this.grammar.any, chars),
		    i = actualStart,
		    charCount = chars.length,
		    length = (i + count > charCount)
		           ? charCount
		           : i + count,
		    type = this.grammar.any;

		if (i < 0 || i >= charCount - 1) {
			return;
		}

		for (i; i < length; i++) {
			if (type.test(chars[i])) {
				chars[i] = this.grammar.placeholder;
			}
		}

		selection.start = selection.end = actualStart;
	},

	_shiftCharacters: function(selection, start, direction, chars) {
		var count = 1,
		    actualStart = this._nextCharIndex(start, direction, this.grammar.any, chars),
		    i = actualStart,
		    type = this.maskChars[i],
		    shiftIndex = -1,
		    charCount = chars.length;

		if (i < 0 || i >= charCount) {
			return;
		}

		for (i; i < charCount; i++) {
			if (type.test(chars[i])) {
				shiftIndex = (shiftIndex === -1)
				           ? this._nextCharIndex(i + count, this.DIRECTION_FORWARDS, type, chars)
				           : this._nextCharIndex(shiftIndex + 1, this.DIRECTION_FORWARDS, type, chars);

				if (shiftIndex > -1 && shiftIndex < charCount && type.test(chars[shiftIndex])) {
					chars[i] = chars[shiftIndex];
				}
				else {
					chars[i] = this.grammar.placeholder;
				}
			}
		}

		selection.start = selection.end = actualStart;
	},

	setMask: function(mask) {
		if (!this.grammar) {
			throw new Error("Cannot set mask without a grammar object");
		}

		this.mask = mask;
		this.maskChars = this.grammar.compile(mask);
	},

	test: function(value) {
		var chars = value.split(""),
		    i = 0,
		    length = chars.length,
		    valid = true,
		    totalMaskChars = this.maskChars.length,
		    c;

		if (length != totalMaskChars) {
			valid = false;
		}
		else {
			for (i; i < length; i++) {
				c = chars[i];

				if (c === this.grammar.placeholder) {
					continue;
				}
				else if (i >= totalMaskChars || !this.maskChars[i].test(c)) {
					valid = false;
					break;
				}
			}
		}

		return valid;
	}

};

InputMaskModule.Template = Template;

})(InputMaskModule);
<commit_msg>Removing unused methods from Template and marking a method as private.<commit_after>(function(InputMaskModule) {

var _id = 0,
    _templates = {};

function Template(grammar, mask, name) {
	this.grammar = grammar || null;

	if (mask) {
		this.setMask(mask);
	}

	this.name = name || String(++_id);
}

Template.defaultGrammar = new InputMaskModule.Grammar();

Template.getByElement = function(element) {
	var name = element.getAttribute("data-mask-name"),
	    mask = element.getAttribute("data-mask");

	if (name) {
		if (_templates[name]) {
			return _templates[name];
		}
		else {
			throw new Error("No instance of InputMaskModule.Template found for name: " + name);
		}
	}
	else if (mask) {
		if (!_templates[mask]) {
			_templates[mask] = new Template(Template.defaultGrammar, mask);
		}

		return _templates[mask];
	}
	else {
		throw new Error("Missing one of data-mask or data-mask-name on " + element.nodeName);
	}
};

Template.register = function(name, mask) {
	if (_templates[name]) {
		throw new Error("A template named '" + name + "' has already been registered");
	}

	_templates[name] = new Template(Template.defaultGrammar, mask, name);

	return _templates[name];
};

Template.prototype = {

	DIRECTION_BACKWARDS: -1,
	DIRECTION_FORWARDS: 1,

	grammar: null,

	mask: "",

	maskChars: null,

	name: null,

	constructor: Template,

	addCharacter: function(start, newChar, text) {
		var chars = text.split(""),
		    actualStart = this._nextCharIndex(start, this.DIRECTION_FORWARDS, this.grammar.anyOrPlaceholder, chars),
		    i = actualStart,
		    charCount = chars.length,
		    selection = { text: text, start: actualStart, end: actualStart, length: 0 };

		if (actualStart < 0) {
			selection.start = selection.end = start;
		}
		else if (this._isValidCharAt(newChar, actualStart)) {
			chars[actualStart] = newChar;
			i = this._nextCharIndex(actualStart + 1, this.DIRECTION_FORWARDS, this.grammar.anyOrPlaceholder, chars);

			if (i > -1) {
				selection.text = chars.join("");
				selection.start = selection.end = i;
			}
			else if (actualStart === charCount - 1) {
				selection.text = chars.join("");
				selection.start = selection.end = charCount;
			}
		}

		return selection;
	},

	getMaskedValue: function(value) {
		var maskChars = this.mask.split(""),
		    valueChars = value.split(""),
		    i = 0, length = maskChars.length,
		    mc, vc, maskedValue = "",
		    grammar = this.grammar,
		    placeholder = grammar.placeholder;

		for (i; i < length; i++) {
			mc = maskChars[i];
			vc = valueChars[i] || "";

			if (mc === grammar.charMarker) {
				maskedValue += grammar.chars.test(vc) ? vc : placeholder;
			}
			else if (mc === grammar.digitMarker) {
				maskedValue += grammar.digits.test(vc) ? vc : placeholder;
			}
			else {
				maskedValue += vc || mc;
			}
		}

		return maskedValue;
	},

	getPlaceholder: function() {
		return this.grammar.placeholder;
	},

	isEmptyValue: function(value) {
		var emptyValue = this.mask
			.replace(new RegExp(this.grammar.digitMarker, "g"), this.grammar.placeholder)
			.replace(new RegExp(this.grammar.charMarker, "g"), this.grammar.placeholder);

		return emptyValue === value;
	},

	_isValidCharAt: function(c, index) {
		if (index < 0 || index >= this.maskChars.length) {
			return false;
		}
		else {
			return this.maskChars[index].test(c);
		}
	},

	_nextCharIndex: function(start, direction, type, chars) {
		var index = -1,
		    i = start,
		    length = chars.length;

		if (direction === 1) {
			for (i; i < length; i++) {
				if (type.test(chars[i])) {
					index = i;
					break;
				}
			}
		}
		else {
			while (i--) {
				if (type.test(chars[i])) {
					index = i;
					break;
				}
			}
		}

		return index;
	},

	removeNextChar: function(start, text) {
		return this._removeChars(start, 0, this.DIRECTION_FORWARDS, text);
	},

	removePrevChar: function(start, text) {
		return this._removeChars(start, 0, this.DIRECTION_BACKWARDS, text);
	},

	removeCharRange: function(start, end, text) {
		return this._removeChars(start, end - start, this.DIRECTION_FORWARDS, text);
	},

	_removeChars: function(start, count, direction, text) {
		start = (start < 0) ? 0 : start;
		count = (count < 0) ? 0 : count;
		direction = direction < 0 ? -1 : 1;

		var selection = { text: null, start: start, end: start, length: 0 },
		    chars = text.split("");

		if (count === 0) {
			this._shiftCharacters(selection, start, direction, chars);
		}
		else {
			this._removeCharacterRange(selection, start, count, chars);
		}

		selection.text = chars.join("");

		return selection;
	},

	_removeCharacterRange: function(selection, start, count, chars) {
		var actualStart = this._nextCharIndex(start, 1, this.grammar.any, chars),
		    i = actualStart,
		    charCount = chars.length,
		    length = (i + count > charCount)
		           ? charCount
		           : i + count,
		    type = this.grammar.any;

		if (i < 0 || i >= charCount - 1) {
			return;
		}

		for (i; i < length; i++) {
			if (type.test(chars[i])) {
				chars[i] = this.grammar.placeholder;
			}
		}

		selection.start = selection.end = actualStart;
	},

	_shiftCharacters: function(selection, start, direction, chars) {
		var count = 1,
		    actualStart = this._nextCharIndex(start, direction, this.grammar.any, chars),
		    i = actualStart,
		    type = this.maskChars[i],
		    shiftIndex = -1,
		    charCount = chars.length;

		if (i < 0 || i >= charCount) {
			return;
		}

		for (i; i < charCount; i++) {
			if (type.test(chars[i])) {
				shiftIndex = (shiftIndex === -1)
				           ? this._nextCharIndex(i + count, this.DIRECTION_FORWARDS, type, chars)
				           : this._nextCharIndex(shiftIndex + 1, this.DIRECTION_FORWARDS, type, chars);

				if (shiftIndex > -1 && shiftIndex < charCount && type.test(chars[shiftIndex])) {
					chars[i] = chars[shiftIndex];
				}
				else {
					chars[i] = this.grammar.placeholder;
				}
			}
		}

		selection.start = selection.end = actualStart;
	},

	setMask: function(mask) {
		if (!this.grammar) {
			throw new Error("Cannot set mask without a grammar object");
		}

		this.mask = mask;
		this.maskChars = this.grammar.compile(mask);
	},

	test: function(value) {
		var chars = value.split(""),
		    i = 0,
		    length = chars.length,
		    valid = true,
		    totalMaskChars = this.maskChars.length,
		    c;

		if (length != totalMaskChars) {
			valid = false;
		}
		else {
			for (i; i < length; i++) {
				c = chars[i];

				if (c === this.grammar.placeholder) {
					continue;
				}
				else if (i >= totalMaskChars || !this.maskChars[i].test(c)) {
					valid = false;
					break;
				}
			}
		}

		return valid;
	}

};

InputMaskModule.Template = Template;

})(InputMaskModule);
<|endoftext|>


<commit_before>//
//    █████╗ ██╗  ██╗██╗██╗     ███████╗███████╗███████╗
//   ██╔══██╗██║ ██╔╝██║██║     ██╔════╝██╔════╝╚══███╔╝
//   ███████║█████╔╝ ██║██║     █████╗  █████╗    ███╔╝
//   ██╔══██║██╔═██╗ ██║██║     ██╔══╝  ██╔══╝   ███╔╝
//   ██║  ██║██║  ██╗██║███████╗███████╗███████╗███████╗
//   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚══════╝╚══════╝╚══════╝╚══════╝

// Rendr Requirements
// ///////////////////////////////////////////////////////////////////////////////

var extend        = require('toolz/src/object/extend')
var dateFormat    = require('toolz/src/date/dateFormat')
var isNullOrUndef = require('toolz/src/lang/isNullOrUndef')
var iterate       = require('toolz/src/async/iterate')
var concurrent    = require('toolz/src/async/concurrent')
var writeFile     = require('toolz/src/file/writeFile')
var Map           = require('toolz/src/cache/map')
var matter        = require('./src/matter')
var layouts       = require('./src/layouts')
var handlebars    = require('handlebars')
var prettify      = require('js-beautify').html
var path          = require('path')
var assert        = require('assert')
var logger        = require('./util/logger')

var cache         = new Map()

// Rendr Process
// ///////////////////////////////////////////////////////////////////////////////

function rendr (files, stack, globals, defaults, cb) {
  iterate.each(files, function (page, key, done) {
    // read file, normalize output
    // console.log(f, ' processing')
    // var page = matter.read(f)
    var meta = page.data || {}
    var text = page.content || ''

    // simulate gulp-draft plugin
    if (meta.draft === true)  return done(null, key)

    // append destination to metadata
    meta.dest = dest(page.path, meta, defaults)

    // check for and assign default layout
    if (isNullOrUndef(meta.layout) || !stack[meta.layout]) {
      meta.layout = defaults.defaultLayout
    }
    // merge context
    var context = extend({}, globals, stack[meta.layout].locals, meta)

    // apply layouts to content
    text = layouts(text, meta.layout, stack, {
      layoutDelims: ['{{=', '}}'],
      defaultLayout: [null]
    }).result

    // compile template
    var template = handlebars.compile(text, {
      noEscape: true,
      preventIndent: true,
      assumeObjects: true
    })

    // mkdir if non-existant, write file to dest and prettify rendered template
    return writeFile(meta.dest.fpath, rendrFilez(template(context), defaults), function (err) {
      assert.ifError(err)
      logger.file(meta.dest.fpath, 'rendered')
      done(null, key)
    })
  }, function (err, result) {
    // callback for iterate.each
    assert.ifError(err)
    cb()
  })
}

function rendrFilez (template, defaults) {
  // if (meta.extname === 'xml') return template
  // built for js-beautify and custom enhancements
  if (defaults.engine === 'html') {
    return prettify(template, defaults.pretty[defaults.engine])
      .replace(/(<\/(a|span|strong|em|h1|h2|h3|h4|h5|h6)>(?!(,|\.|!|\?|;|:)))/g, "$1 ")
      .replace(/(\r\n|\n){2,}/g, "\n")
      .replace(/(\s*(?:<!--|\/\*)\s*)(?:(?!.+(\s*(?:<!--|\/\*)\s*)))/g, "\n$1")
  } else if (defaults.engine === 'css') {
    return prettify(template, defaults.pretty[defaults.engine])
      // put future replacements here for further beautifying
  } else if (defaults.engine === 'js') {
    return prettify(template, defaults.pretty[defaults.engine])
      // put future replacements here for further beautifying
  } else {
    return template
  }
}

function dest (file, meta, defaults) {
  // get file information and return (annotated version of frontMatter() )
  // purpose to simulate vinly-fs dest info for appending to metadata
  // of each file. Used to determine relative file path for navigation

  if (cache.has(file)) return cache.get(file)

  var pathSeparator = '/'
  var buildDir      = defaults.destination
  var buildFileExt  = path.basename(file).replace(/hbs$/, meta.extname || defaults.engine)
  var buildFile     = path.basename(file, path.extname(file))
  var buildExt      = path.extname(file).replace(/hbs$/, meta.extname || defaults.engine)
  var buildPath     = path.dirname(file).replace(defaults.templateRoot, '')
  var buildDirFileExt
  var buildDirPathFileExt
  var results

  if (buildPath) {
    buildDirFileExt     = [buildPath, pathSeparator, buildFileExt].join('')
    buildDirPathFileExt = buildDir + buildDirFileExt
  } else {
    buildDirFileExt     = buildFileExt
    buildDirPathFileExt = [buildDir, pathSeparator, buildDirFileExt].join('')
  }

  results = {
    dirname  : buildDir,
    bpath    : buildDir + buildPath,
    fpath    : buildDirPathFileExt,
    basename : buildFileExt,
    name     : buildFile,
    extname  : buildExt
  }

  cache.set(file, results)
  // console.log(cache.get())
  return results
}

// Stack Object
// ///////////////////////////////////////////////////////////////////////////////

function buildLayoutStack (files, cb) {
  // build an array of layouts for use in wrapping templates
  var stack = {}
  iterate.each(files, function(f, key, done) {
    var namespace = path.basename(f, path.extname(f))
    var page = matter.read(f)
    var meta = page.data || {}
    var text = page.content || ''

    stack[namespace] = {
      content: text,
      locals: meta,
      layout: meta.layout || ''
    }
    done(null, key)
  }, function (err, results) {
    assert.ifError(err)
    cb(null, stack)
  })
}

// frontMatter
// ///////////////////////////////////////////////////////////////////////////////

function frontMatter (filenames, defaults, cb) {
  // build an object of all templates' yaml-front-matter and append file path
  // metadata to each key. Used for navigation, collections, sitemaps, sorting
  // and many other things.
  var parsed = {}
  var fileSeparator = '-'
  var pathSeparator = '/'
  var autolink = '..'

  iterate.each(filenames, function (f, key, done) {
    var baseName = path.dirname(f)
      .split(path.sep)
      .slice(-1)[0]
      + fileSeparator
      + path.basename(f, path.extname(f))

    var metadata = matter.read(f).data

    metadata.STRT = '---------------------------------------------'

    if (metadata.pubDate) metadata.iso8601Date = dateFormat(metadata.pubDate, 'iso')
    else metadata.iso8601Date = dateFormat('iso')

    metadata.parentPath = path.dirname(f)
      + pathSeparator
      + f.split(path.sep)
      .slice( -1 )[0]
      .replace(/hbs$/, defaults.engine)

    metadata.parentDir = path.dirname(f).split(path.sep).slice( 3 )[0]

    // set regex to remove path items which will not translate
    // to the build directory in the options
    metadata.buildDir = path.dirname(f).replace(defaults.templateRoot, '')

    if (metadata.buildDir) {
      metadata.buildDirFileExt = metadata.buildDir
        + pathSeparator
        + path.basename(f)
        .replace(/hbs$/, defaults.engine)
    } else {
      metadata.buildDirFileExt = pathSeparator
        + path.basename(f)
        .replace(/hbs$/, defaults.engine)
    }

    metadata.buildFileExt = path.basename(f).replace(/hbs$/, defaults.engine)
    metadata.buildFile    = path.basename(f, path.extname(f))
    metadata.buildExt     = path.extname(f).replace(/hbs$/, defaults.engine)
    metadata.buildDest    = defaults.destination + metadata.buildDirFileExt
    metadata.buildPath    = defaults.destination + metadata.buildDir
    metadata.buildSlug    = metadata.slug
    metadata.buildTitle   = metadata.navTitle
    metadata.autolink     = autolink + metadata.buildDirFileExt

    metadata.END = '----------------------------------------------'

    parsed[baseName] = metadata;
    done(null, key)
  }, function (err, result) { // callback for eachAsync
    assert.ifError(err)
    cb(null, parsed)
  })
}

// readFile
// ///////////////////////////////////////////////////////////////////////////////

function readFile (fn) { // fn = filename;
  var page = {}
  var fileSeparator = '-'
  var basename = path.dirname(fn)
    .split(path.sep)
    .slice(-1)[0]
    + fileSeparator
    + path.basename(fn, path.extname(fn))
  page[basename] = matter.read(fn)
  return page
}

// Expose API
// ///////////////////////////////////////////////////////////////////////////////

exports.rendr = rendr
exports.frontMatter = frontMatter
exports.buildLayoutStack = buildLayoutStack
exports.readFile = readFile
<commit_msg>update app/rendr.js<commit_after>//
//    █████╗ ██╗  ██╗██╗██╗     ███████╗███████╗███████╗
//   ██╔══██╗██║ ██╔╝██║██║     ██╔════╝██╔════╝╚══███╔╝
//   ███████║█████╔╝ ██║██║     █████╗  █████╗    ███╔╝
//   ██╔══██║██╔═██╗ ██║██║     ██╔══╝  ██╔══╝   ███╔╝
//   ██║  ██║██║  ██╗██║███████╗███████╗███████╗███████╗
//   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚══════╝╚══════╝╚══════╝╚══════╝

// Rendr Requirements
// ///////////////////////////////////////////////////////////////////////////////

var extend        = require('toolz/src/object/extend')
var dateFormat    = require('toolz/src/date/dateFormat')
var isNullOrUndef = require('toolz/src/lang/isNullOrUndef')
var iterate       = require('toolz/src/async/iterate')
var concurrent    = require('toolz/src/async/concurrent')
var writeFile     = require('toolz/src/file/writeFile')
var segments      = require('toolz/src/path/segments')
var Map           = require('toolz/src/cache/map')
var matter        = require('./src/matter')
var layouts       = require('./src/layouts')
var handlebars    = require('handlebars')
var prettify      = require('js-beautify').html
var path          = require('path')
var assert        = require('assert')
var logger        = require('./util/logger')

var cache         = new Map()

// Rendr Process
// ///////////////////////////////////////////////////////////////////////////////

function rendr (files, stack, globals, defaults, cb) {
  iterate.each(files, function (page, key, done) {
    var tmpl = {}
    // read file, normalize output
    // console.log(f, ' processing')
    // var page = matter.read(f)
    var meta = page.data || {}
    var text = page.content || ''

    // simulate gulp-draft plugin
    if (meta.draft === true)  return done(null, key)

    // append destination to metadata
    meta.dest = dest(page.path, meta, defaults)

    // check for and assign default layout
    if (isNullOrUndef(meta.layout) || !stack[meta.layout]) {
      meta.layout = defaults.defaultLayout
    }

    // namespace metadata for primarily for navigation but
    // other uses as well.
    tmpl.tmpl = meta
    // merge context
    var context = extend({}, globals, stack[meta.layout].locals, meta, tmpl)

    // apply layouts to content
    text = layouts(text, meta.layout, stack, {
      layoutDelims: ['{{=', '}}'],
      defaultLayout: [null]
    }).result

    // compile template
    var template = handlebars.compile(text, {
      noEscape: true,
      preventIndent: true,
      assumeObjects: true
    })

    // mkdir if non-existant, write file to dest and prettify rendered template
    return writeFile(meta.dest.fpath, rendrFilez(template(context), defaults), function (err) {
      assert.ifError(err)
      logger.file(meta.dest.fpath, 'rendered')
      done(null, key)
    })
  }, function (err, result) {
    // callback for iterate.each
    assert.ifError(err)
    cb()
  })
}

function rendrFilez (template, defaults) {
  // if (meta.extname === 'xml') return template
  // built for js-beautify and custom enhancements
  if (defaults.engine === 'html') {
    return prettify(template, defaults.pretty[defaults.engine])
      .replace(/(<\/(a|span|strong|em|h1|h2|h3|h4|h5|h6)>(?!(,|\.|!|\?|;|:)))/g, "$1 ")
      .replace(/(\r\n|\n){2,}/g, "\n")
      .replace(/(\s*(?:<!--|\/\*)\s*)(?:(?!.+(\s*(?:<!--|\/\*)\s*)))/g, "\n$1")
  } else if (defaults.engine === 'css') {
    return prettify(template, defaults.pretty[defaults.engine])
      // put future replacements here for further beautifying
  } else if (defaults.engine === 'js') {
    return prettify(template, defaults.pretty[defaults.engine])
      // put future replacements here for further beautifying
  } else {
    return template
  }
}

function dest (file, meta, defaults) {
  // get file information and return (annotated version of frontMatter() )
  // purpose to simulate vinly-fs dest info for appending to metadata
  // of each file. Used to determine relative file path for navigation

  if (cache.has(file)) return cache.get(file)

  var pathSeparator = '/'
  var buildDir      = defaults.destination
  var buildFileExt  = path.basename(file).replace(/hbs$/, meta.extname || defaults.engine)
  var buildFile     = path.basename(file, path.extname(file))
  var buildExt      = path.extname(file).replace(/hbs$/, meta.extname || defaults.engine)
  var buildPath     = path.dirname(file).replace(defaults.templateRoot, '')
  var buildDirFileExt
  var buildDirPathFileExt
  var results

  if (buildPath) {
    buildDirFileExt     = [buildPath, pathSeparator, buildFileExt].join('')
    buildDirPathFileExt = buildDir + buildDirFileExt
  } else {
    buildDirFileExt     = buildFileExt
    buildDirPathFileExt = [buildDir, pathSeparator, buildDirFileExt].join('')
  }

  results = {
    dirname  : buildDir,
    bpath    : buildDir + buildPath,
    fpath    : buildDirPathFileExt,
    basename : buildFileExt,
    name     : buildFile,
    extname  : buildExt
  }

  cache.set(file, results)
  // console.log(cache.get())
  return results
}

// Stack Object
// ///////////////////////////////////////////////////////////////////////////////

function buildLayoutStack (files, cb) {
  // build an array of layouts for use in wrapping templates
  var stack = {}
  iterate.each(files, function(f, key, done) {
    var namespace = path.basename(f, path.extname(f))
    var page = matter.read(f)
    var meta = page.data || {}
    var text = page.content || ''

    stack[namespace] = {
      content: text,
      locals: meta,
      layout: meta.layout || ''
    }
    done(null, key)
  }, function (err, results) {
    assert.ifError(err)
    cb(null, stack)
  })
}

// frontMatter
// ///////////////////////////////////////////////////////////////////////////////

function frontMatter (filenames, defaults, cb) {
  // build an object of all templates' yaml-front-matter and append file path
  // metadata to each key. Used for navigation, collections, sitemaps, sorting
  // and many other things.
  var parsed = {}
  var fileSeparator = '-'
  var pathSeparator = '/'
  var autolink = '..'

  iterate.each(filenames, function (f, key, done) {
    var baseName = path.dirname(f)
      .split(path.sep)
      .slice(-1)[0]
      + fileSeparator
      + path.basename(f, path.extname(f))

    var metadata = matter.read(f).data

    metadata.STRT = '---------------------------------------------'

    if (metadata.pubDate) metadata.iso8601Date = dateFormat(metadata.pubDate, 'iso')
    else metadata.iso8601Date = dateFormat('iso')

    metadata.parentPath = path.dirname(f)
      + pathSeparator
      + f.split(path.sep)
      .slice( -1 )[0]
      .replace(/hbs$/, defaults.engine)

    metadata.parentDir = path.dirname(f).split(path.sep).slice( 3 )[0]

    // set regex to remove path items which will not translate
    // to the build directory in the options
    metadata.buildDir = path.dirname(f).replace(defaults.templateRoot, '')

    if (metadata.buildDir) {
      metadata.buildDirFileExt = metadata.buildDir
        + pathSeparator
        + path.basename(f)
        .replace(/hbs$/, defaults.engine)
    } else {
      metadata.buildDirFileExt = pathSeparator
        + path.basename(f)
        .replace(/hbs$/, defaults.engine)
    }

    metadata.buildFileExt = path.basename(f).replace(/hbs$/, defaults.engine)
    metadata.buildFile    = path.basename(f, path.extname(f))
    metadata.buildExt     = path.extname(f).replace(/hbs$/, defaults.engine)
    metadata.buildDest    = defaults.destination + metadata.buildDirFileExt
    metadata.buildPath    = defaults.destination + metadata.buildDir
    metadata.autolink     = autolink + metadata.buildDirFileExt

    metadata.END = '----------------------------------------------'

    parsed[baseName] = metadata;
    done(null, key)
  }, function (err, result) { // callback for eachAsync
    assert.ifError(err)
    cb(null, parsed)
  })
}

// readFile
// ///////////////////////////////////////////////////////////////////////////////

function readFile (fn, opt) { // fn = filename;
  var page = {}
  var basename

  if (opt) {
    basename = segments.last(fn).replace(/\.hbs$/, '')
  } else {
    basename = segments.last(fn, 2, '-').replace(/\.hbs$/, '')
  }

  page[basename] = matter.read(fn)
  return page
}

// Expose API
// ///////////////////////////////////////////////////////////////////////////////

exports.rendr = rendr
exports.frontMatter = frontMatter
exports.buildLayoutStack = buildLayoutStack
exports.readFile = readFile
<|endoftext|>


<commit_before>/**
 * @file MaterialDatePicker
 * @author sunday(sunday.wei@derbysoft.com)
 */

import MaterialDatePicker from './MaterialDatePicker';

export default from './MaterialDatePicker';<commit_msg>update MaterialDatePicker<commit_after>/**
 * @file MaterialDatePicker
 * @author sunday(sunday.wei@derbysoft.com)
 */

export default from './MaterialDatePicker';<|endoftext|>


<commit_before>'use strict';

/**
 * @license
 * Copyright SOAJS All Rights Reserved.
 *
 * Use of this source code is governed by an Apache license that can be
 * found in the LICENSE file at the root of this repository
 */

const lib = {
    "mail": require("../../lib/mail.js"),
    "pin": require("../../lib/pin.js")
};

let bl = null;
let local = (soajs, inputmaskData, options, cb) => {
    let modelObj = bl.user.mt.getModel(soajs);
    options = {};
    options.mongoCore = modelObj.mongoCore;

    inputmaskData = inputmaskData || {};

    bl.user.countUser(soajs, inputmaskData, options, (error, found) => {
        if (error) {
            //close model
            bl.user.mt.closeModel(modelObj);
            return cb(bl.user.handleError(soajs, 602, error), null);
        }
        if (found) {
            //close model
            bl.user.mt.closeModel(modelObj);
            return cb(bl.user.handleError(soajs, 602, null));
        }

        inputmaskData.config = {};

        let doAdd = (pin, pinCode) => {
            bl.user.add(soajs, inputmaskData, options, (error, userRecord) => {
                if (error) {
                    //close model
                    bl.user.mt.closeModel(modelObj);
                    return cb(error, null);
                }
                if (pin) {
                    userRecord.pin = pinCode;
                    lib.mail.send(soajs, 'invitePin', userRecord, null, function (error) {
                        if (error) {
                            soajs.log.info('invitePin: No Mail was sent: ' + error);
                        }
                    });
                }
                if (userRecord.status !== "pendingNew") {
                    //close model
                    bl.user.mt.closeModel(modelObj);
                    return cb(null, {
                        id: userRecord._id.toString()
                    });
                }
                else {
                    let data = {};
                    data.userId = userRecord._id.toString();
                    data.username = userRecord.username;
                    data.service = "addUser";
                    bl.token.add(soajs, data, options, (error, tokenRecord) => {
                        bl.user.mt.closeModel(modelObj);
                        if (error) {
                            return cb(error, null);
                        }
                        lib.mail.send(soajs, "addUser", userRecord, tokenRecord, function (error, mailRecord) {
                            if (error) {
                                soajs.log.info('addUser: No Mail was sent: ' + error);
                            }
                            return cb(null, {
                                id: userRecord._id.toString(),
                                token: tokenRecord.token,
                                link: mailRecord.link || null
                            });
                        });
                    });
                }
            });
        };

        let doPin = (mainTenant) => {
            let generatedPin = null;
            if (inputmaskData.pin && inputmaskData.pin.code) {
                let pinConfig = lib.pin.config(soajs, bl.user.localConfig);
                try {
                    generatedPin = lib.pin.generate(pinConfig);
                    if (mainTenant) {
                        inputmaskData.tenant.pin.code = generatedPin;
                        inputmaskData.tenant.pin.allowed = !!inputmaskData.pin.allowed;
                    }
                    else {
                        inputmaskData.config.allowedTenants[0].tenant.pin.code = generatedPin;
                        inputmaskData.config.allowedTenants[0].tenant.pin.allowed = !!inputmaskData.pin.allowed;
                    }
                    doAdd(true, generatedPin);
                } catch (e) {
                    //close model
                    bl.user.mt.closeModel(modelObj);
                    return cb(bl.user.handleError(soajs, 525, e));
                }
            }
            else {
                doAdd(false, null);
            }
        };

        if (soajs.tenant.type === "client" && soajs.tenant.main) {
            inputmaskData.tenant = {
                id: soajs.tenant.main.id,
                code: soajs.tenant.main.code,
                pin: {}
            };
            if (!inputmaskData.config.allowedTenants) {
                inputmaskData.config.allowedTenants = [];
            }
            let allowedTenantObj = {
                "tenant": {
                    "id": soajs.tenant.id,
                    "code": soajs.tenant.code,
                    "pin": {}
                }
            };
            if (inputmaskData.groups) {
                allowedTenantObj.groups = inputmaskData.groups;
                inputmaskData.groups = [];
            }

            inputmaskData.config.allowedTenants.push(allowedTenantObj);

            doPin(false);
        } else {
            inputmaskData.tenant = {
                "id": soajs.tenant.id,
                "code": soajs.tenant.code,
                "pin": {}
            };

            doPin(true);
        }
    });
};

module.exports = function (_bl) {
    bl = _bl;
    return local;
};<commit_msg>addUser to send an email with the password if the password was sent as input to the API<commit_after>'use strict';

/**
 * @license
 * Copyright SOAJS All Rights Reserved.
 *
 * Use of this source code is governed by an Apache license that can be
 * found in the LICENSE file at the root of this repository
 */

const lib = {
    "mail": require("../../lib/mail.js"),
    "pin": require("../../lib/pin.js")
};

let bl = null;
let local = (soajs, inputmaskData, options, cb) => {
    let modelObj = bl.user.mt.getModel(soajs);
    options = {};
    options.mongoCore = modelObj.mongoCore;

    inputmaskData = inputmaskData || {};

    bl.user.countUser(soajs, inputmaskData, options, (error, found) => {
        if (error) {
            //close model
            bl.user.mt.closeModel(modelObj);
            return cb(bl.user.handleError(soajs, 602, error), null);
        }
        if (found) {
            //close model
            bl.user.mt.closeModel(modelObj);
            return cb(bl.user.handleError(soajs, 602, null));
        }

        inputmaskData.config = {};

        let doAdd = (pin, pinCode) => {
            bl.user.add(soajs, inputmaskData, options, (error, userRecord) => {
                if (error) {
                    //close model
                    bl.user.mt.closeModel(modelObj);
                    return cb(error, null);
                }
                if (pin) {
                    userRecord.pin = pinCode;
                    lib.mail.send(soajs, 'invitePin', userRecord, null, function (error) {
                        if (error) {
                            soajs.log.info('invitePin: No Mail was sent: ' + error);
                        }
                    });
                }
                if (userRecord.status !== "pendingNew") {
                    //close model
                    bl.user.mt.closeModel(modelObj);

                    lib.mail.send(soajs, "addUserNotPending", userRecord, null, function (error) {
                        if (error) {
                            soajs.log.info('addUserNotPending: No Mail was sent: ' + error);
                        }
                        return cb(null, {
                            id: userRecord._id.toString()
                        });
                    });
                }
                else {
                    let data = {};
                    data.userId = userRecord._id.toString();
                    data.username = userRecord.username;
                    data.service = "addUser";
                    bl.token.add(soajs, data, options, (error, tokenRecord) => {
                        bl.user.mt.closeModel(modelObj);
                        if (error) {
                            return cb(error, null);
                        }
                        lib.mail.send(soajs, "addUser", userRecord, tokenRecord, function (error, mailRecord) {
                            if (error) {
                                soajs.log.info('addUser: No Mail was sent: ' + error);
                            }
                            return cb(null, {
                                id: userRecord._id.toString(),
                                token: tokenRecord.token,
                                link: mailRecord.link || null
                            });
                        });
                    });
                }
            });
        };

        let doPin = (mainTenant) => {
            let generatedPin = null;
            if (inputmaskData.pin && inputmaskData.pin.code) {
                let pinConfig = lib.pin.config(soajs, bl.user.localConfig);
                try {
                    generatedPin = lib.pin.generate(pinConfig);
                    if (mainTenant) {
                        inputmaskData.tenant.pin.code = generatedPin;
                        inputmaskData.tenant.pin.allowed = !!inputmaskData.pin.allowed;
                    }
                    else {
                        inputmaskData.config.allowedTenants[0].tenant.pin.code = generatedPin;
                        inputmaskData.config.allowedTenants[0].tenant.pin.allowed = !!inputmaskData.pin.allowed;
                    }
                    doAdd(true, generatedPin);
                } catch (e) {
                    //close model
                    bl.user.mt.closeModel(modelObj);
                    return cb(bl.user.handleError(soajs, 525, e));
                }
            }
            else {
                doAdd(false, null);
            }
        };

        if (soajs.tenant.type === "client" && soajs.tenant.main) {
            inputmaskData.tenant = {
                id: soajs.tenant.main.id,
                code: soajs.tenant.main.code,
                pin: {}
            };
            if (!inputmaskData.config.allowedTenants) {
                inputmaskData.config.allowedTenants = [];
            }
            let allowedTenantObj = {
                "tenant": {
                    "id": soajs.tenant.id,
                    "code": soajs.tenant.code,
                    "pin": {}
                }
            };
            if (inputmaskData.groups) {
                allowedTenantObj.groups = inputmaskData.groups;
                inputmaskData.groups = [];
            }

            inputmaskData.config.allowedTenants.push(allowedTenantObj);

            doPin(false);
        } else {
            inputmaskData.tenant = {
                "id": soajs.tenant.id,
                "code": soajs.tenant.code,
                "pin": {}
            };

            doPin(true);
        }
    });
};

module.exports = function (_bl) {
    bl = _bl;
    return local;
};<|endoftext|>


<commit_before>/**
 * @file bbn-autocomplete component
 * @description The autocomplete allows to select a single value from a list of items by proposeing suggestions based on the typed characters.
 * @copyright BBN Solutions
 * @author BBN Solutions
 * @created 10/02/2017.
 */

(function(bbn){
  "use strict";

  Vue.component('bbn-autocomplete', {
    /**
     * @mixin bbn.vue.basicComponent
     * @mixin bbn.vue.eventsComponent
     * @mixin bbn.vue.inputComponent
     * @mixin bbn.vue.resizerComponent
     * @mixin bbn.vue.listComponent
     * @mixin bbn.vue.keynavComponent
     * @mixin bbn.vue.urlComponent
     * @mixin bbn.vue.dropdownComponent
      */
    mixins: [
      bbn.vue.basicComponent,
      bbn.vue.eventsComponent,
      bbn.vue.inputComponent,
      bbn.vue.resizerComponent,
      bbn.vue.listComponent,
      bbn.vue.keynavComponent,
      bbn.vue.urlComponent,
      bbn.vue.dropdownComponent
    ],
    props: {
      /**
       * For to apply the filters or not.
       *
       * @prop {Boolean} filterable
       */
      filterable: {
        type: Boolean,
        default: true
      },
      /**
       * To define the length of the string to start the filter.
       *
       * @prop {Number} [0] minLength
       */
      minLength: {
        type: Number,
        default: 2
      },
      /**
       * Specifies the time of delay.
       *
       * @prop {Number} [250] delay
       */
      delay: {
        type: Number,
        default: 250
      },
      /**
       * Specifies the mode of the filter.
       *
       * @prop {String} ['startswith'] filterMode
       */
      filterMode: {
        type: String,
        default: 'startswith'
      },
      /**
       * Autobind defaults at false.
       *
       * @prop {Boolean} [false] autobind
       */
      autobind: {
        type: Boolean,
        default: false
      }
    },
    data(){
      return {
        /**
         * Indicates if the filter input is visible
         * @data {Boolean} [false] inputIsVisible
         */
        inputIsVisible: false
      }
    },
    methods: {
      /**
       * Shows the filter input
       * @method _setInputVisible
       */
      _setInputVisible(){
        this.filterString = this.currentText;
        this.inputIsVisible = true;
        this.$nextTick(() => {
          this.getRef('input').focus();
        })
      },
      onChange(){
        if (!this.ready) {
          this.ready = true;
        }
      },
      /**
       * Puts the focus on the element.
       *
       * @method click
       * @fires getRef
       */
      click(){
        if (!this.isDisabled) {
          this.getRef('input').focus();
          if (this.filteredData.length) {
            this.isOpened = !this.isOpened;
          }
        }
      },
      /**
       * Remove the filter and close the list if it is notabove it.
       *
       * @method leave
       * @fires getRef
       */
      leave(){
        if ( this.isOpened && !this.getRef('list').isOver ){
          this.isOpened = false;
        }
        this.inputIsVisible = false;
        this.filterString = '';
      },
      /**
       * Emits the event 'select'.
       *
       * @method select
       * @param {Object} item
       * @fires emitInput
       * @fires getRef
       * @emit change
       */
      select(item){
        if (item) {
          const v = item;
          if (this.sourceValue && (item[this.sourceValue] !== undefined)) {
            v = item[this.sourceValue];
          }

          this.emitInput(v);
          this.$emit('change', v);
          this.currentText = item[this.sourceText];
          this.filterString = item[this.sourceText];
          this.$nextTick(() => {
            this.getRef('input').focus();
          });
        }
        this.isOpened = false;
      },
      /**
       * Function to do the reset and if the component is open it closes it.
       *
       * @method resetDropdown
       * @fires unfilter
       */
      resetDropdown(){
        this.currentText = this.currentTextValue;
        this.filterString = this.currentTextValue;
        this.unfilter();
        if ( this.isOpened ){
          this.isOpened = false;
        }
      },
      /**
       * Function that performs different actions based on what is being pressed.
       *
       * @method keydown
       * @param {Event} e
       * @fires resetDropdown
       * @fires commonKeydown
       * @fires keynav
       */
      keydown(e){
        if ( this.commonKeydown(e) ){
          return;
        }
        else if (this.isOpened && (e.key === 'Escape')) {
          e.stopPropagation();
          e.preventDefault();
          this.resetDropdown();
          return;
        }
        else if (bbn.var.keys.upDown.includes(e.keyCode)) {
          this.keynav(e);
        }
      },
    },
    /**
     * @event created
     * @fires emitInput
     */
    created(){
      this.$on('dataloaded', () => {
        if ( this.value !== undefined ){
          let row = bbn.fn.getRow(this.currentData, a => {
            return a.data[this.sourceValue] === this.value;
          });
          if ( row ){
            this.currentText = row.data[this.sourceText];
          }
        }
        if ( !this.currentText && !this.isNullable && this.filteredData.length ){
          this.emitInput(this.filteredData[0][this.sourceValue]);
        }
      });
    },
    watch: {
      /**
       * @watch filterString
       * @fires onResize
       * @fires unfilter
       * @param {String} v
       */
      filterString(v){
        if (!this.ready) {
          this.ready = true;
        }

        clearTimeout(this.filterTimeout);
        bbn.fn.log("CLEARED")
        if (!v && this.nullable && this.inputIsVisible) {
          bbn.fn.log("NO VALUE")
          this.unfilter();
          this.emitInput(null);
          this.currentText = '';
          if (this.currentData.length) {
            this.currentData.splice(0, this.currentData.length);
          }
        }
        else if (v) {
          bbn.fn.log("VALUE")
          if (v.length < this.minLength) {
            if (this.currentData.length) {
              this.currentData.splice(0, this.currentData.length);
            }
          }
          else if ((v !== this.currentText)) {
            bbn.fn.log("MIN PASSED")
            this.isOpened = false;
            this.filterTimeout = setTimeout(() => {
              // this.filterTimeout = false;
              // We don't relaunch the source if the component has been left
              if (this.isActive) {
                bbn.fn.log("UPDATING AUTOC");
                this.currentFilters.conditions.splice(0, this.currentFilters.conditions.length ? 1 : 0, {
                  field: this.sourceText,
                  operator: this.filterMode,
                  value: v
                });
                this.updateData().then(() => {
                  this.isOpened = true;
                })
              }
            }, this.delay);
          }
        }
        else if ( !v ){
          this.unfilter();
        }
      }
    }
  });

})(bbn);
<commit_msg>Added keydown event on autocomplete<commit_after>/**
 * @file bbn-autocomplete component
 * @description The autocomplete allows to select a single value from a list of items by proposeing suggestions based on the typed characters.
 * @copyright BBN Solutions
 * @author BBN Solutions
 * @created 10/02/2017.
 */

(function(bbn){
  "use strict";

  Vue.component('bbn-autocomplete', {
    /**
     * @mixin bbn.vue.basicComponent
     * @mixin bbn.vue.eventsComponent
     * @mixin bbn.vue.inputComponent
     * @mixin bbn.vue.resizerComponent
     * @mixin bbn.vue.listComponent
     * @mixin bbn.vue.keynavComponent
     * @mixin bbn.vue.urlComponent
     * @mixin bbn.vue.dropdownComponent
      */
    mixins: [
      bbn.vue.basicComponent,
      bbn.vue.eventsComponent,
      bbn.vue.inputComponent,
      bbn.vue.resizerComponent,
      bbn.vue.listComponent,
      bbn.vue.keynavComponent,
      bbn.vue.urlComponent,
      bbn.vue.dropdownComponent
    ],
    props: {
      /**
       * For to apply the filters or not.
       *
       * @prop {Boolean} filterable
       */
      filterable: {
        type: Boolean,
        default: true
      },
      /**
       * To define the length of the string to start the filter.
       *
       * @prop {Number} [0] minLength
       */
      minLength: {
        type: Number,
        default: 2
      },
      /**
       * Specifies the time of delay.
       *
       * @prop {Number} [250] delay
       */
      delay: {
        type: Number,
        default: 250
      },
      /**
       * Specifies the mode of the filter.
       *
       * @prop {String} ['startswith'] filterMode
       */
      filterMode: {
        type: String,
        default: 'startswith'
      },
      /**
       * Autobind defaults at false.
       *
       * @prop {Boolean} [false] autobind
       */
      autobind: {
        type: Boolean,
        default: false
      }
    },
    data(){
      return {
        /**
         * Indicates if the filter input is visible
         * @data {Boolean} [false] inputIsVisible
         */
        inputIsVisible: false
      }
    },
    methods: {
      /**
       * Shows the filter input
       * @method _setInputVisible
       */
      _setInputVisible(){
        this.filterString = this.currentText;
        this.inputIsVisible = true;
        this.$nextTick(() => {
          this.getRef('input').focus();
        })
      },
      onChange(){
        if (!this.ready) {
          this.ready = true;
        }
      },
      /**
       * Puts the focus on the element.
       *
       * @method click
       * @fires getRef
       */
      click(){
        if (!this.isDisabled) {
          this.getRef('input').focus();
          if (this.filteredData.length) {
            this.isOpened = !this.isOpened;
          }
        }
      },
      /**
       * Remove the filter and close the list if it is notabove it.
       *
       * @method leave
       * @fires getRef
       */
      leave(){
        if ( this.isOpened && !this.getRef('list').isOver ){
          this.isOpened = false;
        }
        this.inputIsVisible = false;
        this.filterString = '';
      },
      /**
       * Emits the event 'select'.
       *
       * @method select
       * @param {Object} item
       * @fires emitInput
       * @fires getRef
       * @emit change
       */
      select(item){
        if (item) {
          const v = item;
          if (this.sourceValue && (item[this.sourceValue] !== undefined)) {
            v = item[this.sourceValue];
          }

          this.emitInput(v);
          this.$emit('change', v);
          this.currentText = item[this.sourceText];
          this.filterString = item[this.sourceText];
          this.$nextTick(() => {
            this.getRef('input').focus();
          });
        }
        this.isOpened = false;
      },
      /**
       * Function to do the reset and if the component is open it closes it.
       *
       * @method resetDropdown
       * @fires unfilter
       */
      resetDropdown(){
        this.currentText = this.currentTextValue;
        this.filterString = this.currentTextValue;
        this.unfilter();
        if ( this.isOpened ){
          this.isOpened = false;
        }
      },
      /**
       * Function that performs different actions based on what is being pressed.
       *
       * @method keydown
       * @param {Event} e
       * @fires resetDropdown
       * @fires commonKeydown
       * @fires keynav
       */
      keydown(e){
        if ( this.commonKeydown(e) ){
          return;
        }
        else if (this.isOpened && (e.key === 'Escape')) {
          e.stopPropagation();
          e.preventDefault();
          this.resetDropdown();
          return;
        }
        else if (bbn.var.keys.upDown.includes(e.keyCode)) {
          this.keynav(e);
        }
        this.$emit('keydown', e);
      },
    },
    /**
     * @event created
     * @fires emitInput
     */
    created(){
      this.$on('dataloaded', () => {
        if ( this.value !== undefined ){
          let row = bbn.fn.getRow(this.currentData, a => {
            return a.data[this.sourceValue] === this.value;
          });
          if ( row ){
            this.currentText = row.data[this.sourceText];
          }
        }
        if ( !this.currentText && !this.isNullable && this.filteredData.length ){
          this.emitInput(this.filteredData[0][this.sourceValue]);
        }
      });
    },
    watch: {
      /**
       * @watch filterString
       * @fires onResize
       * @fires unfilter
       * @param {String} v
       */
      filterString(v){
        if (!this.ready) {
          this.ready = true;
        }

        clearTimeout(this.filterTimeout);
        if (!v && this.nullable && this.inputIsVisible) {
          this.unfilter();
          this.emitInput(null);
          this.currentText = '';
          if (this.currentData.length) {
            this.currentData.splice(0, this.currentData.length);
          }
        }
        else if (v) {
          if (v.length < this.minLength) {
            if (this.currentData.length) {
              this.currentData.splice(0, this.currentData.length);
            }
          }
          else if ((v !== this.currentText)) {
            this.isOpened = false;
            this.filterTimeout = setTimeout(() => {
              // this.filterTimeout = false;
              // We don't relaunch the source if the component has been left
              if (this.isActive) {
                this.currentFilters.conditions.splice(0, this.currentFilters.conditions.length ? 1 : 0, {
                  field: this.sourceText,
                  operator: this.filterMode,
                  value: v
                });
                this.updateData().then(() => {
                  this.isOpened = true;
                })
              }
            }, this.delay);
          }
        }
        else if ( !v ){
          this.unfilter();
        }
      }
    }
  });

})(bbn);
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 */

/* global app */

window.onerror = function myErrorHandler(errorMessage, fileUrl, lineNumber) {
  var html = [
    '
',
    'Sorry! Something went wrong. It\'s our fault, not yours.',
    'Could you please send us a note at',
    'support@tidepool.org',
    'and we\'ll try to see what broke?',
    'In the meantime, could you try refreshing your browser to reload the app?',
    '
',
    '
',
    'Original error message:',
    '"' + errorMessage + '"',
    '(' + fileUrl + ' at line ' + lineNumber + ')',
    '
'
  ].join(' ');

  var style = [
    'position: fixed;',
    'top: 0;',
    'left: 0;',
    'right: 0;',
    'padding: 0 20px;',
    'text-align: center;',
    'background: #ff8b7c;',
    'color: #fff;'
  ].join(' ');

  var el = document.createElement('div');
  el.innerHTML = html;
  el.setAttribute('style', style);
  document.body.appendChild(el);

  // Let default handler run
  return false;
};

app.start();
<commit_msg>Add close to runtime error notification<commit_after>/**
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 */

/* global app */

window.onerror = function myErrorHandler(errorMessage, fileUrl, lineNumber) {
  var html = [
    '
',
    'Sorry! Something went wrong. It\'s our fault, not yours.',
    'Could you please send us a note at',
    'support@tidepool.org',
    'and we\'ll try to see what broke?',
    'In the meantime, could you try refreshing your browser to reload the app?',
    '
',
    '
',
    'Original error message:',
    '"' + errorMessage + '"',
    '(' + fileUrl + ' at line ' + lineNumber + ')',
    '
',
    '
',
    'Close',
    '
',
  ].join(' ');

  var style = [
    'position: fixed;',
    'top: 0;',
    'left: 0;',
    'right: 0;',
    'padding: 0 20px;',
    'text-align: center;',
    'background: #ff8b7c;',
    'color: #fff;'
  ].join(' ');

  var el = document.createElement('div');
  el.innerHTML = html;
  el.setAttribute('style', style);
  document.body.appendChild(el);

  var closeEl = document.getElementById('error-close');
  closeEl.addEventListener('click', function(e) {
    e.preventDefault();
    el.parentNode.removeChild(el);
  });

  // Let default handler run
  return false;
};

app.start();
<|endoftext|>


<commit_before>import moment from 'moment';
import debounce from 'lodash.debounce';

const delayAction = debounce(action => action(), 300);

function getStatus(start, end) {
    const now = moment().unix();
    return now > start ? ( now < end ? 'active' : 'ended' ) : 'coming';
};

function isEmpty(str) {
    return str === '' || str.trim().length === 0;
};

function isDate(str, format='YYYYMMDD') {
    return moment(str, format, true).isValid();
};

function capitalize(str) {
    return str[0].toUpperCase() + str.substr(1);
};

function sortByKey(arr, key) {
    return arr.sort((a, b) => {
        return a[key].localeCompare(b[key]);
    });
};

function overlayAdd() {
    document.querySelector('body').className = 'overlay';
};

function overlayRemove() {
    document.querySelector('body').removeAttribute('class');
};

export {
    capitalize,
    delayAction,
    getStatus,
    isDate,
    isEmpty,
    overlayAdd,
    overlayRemove,
    sortByKey
};
<commit_msg>use document height to determine wheter or not to set scroll style on body with overlay<commit_after>import moment from 'moment';
import debounce from 'lodash.debounce';

const body = document.querySelector('body');
const delayAction = debounce(action => action(), 300);

function getStatus(start, end) {
    const now = moment().unix();
    return now > start ? ( now < end ? 'active' : 'ended' ) : 'coming';
};

function isEmpty(str) {
    return str === '' || str.trim().length === 0;
};

function isDate(str, format='YYYYMMDD') {
    return moment(str, format, true).isValid();
};

function capitalize(str) {
    return str[0].toUpperCase() + str.substr(1);
};

function sortByKey(arr, key) {
    return arr.sort((a, b) => {
        return a[key].localeCompare(b[key]);
    });
};

function overlayAdd() {
    const lockScroll = document.body.clientHeight >= document.documentElement.clientHeight;
    body.className = 'overlay';
    body.style.overflowY = lockScroll ? 'scroll' : '';
};

function overlayRemove() {
    body.removeAttribute('class');
    body.removeAttribute('style');
};

export {
    capitalize,
    delayAction,
    getStatus,
    isDate,
    isEmpty,
    overlayAdd,
    overlayRemove,
    sortByKey
};
<|endoftext|>


<commit_before>var Decimal = require("decimal.js");
var request = require("request");

var config = require("./config.js");
var coins = require("./coins.js");
var coinConfig = coins[config.coin];

var ipCache = {};

var exponentScales = [
	{val:1000000000000000000000000000000000, name:"?", abbreviation:"V", exponent:"33"},
	{val:1000000000000000000000000000000, name:"?", abbreviation:"W", exponent:"30"},
	{val:1000000000000000000000000000, name:"?", abbreviation:"X", exponent:"27"},
	{val:1000000000000000000000000, name:"yotta", abbreviation:"Y", exponent:"24"},
	{val:1000000000000000000000, name:"zetta", abbreviation:"Z", exponent:"21"},
	{val:1000000000000000000, name:"exa", abbreviation:"E", exponent:"18"},
	{val:1000000000000000, name:"peta", abbreviation:"P", exponent:"15"},
	{val:1000000000000, name:"tera", abbreviation:"T", exponent:"12"},
	{val:1000000000, name:"giga", abbreviation:"G", exponent:"9"},
	{val:1000000, name:"mega", abbreviation:"M", exponent:"6"},
	{val:1000, name:"kilo", abbreviation:"K", exponent:"3"}
];

function redirectToConnectPageIfNeeded(req, res) {
	if (!req.session.host) {
		req.session.redirectUrl = req.originalUrl;

		res.redirect("/");
		res.end();

		return true;
	}

	return false;
}

function hex2ascii(hex) {
	var str = "";
	for (var i = 0; i < hex.length; i += 2) {
		str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
	}

	return str;
}

function splitArrayIntoChunks(array, chunkSize) {
	var j = array.length;
	var chunks = [];

	for (var i = 0; i < j; i += chunkSize) {
		chunks.push(array.slice(i, i + chunkSize));
	}

	return chunks;
}

function getRandomString(length, chars) {
    var mask = '';

    if (chars.indexOf('a') > -1) {
		mask += 'abcdefghijklmnopqrstuvwxyz';
	}

    if (chars.indexOf('A') > -1) {
		mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	}

    if (chars.indexOf('#') > -1) {
		mask += '0123456789';
	}

	if (chars.indexOf('!') > -1) {
		mask += '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\';
	}

    var result = '';
    for (var i = length; i > 0; --i) {
		result += mask[Math.floor(Math.random() * mask.length)];
	}

	return result;
}

var formatCurrencyCache = {};

function formatCurrencyAmountWithForcedDecimalPlaces(amount, formatType, forcedDecimalPlaces) {
	if (formatCurrencyCache[formatType]) {
		var dec = new Decimal(amount);
		dec = dec.times(formatCurrencyCache[formatType].multiplier);

		var decimalPlaces = formatCurrencyCache[formatType].decimalPlaces;
		if (decimalPlaces == 0 && dec < 1) {
			decimalPlaces = 5;
		}

		if (forcedDecimalPlaces >= 0) {
			decimalPlaces = forcedDecimalPlaces;
		}

		return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + formatCurrencyCache[formatType].name;
	}

	for (var x = 0; x < coins[config.coin].currencyUnits.length; x++) {
		var currencyUnit = coins[config.coin].currencyUnits[x];

		for (var y = 0; y < currencyUnit.values.length; y++) {
			var currencyUnitValue = currencyUnit.values[y];

			if (currencyUnitValue == formatType) {
				formatCurrencyCache[formatType] = currencyUnit;

				var dec = new Decimal(amount);
				dec = dec.times(currencyUnit.multiplier);

				var decimalPlaces = currencyUnit.decimalPlaces;
				if (decimalPlaces == 0 && dec < 1) {
					decimalPlaces = 5;
				}

				if (forcedDecimalPlaces >= 0) {
					decimalPlaces = forcedDecimalPlaces;
				}

				return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + currencyUnit.name;
			}
		}
	}

	return amount;
}

function formatCurrencyAmount(amount, formatType) {
	return formatCurrencyAmountWithForcedDecimalPlaces(amount, formatType, -1);
}

function formatCurrencyAmountInSmallestUnits(amount, forcedDecimalPlaces) {
	return formatCurrencyAmountWithForcedDecimalPlaces(amount, coins[config.coin].currencyUnits[coins[config.coin].currencyUnits.length - 1].name, forcedDecimalPlaces);
}

// ref: https://stackoverflow.com/a/2901298/673828
function addThousandsSeparators(x) {
	var parts = x.toString().split(".");
	parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");

	return parts.join(".");
}

function formatExchangedCurrency(amount) {
	if (global.exchangeRate != null) {
		var dec = new Decimal(amount);
		dec = dec.times(global.exchangeRate);

		return addThousandsSeparators(dec.toDecimalPlaces(2)) + " " + coins[config.coin].exchangeRateData.exchangedCurrencyName;
	}

	return "";
}

function seededRandom(seed) {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
}

function seededRandomIntBetween(seed, min, max) {
	var rand = seededRandom(seed);
	return (min + (max - min) * rand);
}

function logMemoryUsage() {
	var mbUsed = process.memoryUsage().heapUsed / 1024 / 1024;
	mbUsed = Math.round(mbUsed * 100) / 100;

	var mbTotal = process.memoryUsage().heapTotal / 1024 / 1024;
	mbTotal = Math.round(mbTotal * 100) / 100;

	//console.log("memoryUsage: heapUsed=" + mbUsed + ", heapTotal=" + mbTotal + ", ratio=" + parseInt(mbUsed / mbTotal * 100));
}

function getMinerFromCoinbaseTx(tx) {
	if (tx == null || tx.vin == null || tx.vin.length == 0) {
		return null;
	}

	if (global.miningPoolsConfigs) {
		for (var i = 0; i < global.miningPoolsConfigs.length; i++) {
			var miningPoolsConfig = global.miningPoolsConfigs[i];

			for (var payoutAddress in miningPoolsConfig.payout_addresses) {
				if (miningPoolsConfig.payout_addresses.hasOwnProperty(payoutAddress)) {
					if (tx.vout && tx.vout.length > 0 && tx.vout[0].scriptPubKey && tx.vout[0].scriptPubKey.addresses && tx.vout[0].scriptPubKey.addresses.length > 0) {
						if (tx.vout[0].scriptPubKey.addresses[0] == payoutAddress) {
							var minerInfo = miningPoolsConfig.payout_addresses[payoutAddress];
							minerInfo.identifiedBy = "payout address " + payoutAddress;

							return minerInfo;
						}
					}
				}
			}

			for (var coinbaseTag in miningPoolsConfig.coinbase_tags) {
				if (miningPoolsConfig.coinbase_tags.hasOwnProperty(coinbaseTag)) {
					if (hex2ascii(tx.vin[0].coinbase).indexOf(coinbaseTag) != -1) {
						var minerInfo = miningPoolsConfig.coinbase_tags[coinbaseTag];
						minerInfo.identifiedBy = "coinbase tag '" + coinbaseTag + "'";

						return minerInfo;
					}
				}
			}
		}
	}

	return null;
}

function getTxTotalInputOutputValues(tx, txInputs, blockHeight) {
	var totalInputValue = new Decimal(0);
	var totalOutputValue = new Decimal(0);

	try {
		for (var i = 0; i < tx.vin.length; i++) {
			if (tx.vin[i].coinbase) {
				totalInputValue = totalInputValue.plus(new Decimal(coinConfig.blockRewardFunction(blockHeight)));

			} else {
				var txInput = txInputs[i];

				if (txInput) {
					try {
						var vout = txInput.vout[tx.vin[i].vout];
						if (vout.value) {
							totalInputValue = totalInputValue.plus(new Decimal(vout.value));
						}
					} catch (err) {
						console.log("Error getting tx.totalInputValue: err=" + err + ", txid=" + tx.txid + ", index=tx.vin[" + i + "]");
					}
				}
			}
		}

		for (var i = 0; i < tx.vout.length; i++) {
			totalOutputValue = totalOutputValue.plus(new Decimal(tx.vout[i].value));
		}
	} catch (err) {
		console.log("Error computing total input/output values for tx: err=" + err + ", tx=" + JSON.stringify(tx) + ", txInputs=" + JSON.stringify(txInputs) + ", blockHeight=" + blockHeight);
	}

	return {input:totalInputValue, output:totalOutputValue};
}

function getBlockTotalFeesFromCoinbaseTxAndBlockHeight(coinbaseTx, blockHeight) {
	if (coinbaseTx == null) {
		return 0;
	}

	var blockReward = coinConfig.blockRewardFunction(blockHeight);

	var totalOutput = new Decimal(0);
	for (var i = 0; i < coinbaseTx.vout.length; i++) {
		var outputValue = coinbaseTx.vout[i].value;
		if (outputValue > 0) {
			totalOutput = totalOutput.plus(new Decimal(outputValue));
		}
	}

	return totalOutput.minus(new Decimal(blockReward));
}

function refreshExchangeRate() {
	if (coins[config.coin].exchangeRateData) {
		request(coins[config.coin].exchangeRateData.jsonUrl, function(error, response, body) {
			if (!error && response && response.statusCode && response.statusCode == 200) {
				var responseBody = JSON.parse(body);

				var exchangeRate = coins[config.coin].exchangeRateData.responseBodySelectorFunction(responseBody);
				if (exchangeRate > 0) {
					global.exchangeRate = exchangeRate;
					global.exchangeRateUpdateTime = new Date();

					console.log("Using exchange rate: " + global.exchangeRate + " USD/" + coins[config.coin].name + " starting at " + global.exchangeRateUpdateTime);

				} else {
					console.log("Unable to get exchange rate data");
				}
			} else {
				console.log("Error:");
				console.log(error);
				console.log("Response:");
				console.log(response);
			}
		});
	}
}

// Uses IPStack.com API
function geoLocateIpAddresses(ipAddresses) {
	return new Promise(function(resolve, reject) {
		var chunks = splitArrayIntoChunks(ipAddresses, 1);

		var promises = [];
		for (var i = 0; i < chunks.length; i++) {
			var ipStr = "";
			for (var j = 0; j < chunks[i].length; j++) {
				if (j > 0) {
					ipStr = ipStr + ",";
				}

				ipStr = ipStr + chunks[i][j];
			}

			if (ipCache[ipStr] != null) {
				promises.push(new Promise(function(resolve2, reject2) {
					resolve2(ipCache[ipStr]);
				}));

			} else if (config.credentials.ipStackComApiAccessKey && config.credentials.ipStackComApiAccessKey.trim().length > 0) {
				var apiUrl = "http://api.ipstack.com/" + ipStr + "?access_key=" + config.credentials.ipStackComApiAccessKey;
				promises.push(new Promise(function(resolve2, reject2) {
					request(apiUrl, function(error, response, body) {
						if (error) {
							reject2(error);

						} else {
							resolve2(response);
						}
					});
				}));
			} else {
				promises.push(new Promise(function(resolve2, reject2) {
					resolve2(null);
				}));
			}
		}

		Promise.all(promises).then(function(results) {
			var ipDetails = {ips:[], detailsByIp:{}};

			for (var i = 0; i < results.length; i++) {
				var res = results[i];
				if (res != null && res["statusCode"] == 200) {
					var resBody = JSON.parse(res["body"]);
					var ip = resBody["ip"];

					ipDetails.ips.push(ip);
					ipDetails.detailsByIp[ip] = resBody;

					if (ipCache[ip] == null) {
						ipCache[ip] = res;
					}
				}
			}

			resolve(ipDetails);
		});
	});
}

function parseExponentStringDouble(val) {
	var [lead,decimal,pow] = val.toString().split(/e|\./);
	return +pow <= 0
		? "0." + "0".repeat(Math.abs(pow)-1) + lead + decimal
		: lead + ( +pow >= decimal.length ? (decimal + "0".repeat(+pow-decimal.length)) : (decimal.slice(0,+pow)+"."+decimal.slice(+pow)));
}

function formatLargeNumber(n, decimalPlaces) {
	for (var i = 0; i < exponentScales.length; i++) {
		var item = exponentScales[i];

		var fraction = new Decimal(n / item.val);
		if (fraction >= 1) {
			return [fraction.toDecimalPlaces(decimalPlaces), item];
		}
	}

	return [new Decimal(n).toDecimalPlaces(decimalPlaces), {}];
}

module.exports = {
	redirectToConnectPageIfNeeded: redirectToConnectPageIfNeeded,
	hex2ascii: hex2ascii,
	splitArrayIntoChunks: splitArrayIntoChunks,
	getRandomString: getRandomString,
	formatCurrencyAmount: formatCurrencyAmount,
	formatCurrencyAmountWithForcedDecimalPlaces: formatCurrencyAmountWithForcedDecimalPlaces,
	formatExchangedCurrency: formatExchangedCurrency,
	addThousandsSeparators: addThousandsSeparators,
	formatCurrencyAmountInSmallestUnits: formatCurrencyAmountInSmallestUnits,
	seededRandom: seededRandom,
	seededRandomIntBetween: seededRandomIntBetween,
	logMemoryUsage: logMemoryUsage,
	getMinerFromCoinbaseTx: getMinerFromCoinbaseTx,
	getBlockTotalFeesFromCoinbaseTxAndBlockHeight: getBlockTotalFeesFromCoinbaseTxAndBlockHeight,
	refreshExchangeRate: refreshExchangeRate,
	parseExponentStringDouble: parseExponentStringDouble,
	formatLargeNumber: formatLargeNumber,
	geoLocateIpAddresses: geoLocateIpAddresses,
	getTxTotalInputOutputValues: getTxTotalInputOutputValues
};
<commit_msg>exchange rate logging to influxdb<commit_after>var Decimal = require("decimal.js");
var request = require("request");

var config = require("./config.js");
var coins = require("./coins.js");
var coinConfig = coins[config.coin];

var ipCache = {};

var exponentScales = [
	{val:1000000000000000000000000000000000, name:"?", abbreviation:"V", exponent:"33"},
	{val:1000000000000000000000000000000, name:"?", abbreviation:"W", exponent:"30"},
	{val:1000000000000000000000000000, name:"?", abbreviation:"X", exponent:"27"},
	{val:1000000000000000000000000, name:"yotta", abbreviation:"Y", exponent:"24"},
	{val:1000000000000000000000, name:"zetta", abbreviation:"Z", exponent:"21"},
	{val:1000000000000000000, name:"exa", abbreviation:"E", exponent:"18"},
	{val:1000000000000000, name:"peta", abbreviation:"P", exponent:"15"},
	{val:1000000000000, name:"tera", abbreviation:"T", exponent:"12"},
	{val:1000000000, name:"giga", abbreviation:"G", exponent:"9"},
	{val:1000000, name:"mega", abbreviation:"M", exponent:"6"},
	{val:1000, name:"kilo", abbreviation:"K", exponent:"3"}
];

function redirectToConnectPageIfNeeded(req, res) {
	if (!req.session.host) {
		req.session.redirectUrl = req.originalUrl;

		res.redirect("/");
		res.end();

		return true;
	}

	return false;
}

function hex2ascii(hex) {
	var str = "";
	for (var i = 0; i < hex.length; i += 2) {
		str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
	}

	return str;
}

function splitArrayIntoChunks(array, chunkSize) {
	var j = array.length;
	var chunks = [];

	for (var i = 0; i < j; i += chunkSize) {
		chunks.push(array.slice(i, i + chunkSize));
	}

	return chunks;
}

function getRandomString(length, chars) {
    var mask = '';

    if (chars.indexOf('a') > -1) {
		mask += 'abcdefghijklmnopqrstuvwxyz';
	}

    if (chars.indexOf('A') > -1) {
		mask += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	}

    if (chars.indexOf('#') > -1) {
		mask += '0123456789';
	}

	if (chars.indexOf('!') > -1) {
		mask += '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\';
	}

    var result = '';
    for (var i = length; i > 0; --i) {
		result += mask[Math.floor(Math.random() * mask.length)];
	}

	return result;
}

var formatCurrencyCache = {};

function formatCurrencyAmountWithForcedDecimalPlaces(amount, formatType, forcedDecimalPlaces) {
	if (formatCurrencyCache[formatType]) {
		var dec = new Decimal(amount);
		dec = dec.times(formatCurrencyCache[formatType].multiplier);

		var decimalPlaces = formatCurrencyCache[formatType].decimalPlaces;
		if (decimalPlaces == 0 && dec < 1) {
			decimalPlaces = 5;
		}

		if (forcedDecimalPlaces >= 0) {
			decimalPlaces = forcedDecimalPlaces;
		}

		return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + formatCurrencyCache[formatType].name;
	}

	for (var x = 0; x < coins[config.coin].currencyUnits.length; x++) {
		var currencyUnit = coins[config.coin].currencyUnits[x];

		for (var y = 0; y < currencyUnit.values.length; y++) {
			var currencyUnitValue = currencyUnit.values[y];

			if (currencyUnitValue == formatType) {
				formatCurrencyCache[formatType] = currencyUnit;

				var dec = new Decimal(amount);
				dec = dec.times(currencyUnit.multiplier);

				var decimalPlaces = currencyUnit.decimalPlaces;
				if (decimalPlaces == 0 && dec < 1) {
					decimalPlaces = 5;
				}

				if (forcedDecimalPlaces >= 0) {
					decimalPlaces = forcedDecimalPlaces;
				}

				return addThousandsSeparators(dec.toDecimalPlaces(decimalPlaces)) + " " + currencyUnit.name;
			}
		}
	}

	return amount;
}

function formatCurrencyAmount(amount, formatType) {
	return formatCurrencyAmountWithForcedDecimalPlaces(amount, formatType, -1);
}

function formatCurrencyAmountInSmallestUnits(amount, forcedDecimalPlaces) {
	return formatCurrencyAmountWithForcedDecimalPlaces(amount, coins[config.coin].currencyUnits[coins[config.coin].currencyUnits.length - 1].name, forcedDecimalPlaces);
}

// ref: https://stackoverflow.com/a/2901298/673828
function addThousandsSeparators(x) {
	var parts = x.toString().split(".");
	parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");

	return parts.join(".");
}

function formatExchangedCurrency(amount) {
	if (global.exchangeRate != null) {
		var dec = new Decimal(amount);
		dec = dec.times(global.exchangeRate);

		return addThousandsSeparators(dec.toDecimalPlaces(2)) + " " + coins[config.coin].exchangeRateData.exchangedCurrencyName;
	}

	return "";
}

function seededRandom(seed) {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
}

function seededRandomIntBetween(seed, min, max) {
	var rand = seededRandom(seed);
	return (min + (max - min) * rand);
}

function logMemoryUsage() {
	var mbUsed = process.memoryUsage().heapUsed / 1024 / 1024;
	mbUsed = Math.round(mbUsed * 100) / 100;

	var mbTotal = process.memoryUsage().heapTotal / 1024 / 1024;
	mbTotal = Math.round(mbTotal * 100) / 100;

	//console.log("memoryUsage: heapUsed=" + mbUsed + ", heapTotal=" + mbTotal + ", ratio=" + parseInt(mbUsed / mbTotal * 100));
}

function getMinerFromCoinbaseTx(tx) {
	if (tx == null || tx.vin == null || tx.vin.length == 0) {
		return null;
	}

	if (global.miningPoolsConfigs) {
		for (var i = 0; i < global.miningPoolsConfigs.length; i++) {
			var miningPoolsConfig = global.miningPoolsConfigs[i];

			for (var payoutAddress in miningPoolsConfig.payout_addresses) {
				if (miningPoolsConfig.payout_addresses.hasOwnProperty(payoutAddress)) {
					if (tx.vout && tx.vout.length > 0 && tx.vout[0].scriptPubKey && tx.vout[0].scriptPubKey.addresses && tx.vout[0].scriptPubKey.addresses.length > 0) {
						if (tx.vout[0].scriptPubKey.addresses[0] == payoutAddress) {
							var minerInfo = miningPoolsConfig.payout_addresses[payoutAddress];
							minerInfo.identifiedBy = "payout address " + payoutAddress;

							return minerInfo;
						}
					}
				}
			}

			for (var coinbaseTag in miningPoolsConfig.coinbase_tags) {
				if (miningPoolsConfig.coinbase_tags.hasOwnProperty(coinbaseTag)) {
					if (hex2ascii(tx.vin[0].coinbase).indexOf(coinbaseTag) != -1) {
						var minerInfo = miningPoolsConfig.coinbase_tags[coinbaseTag];
						minerInfo.identifiedBy = "coinbase tag '" + coinbaseTag + "'";

						return minerInfo;
					}
				}
			}
		}
	}

	return null;
}

function getTxTotalInputOutputValues(tx, txInputs, blockHeight) {
	var totalInputValue = new Decimal(0);
	var totalOutputValue = new Decimal(0);

	try {
		for (var i = 0; i < tx.vin.length; i++) {
			if (tx.vin[i].coinbase) {
				totalInputValue = totalInputValue.plus(new Decimal(coinConfig.blockRewardFunction(blockHeight)));

			} else {
				var txInput = txInputs[i];

				if (txInput) {
					try {
						var vout = txInput.vout[tx.vin[i].vout];
						if (vout.value) {
							totalInputValue = totalInputValue.plus(new Decimal(vout.value));
						}
					} catch (err) {
						console.log("Error getting tx.totalInputValue: err=" + err + ", txid=" + tx.txid + ", index=tx.vin[" + i + "]");
					}
				}
			}
		}

		for (var i = 0; i < tx.vout.length; i++) {
			totalOutputValue = totalOutputValue.plus(new Decimal(tx.vout[i].value));
		}
	} catch (err) {
		console.log("Error computing total input/output values for tx: err=" + err + ", tx=" + JSON.stringify(tx) + ", txInputs=" + JSON.stringify(txInputs) + ", blockHeight=" + blockHeight);
	}

	return {input:totalInputValue, output:totalOutputValue};
}

function getBlockTotalFeesFromCoinbaseTxAndBlockHeight(coinbaseTx, blockHeight) {
	if (coinbaseTx == null) {
		return 0;
	}

	var blockReward = coinConfig.blockRewardFunction(blockHeight);

	var totalOutput = new Decimal(0);
	for (var i = 0; i < coinbaseTx.vout.length; i++) {
		var outputValue = coinbaseTx.vout[i].value;
		if (outputValue > 0) {
			totalOutput = totalOutput.plus(new Decimal(outputValue));
		}
	}

	return totalOutput.minus(new Decimal(blockReward));
}

function refreshExchangeRate() {
	if (coins[config.coin].exchangeRateData) {
		request(coins[config.coin].exchangeRateData.jsonUrl, function(error, response, body) {
			if (!error && response && response.statusCode && response.statusCode == 200) {
				var responseBody = JSON.parse(body);

				var exchangeRate = coins[config.coin].exchangeRateData.responseBodySelectorFunction(responseBody);
				if (exchangeRate > 0) {
					global.exchangeRate = exchangeRate;
					global.exchangeRateUpdateTime = new Date();

					if (global.influxdb) {
						global.influxdb.writePoints([{
							measurement: "exchange_rates.btc_usd",
							fields:{value:parseFloat(exchangeRate)}

						}]).catch(err => {
							console.error(`Error saving data to InfluxDB: ${err.stack}`)
						});
					}

					console.log("Using exchange rate: " + global.exchangeRate + " USD/" + coins[config.coin].name + " starting at " + global.exchangeRateUpdateTime);

				} else {
					console.log("Unable to get exchange rate data");
				}
			} else {
				console.log("Error:");
				console.log(error);
				console.log("Response:");
				console.log(response);
			}
		});
	}
}

// Uses IPStack.com API
function geoLocateIpAddresses(ipAddresses) {
	return new Promise(function(resolve, reject) {
		var chunks = splitArrayIntoChunks(ipAddresses, 1);

		var promises = [];
		for (var i = 0; i < chunks.length; i++) {
			var ipStr = "";
			for (var j = 0; j < chunks[i].length; j++) {
				if (j > 0) {
					ipStr = ipStr + ",";
				}

				ipStr = ipStr + chunks[i][j];
			}

			if (ipCache[ipStr] != null) {
				promises.push(new Promise(function(resolve2, reject2) {
					resolve2(ipCache[ipStr]);
				}));

			} else if (config.credentials.ipStackComApiAccessKey && config.credentials.ipStackComApiAccessKey.trim().length > 0) {
				var apiUrl = "http://api.ipstack.com/" + ipStr + "?access_key=" + config.credentials.ipStackComApiAccessKey;
				promises.push(new Promise(function(resolve2, reject2) {
					request(apiUrl, function(error, response, body) {
						if (error) {
							reject2(error);

						} else {
							resolve2(response);
						}
					});
				}));
			} else {
				promises.push(new Promise(function(resolve2, reject2) {
					resolve2(null);
				}));
			}
		}

		Promise.all(promises).then(function(results) {
			var ipDetails = {ips:[], detailsByIp:{}};

			for (var i = 0; i < results.length; i++) {
				var res = results[i];
				if (res != null && res["statusCode"] == 200) {
					var resBody = JSON.parse(res["body"]);
					var ip = resBody["ip"];

					ipDetails.ips.push(ip);
					ipDetails.detailsByIp[ip] = resBody;

					if (ipCache[ip] == null) {
						ipCache[ip] = res;
					}
				}
			}

			resolve(ipDetails);
		});
	});
}

function parseExponentStringDouble(val) {
	var [lead,decimal,pow] = val.toString().split(/e|\./);
	return +pow <= 0
		? "0." + "0".repeat(Math.abs(pow)-1) + lead + decimal
		: lead + ( +pow >= decimal.length ? (decimal + "0".repeat(+pow-decimal.length)) : (decimal.slice(0,+pow)+"."+decimal.slice(+pow)));
}

function formatLargeNumber(n, decimalPlaces) {
	for (var i = 0; i < exponentScales.length; i++) {
		var item = exponentScales[i];

		var fraction = new Decimal(n / item.val);
		if (fraction >= 1) {
			return [fraction.toDecimalPlaces(decimalPlaces), item];
		}
	}

	return [new Decimal(n).toDecimalPlaces(decimalPlaces), {}];
}

module.exports = {
	redirectToConnectPageIfNeeded: redirectToConnectPageIfNeeded,
	hex2ascii: hex2ascii,
	splitArrayIntoChunks: splitArrayIntoChunks,
	getRandomString: getRandomString,
	formatCurrencyAmount: formatCurrencyAmount,
	formatCurrencyAmountWithForcedDecimalPlaces: formatCurrencyAmountWithForcedDecimalPlaces,
	formatExchangedCurrency: formatExchangedCurrency,
	addThousandsSeparators: addThousandsSeparators,
	formatCurrencyAmountInSmallestUnits: formatCurrencyAmountInSmallestUnits,
	seededRandom: seededRandom,
	seededRandomIntBetween: seededRandomIntBetween,
	logMemoryUsage: logMemoryUsage,
	getMinerFromCoinbaseTx: getMinerFromCoinbaseTx,
	getBlockTotalFeesFromCoinbaseTxAndBlockHeight: getBlockTotalFeesFromCoinbaseTxAndBlockHeight,
	refreshExchangeRate: refreshExchangeRate,
	parseExponentStringDouble: parseExponentStringDouble,
	formatLargeNumber: formatLargeNumber,
	geoLocateIpAddresses: geoLocateIpAddresses,
	getTxTotalInputOutputValues: getTxTotalInputOutputValues
};
<|endoftext|>


<commit_before>import Immutable from 'immutable'
import React, { PropTypes } from 'react'
import EmbedRegion from '../regions/EmbedRegion'
import ImageRegion from '../regions/ImageRegion'
import TextRegion from '../regions/TextRegion'
import { isIOS } from '../../lib/jello'

export function RegionItems(props) {
  const { columnWidth, commentOffset, content, contentWidth,
    detailPath, innerHeight, isComment, isGridMode, isPostDetail } = props
  // sometimes the content is null/undefined for some reason
  if (!content) { return null }
  const cells = []
  content.forEach((region) => {
    switch (region.get('kind')) {
      case 'text':
        cells.push(
          <TextRegion
            content={region.get('data')}
            detailPath={detailPath}
            isComment={isComment}
            isGridMode={isGridMode}
            key={`TextRegion_${region.get('data')}`}
          />,
        )
        break
      case 'image': {
        const asset = region.get('asset')
        cells.push(
          <ImageRegion
            asset={asset}
            buyLinkURL={region.get('linkUrl')}
            columnWidth={columnWidth}
            commentOffset={commentOffset}
            content={region.get('data')}
            contentWidth={contentWidth}
            detailPath={detailPath}
            innerHeight={innerHeight}
            isComment={isComment}
            isGridMode={isGridMode}
            key={`ImageRegion_${JSON.stringify(region.get('data'))}`}
            shouldUseVideo={!!(asset && asset.getIn(['attachment', 'video'], Immutable.Map()).size) && !isIOS() && !isPostDetail}
          />,
        )
        break
      }
      case 'embed':
        cells.push(
          <EmbedRegion
            detailPath={detailPath}
            isComment={isComment}
            key={`EmbedRegion_${JSON.stringify(region.get('data'))}`}
            region={region}
          />,
        )
        break
      default:
        break
    }
  })
  // loop through cells to grab out image/text
  return
{cells}

}
RegionItems.propTypes = {
  columnWidth: PropTypes.number.isRequired,
  commentOffset: PropTypes.number.isRequired,
  content: PropTypes.object.isRequired,
  contentWidth: PropTypes.number.isRequired,
  detailPath: PropTypes.string.isRequired,
  innerHeight: PropTypes.number.isRequired,
  isComment: PropTypes.bool,
  isGridMode: PropTypes.bool.isRequired,
  isPostDetail: PropTypes.bool.isRequired,
}
RegionItems.defaultProps = {
  isComment: false,
}

export function regionItemsForNotifications(content, detailPath) {
  const imageAssets = []
  const texts = []
  content.forEach((region) => {
    switch (region.get('kind')) {
      case 'text':
        texts.push(
          <TextRegion
            content={region.get('data')}
            detailPath={detailPath}
            isGridMode={false}
            key={`TextRegion_${region.get('data')}`}
          />,
        )
        break
      case 'image':
        imageAssets.push(
          <ImageRegion
            asset={region.get('asset')}
            buyLinkURL={region.get('linkUrl')}
            content={region.get('data')}
            detailPath={detailPath}
            isGridMode
            isNotification
            key={`ImageRegion_${JSON.stringify(region.get('data'))}`}
            links={region.get('links')}
          />,
        )
        break
      case 'embed':
        imageAssets.push(
          <EmbedRegion
            detailPath={detailPath}
            key={`EmbedRegion_${JSON.stringify(region.get('data'))}`}
            region={region}
          />,
        )
        break
      case 'rule':
        texts.push(<hr className="NotificationRule" key={`NotificationRule_${detailPath}`} />)
        break
      default:
        break
    }
  })
  return { assets: imageAssets, texts }
}

<commit_msg>Also add shouldUseVideo to notifications<commit_after>import Immutable from 'immutable'
import React, { PropTypes } from 'react'
import EmbedRegion from '../regions/EmbedRegion'
import ImageRegion from '../regions/ImageRegion'
import TextRegion from '../regions/TextRegion'
import { isIOS } from '../../lib/jello'

export function RegionItems(props) {
  const { columnWidth, commentOffset, content, contentWidth,
    detailPath, innerHeight, isComment, isGridMode, isPostDetail } = props
  // sometimes the content is null/undefined for some reason
  if (!content) { return null }
  const cells = []
  content.forEach((region) => {
    switch (region.get('kind')) {
      case 'text':
        cells.push(
          <TextRegion
            content={region.get('data')}
            detailPath={detailPath}
            isComment={isComment}
            isGridMode={isGridMode}
            key={`TextRegion_${region.get('data')}`}
          />,
        )
        break
      case 'image': {
        const asset = region.get('asset')
        cells.push(
          <ImageRegion
            asset={asset}
            buyLinkURL={region.get('linkUrl')}
            columnWidth={columnWidth}
            commentOffset={commentOffset}
            content={region.get('data')}
            contentWidth={contentWidth}
            detailPath={detailPath}
            innerHeight={innerHeight}
            isComment={isComment}
            isGridMode={isGridMode}
            key={`ImageRegion_${JSON.stringify(region.get('data'))}`}
            shouldUseVideo={!!(asset && asset.getIn(['attachment', 'video'], Immutable.Map()).size) && !isIOS() && !isPostDetail}
          />,
        )
        break
      }
      case 'embed':
        cells.push(
          <EmbedRegion
            detailPath={detailPath}
            isComment={isComment}
            key={`EmbedRegion_${JSON.stringify(region.get('data'))}`}
            region={region}
          />,
        )
        break
      default:
        break
    }
  })
  // loop through cells to grab out image/text
  return
{cells}

}
RegionItems.propTypes = {
  columnWidth: PropTypes.number.isRequired,
  commentOffset: PropTypes.number.isRequired,
  content: PropTypes.object.isRequired,
  contentWidth: PropTypes.number.isRequired,
  detailPath: PropTypes.string.isRequired,
  innerHeight: PropTypes.number.isRequired,
  isComment: PropTypes.bool,
  isGridMode: PropTypes.bool.isRequired,
  isPostDetail: PropTypes.bool.isRequired,
}
RegionItems.defaultProps = {
  isComment: false,
}

export function regionItemsForNotifications(content, detailPath) {
  const imageAssets = []
  const texts = []
  content.forEach((region) => {
    switch (region.get('kind')) {
      case 'text':
        texts.push(
          <TextRegion
            content={region.get('data')}
            detailPath={detailPath}
            isGridMode={false}
            key={`TextRegion_${region.get('data')}`}
          />,
        )
        break
      case 'image': {
        const asset = region.get('asset')
        imageAssets.push(
          <ImageRegion
            asset={asset}
            buyLinkURL={region.get('linkUrl')}
            content={region.get('data')}
            detailPath={detailPath}
            isGridMode
            isNotification
            key={`ImageRegion_${JSON.stringify(region.get('data'))}`}
            links={region.get('links')}
            shouldUseVideo={!!(asset && asset.getIn(['attachment', 'video'], Immutable.Map()).size) && !isIOS()}
          />,
        )
        break
      }
      case 'embed':
        imageAssets.push(
          <EmbedRegion
            detailPath={detailPath}
            key={`EmbedRegion_${JSON.stringify(region.get('data'))}`}
            region={region}
          />,
        )
        break
      case 'rule':
        texts.push(<hr className="NotificationRule" key={`NotificationRule_${detailPath}`} />)
        break
      default:
        break
    }
  })
  return { assets: imageAssets, texts }
}

<|endoftext|>


<commit_before>/* ex: set tabstop=4 expandtab: */
var path = require('path');

var consoleArgs = process.argv.slice(2);
var supportedArgs = [
    'debug', '-d',
    'verbose', '-v',
    'post', '-p'
];
var isDebug = exports.isDebug = hasConsoleArgs(supportedArgs.slice(0,2));
var isVerbose = exports.isVerbose = hasConsoleArgs(supportedArgs.slice(2,4));
var postProcessor = exports.postProcessor = getConsoleArgs(supportedArgs.slice(4,6));

var srcDir = exports.srcDir = path.join(__dirname, 'src'); // Template sources
var releaseDir = exports.releaseDir = path.join(__dirname, 'release'); // Website release
var contentDir = exports.contentDir = path.join(__dirname, 'content'); // Markdown content

var mapping = exports.mapping = {
    'team': '/team'
};

function getConsoleArgs(args){
    // Summary:
    //      Returns the property of a console arg.
    for (var i=0, l=args.length; i<l; i++){
        var index = consoleArgs.indexOf(args[i]);
        if (index!=-1){
            return supportedArgs.indexOf(consoleArgs[index+1]) > -1 ? false : consoleArgs[index+1];
        }
    }
    return false;
}

function hasConsoleArgs(args){
    // Summary:
    //      Returns a boolean for whether an argument got passed.
    for (var i=0, l=args.length; i<l; i++){
        if (consoleArgs.indexOf(args[i])!=-1){
            return true;
        }
    }
    return false;
}

exports.printValues = function(){
    console.log('');
    console.log('  Debug mode (-d)\t\t' + isDebug);
    console.log('  Verbose mode (-v)\t\t' + isVerbose);
    console.log('  Post processor (-p) filename\t\t' + postProcessor);
    console.log('');
}
<commit_msg>Fixed typo<commit_after>/* ex: set tabstop=4 expandtab: */
var path = require('path');

var consoleArgs = process.argv.slice(2);
var supportedArgs = [
    'debug', '-d',
    'verbose', '-v',
    'post', '-p'
];
var isDebug = exports.isDebug = hasConsoleArgs(supportedArgs.slice(0,2));
var isVerbose = exports.isVerbose = hasConsoleArgs(supportedArgs.slice(2,4));
var postProcessor = exports.postProcessor = getConsoleArgs(supportedArgs.slice(4,6));

var srcDir = exports.srcDir = path.join(__dirname, 'src'); // Template sources
var releaseDir = exports.releaseDir = path.join(__dirname, 'release'); // Website release
var contentDir = exports.contentDir = path.join(__dirname, 'content'); // Markdown content

var mapping = exports.mapping = {
    'team': '/team'
};

function getConsoleArgs(args){
    // Summary:
    //      Returns the property of a console arg.
    for (var i=0, l=args.length; i<l; i++){
        var index = consoleArgs.indexOf(args[i]);
        if (index!=-1){
            return supportedArgs.indexOf(consoleArgs[index+1]) > -1 ? false : consoleArgs[index+1];
        }
    }
    return false;
}

function hasConsoleArgs(args){
    // Summary:
    //      Returns a boolean for whether an argument got passed.
    for (var i=0, l=args.length; i<l; i++){
        if (consoleArgs.indexOf(args[i])!=-1){
            return true;
        }
    }
    return false;
}

exports.printValues = function(){
    console.log('');
    console.log('  Debug mode (-d)\t\t' + isDebug);
    console.log('  Verbose mode (-v)\t\t' + isVerbose);
    console.log('  Post processor (-p filename) \t' + postProcessor);
    console.log('');
}
<|endoftext|>