<commit_before>//WARNING: no log() function usage before init.js

exports.core.os = navigator.platform
exports.core.device = 0
exports.core.vendor = ""

exports.trace = { key: false, focus: false }

exports.core.keyCodes = {
	13: 'Select',
	27: 'Back',
	37: 'Left',
	32: 'Space',
	33: 'PageUp',
	34: 'PageDown',
	38: 'Up',
	39: 'Right',
	40: 'Down',
	48: '0',
	49: '1',
	50: '2',
	51: '3',
	52: '4',
	53: '5',
	54: '6',
	55: '7',
	56: '8',
	57: '9',
	112: 'Red',
	113: 'Green',
	114: 'Yellow',
	115: 'Blue'
}

var copyArguments = function(args, src, prefix) {
	var copy = Array.prototype.slice.call(args, src)
	if (prefix !== undefined)
		copy.unshift(prefix)
	return copy
}

exports.core.copyArguments = copyArguments

/* ${init.js} */

if (log === null)
	log = console.log.bind(console)

var _checkDevice = function(target, info) {
	if (navigator.userAgent.indexOf(target) < 0)
		return

	log("[QML] " + target)
	exports.core.vendor = info.vendor
	exports.core.device = info.device
	exports.core.os = info.os
	log("loaded")
}

_checkDevice('Blackberry', { 'vendor': 'blackberry', 'device': 2, 'os': 'blackberry' })
_checkDevice('Android', { 'vendor': 'google', 'device': 2, 'os': 'android' })
_checkDevice('iPhone', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })
_checkDevice('iPad', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })
_checkDevice('iPod', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })

var colorTable = {
	'maroon':	'800000',
	'red':		'ff0000',
	'orange':	'ffA500',
	'yellow':	'ffff00',
	'olive':	'808000',
	'purple':	'800080',
	'fuchsia':	'ff00ff',
	'white':	'ffffff',
	'lime':		'00ff00',
	'green':	'008000',
	'navy':		'000080',
	'blue':		'0000ff',
	'aqua':		'00ffff',
	'teal':		'008080',
	'black':	'000000',
	'silver':	'c0c0c0',
	'gray':		'080808',
	'transparent': '0000'
}

var safeCallImpl = function(callback, args, onError) {
	try { return callback.apply(null, args) } catch(ex) { onError(ex) }
}

exports.core.safeCall = function(args, onError) {
	return function(callback) { return safeCallImpl(callback, args, onError) }
}

/**
 * @constructor
 */
exports.core.CoreObject = function() { }
exports.core.CoreObject.prototype.constructor = exports.core.CoreObject

/** @constructor */
exports.core.Color = function(value) {
	if (typeof value !== 'string')
	{
		this.r = 255
		this.g = 0
		this.b = 255
		log("invalid color specification: " + value)
		return
	}
	var triplet
	if (value.substring(0, 4) == "rgba") {
		var b = value.indexOf('('), e = value.lastIndexOf(')')
		value = value.substring(b + 1, e).split(',')
		this.r = parseInt(value[0], 10)
		this.g = parseInt(value[1], 10)
		this.b = parseInt(value[2], 10)
		this.a = parseInt(value[3], 10)
		return
	}
	else {
		var h = value.charAt(0);
		if (h != '#')
			triplet = colorTable[value];
		else
			triplet = value.substring(1)
	}
	if (!triplet) {
		this.r = 255
		this.g = 0
		this.b = 255
		log("invalid color specification: " + value)
		return
	}

	var len = triplet.length;
	if (len == 3 || len == 4) {
		var r = parseInt(triplet.charAt(0), 16)
		var g = parseInt(triplet.charAt(1), 16)
		var b = parseInt(triplet.charAt(2), 16)
		var a = (len == 4)? parseInt(triplet.charAt(3), 16): 15
		this.r = (r << 4) | r;
		this.g = (g << 4) | g;
		this.b = (b << 4) | b;
		this.a = (a << 4) | a;
	} else if (len == 6 || len == 8) {
		this.r = parseInt(triplet.substring(0, 2), 16)
		this.g = parseInt(triplet.substring(2, 4), 16)
		this.b = parseInt(triplet.substring(4, 6), 16)
		this.a = (len == 8)? parseInt(triplet.substring(6, 8), 16): 255
	} else
		throw new Error("invalid color specification: " + value)
}
exports.core.Color.prototype.constructor = exports.core.Color
/** @const */
var Color = exports.core.Color

exports.core.Color.prototype.get = function() {
	return "rgba(" + this.r + "," + this.g + "," + this.b + "," + (this.a / 255) + ")";
}

exports.core.normalizeColor = function(spec) {
	return (new Color(spec)).get()
}

/** @constructor */
exports.core.DelayedAction = function(context, action) {
	this.context = context
	this.action = function() {
		this._scheduled = false
		action()
	}.bind(this)
}

exports.core.DelayedAction.prototype.schedule = function() {
	if (!this._scheduled) {
		this._scheduled = true
		this.context.scheduleAction(this.action)
	}
}

var Modernizr = window.Modernizr
var requestAnimationFrame = Modernizr.prefixed('requestAnimationFrame', window)	|| function(callback) { return setTimeout(callback, 0) }
var cancelAnimationFrame = Modernizr.prefixed('cancelAnimationFrame', window)	|| function(id) { return clearTimeout(id) }

exports.addProperty = function(proto, type, name, defaultValue) {
	var convert
	switch(type) {
		case 'enum':
		case 'int':		convert = function(value) { return parseInt(value, 0) }; break
		case 'bool':	convert = function(value) { return value? true: false }; break
		case 'real':	convert = function(value) { return parseFloat(value) }; break
		case 'string':	convert = function(value) { return String(value) }; break
		default:		convert = function(value) { return value }; break
	}

	if (defaultValue !== undefined) {
		defaultValue = convert(defaultValue)
	} else {
		switch(type) {
			case 'enum': //fixme: add default value here
			case 'int':		defaultValue = 0; break
			case 'bool':	defaultValue = false; break
			case 'real':	defaultValue = 0.0; break
			case 'string':	defaultValue = ""; break
			case 'array':	defaultValue = []; break
			default:
				defaultValue = (type[0].toUpperCase() == type[0])? null: undefined
		}
	}

	var storageName = '__property_' + name

	Object.defineProperty(proto, name, {
		get: function() {
			var p = this[storageName]
			return p !== undefined?
				p.interpolatedValue !== undefined? p.interpolatedValue: p.value:
				defaultValue
		},

		set: function(newValue) {
			newValue = convert(newValue)
			var p = this[storageName]
			if (p === undefined) { //no storage
				if (newValue === defaultValue) //value == defaultValue, no storage allocation
					return

				p = this[storageName] = { value : defaultValue }
			}
			var animation = this.getAnimation(name)
			if (animation && p.value !== newValue) {
				if (p.frameRequest)
					cancelAnimationFrame(p.frameRequest)

				var now = new Date()
				p.started = now.getTime() + now.getMilliseconds() / 1000.0

				var src = p.interpolatedValue !== undefined? p.interpolatedValue: p.value
				var dst = newValue

				var self = this

				var complete = function() {
					cancelAnimationFrame(p.frameRequest)
					p.frameRequest = undefined
					animation.complete = function() { }
					animation.running = false
					p.interpolatedValue = undefined
					p.started = undefined
					self._update(name, dst, src)
				}

				var duration = animation.duration

				var nextFrame = function() {
					var date = new Date()
					var now = date.getTime() + date.getMilliseconds() / 1000.0
					var t = 1.0 * (now - p.started) / duration
					if (t >= 1) {
						complete()
					} else {
						p.interpolatedValue = convert(animation.interpolate(dst, src, t))
						self._update(name, p.interpolatedValue, src)
						p.frameRequest = requestAnimationFrame(nextFrame)
					}
				}

				p.frameRequest = requestAnimationFrame(nextFrame)

				animation.running = true
				animation.complete = complete
			}
			var oldValue = p.value
			if (oldValue !== newValue) {
				p.value = newValue
				if (!animation)
					this._update(name, newValue, oldValue)
			}
		},
		enumerable: true
	})
}

exports.addAliasProperty = function(self, name, getObject, srcProperty) {
	var target = getObject()
	target.onChanged(srcProperty, function(value) { self._update(name, value) })

	Object.defineProperty(self, name, {
		get: function() { return target[srcProperty] },
		set: function(value) { target[srcProperty] = value },
		enumerable: true
	})
}

exports.addSignal = function(self, name) {
	self[name] = (function() {
		var args = copyArguments(arguments, 0, name)
		self.emit.apply(self, args)
	})
}

exports.core.createSignalForwarder = function(object, name) {
	return (function() { object.emit.apply(object, copyArguments(arguments, 0, name)) })
}

/** @constructor */
exports.core.EventBinder = function(target) {
	this.target = target
	this.callbacks = {}
	this.enabled = false
}

exports.core.EventBinder.prototype.on = function(event, callback) {
	if (event in this.callbacks)
		throw new Error('double adding of event (' + event + ')')
	this.callbacks[event] = callback
	if (this.enabled)
		this.target.on(event, callback)
}

exports.core.EventBinder.prototype.constructor = exports.core.EventBinder

exports.core.EventBinder.prototype.enable = function(value) {
	if (value != this.enabled) {
		var target = this.target
		this.enabled = value
		if (value) {
			for(var event in this.callbacks)
				target.on(event, this.callbacks[event])
		} else {
			for(var event in this.callbacks)
				target.removeListener(event, this.callbacks[event])
		}
	}
}
<commit_msg>register alias update connection in parent object<commit_after>//WARNING: no log() function usage before init.js

exports.core.os = navigator.platform
exports.core.device = 0
exports.core.vendor = ""

exports.trace = { key: false, focus: false }

exports.core.keyCodes = {
	13: 'Select',
	27: 'Back',
	37: 'Left',
	32: 'Space',
	33: 'PageUp',
	34: 'PageDown',
	38: 'Up',
	39: 'Right',
	40: 'Down',
	48: '0',
	49: '1',
	50: '2',
	51: '3',
	52: '4',
	53: '5',
	54: '6',
	55: '7',
	56: '8',
	57: '9',
	112: 'Red',
	113: 'Green',
	114: 'Yellow',
	115: 'Blue'
}

var copyArguments = function(args, src, prefix) {
	var copy = Array.prototype.slice.call(args, src)
	if (prefix !== undefined)
		copy.unshift(prefix)
	return copy
}

exports.core.copyArguments = copyArguments

/* ${init.js} */

if (log === null)
	log = console.log.bind(console)

var _checkDevice = function(target, info) {
	if (navigator.userAgent.indexOf(target) < 0)
		return

	log("[QML] " + target)
	exports.core.vendor = info.vendor
	exports.core.device = info.device
	exports.core.os = info.os
	log("loaded")
}

_checkDevice('Blackberry', { 'vendor': 'blackberry', 'device': 2, 'os': 'blackberry' })
_checkDevice('Android', { 'vendor': 'google', 'device': 2, 'os': 'android' })
_checkDevice('iPhone', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })
_checkDevice('iPad', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })
_checkDevice('iPod', { 'vendor': 'apple', 'device': 2, 'os': 'iOS' })

var colorTable = {
	'maroon':	'800000',
	'red':		'ff0000',
	'orange':	'ffA500',
	'yellow':	'ffff00',
	'olive':	'808000',
	'purple':	'800080',
	'fuchsia':	'ff00ff',
	'white':	'ffffff',
	'lime':		'00ff00',
	'green':	'008000',
	'navy':		'000080',
	'blue':		'0000ff',
	'aqua':		'00ffff',
	'teal':		'008080',
	'black':	'000000',
	'silver':	'c0c0c0',
	'gray':		'080808',
	'transparent': '0000'
}

var safeCallImpl = function(callback, args, onError) {
	try { return callback.apply(null, args) } catch(ex) { onError(ex) }
}

exports.core.safeCall = function(args, onError) {
	return function(callback) { return safeCallImpl(callback, args, onError) }
}

/**
 * @constructor
 */
exports.core.CoreObject = function() { }
exports.core.CoreObject.prototype.constructor = exports.core.CoreObject

/** @constructor */
exports.core.Color = function(value) {
	if (typeof value !== 'string')
	{
		this.r = 255
		this.g = 0
		this.b = 255
		log("invalid color specification: " + value)
		return
	}
	var triplet
	if (value.substring(0, 4) == "rgba") {
		var b = value.indexOf('('), e = value.lastIndexOf(')')
		value = value.substring(b + 1, e).split(',')
		this.r = parseInt(value[0], 10)
		this.g = parseInt(value[1], 10)
		this.b = parseInt(value[2], 10)
		this.a = parseInt(value[3], 10)
		return
	}
	else {
		var h = value.charAt(0);
		if (h != '#')
			triplet = colorTable[value];
		else
			triplet = value.substring(1)
	}
	if (!triplet) {
		this.r = 255
		this.g = 0
		this.b = 255
		log("invalid color specification: " + value)
		return
	}

	var len = triplet.length;
	if (len == 3 || len == 4) {
		var r = parseInt(triplet.charAt(0), 16)
		var g = parseInt(triplet.charAt(1), 16)
		var b = parseInt(triplet.charAt(2), 16)
		var a = (len == 4)? parseInt(triplet.charAt(3), 16): 15
		this.r = (r << 4) | r;
		this.g = (g << 4) | g;
		this.b = (b << 4) | b;
		this.a = (a << 4) | a;
	} else if (len == 6 || len == 8) {
		this.r = parseInt(triplet.substring(0, 2), 16)
		this.g = parseInt(triplet.substring(2, 4), 16)
		this.b = parseInt(triplet.substring(4, 6), 16)
		this.a = (len == 8)? parseInt(triplet.substring(6, 8), 16): 255
	} else
		throw new Error("invalid color specification: " + value)
}
exports.core.Color.prototype.constructor = exports.core.Color
/** @const */
var Color = exports.core.Color

exports.core.Color.prototype.get = function() {
	return "rgba(" + this.r + "," + this.g + "," + this.b + "," + (this.a / 255) + ")";
}

exports.core.normalizeColor = function(spec) {
	return (new Color(spec)).get()
}

/** @constructor */
exports.core.DelayedAction = function(context, action) {
	this.context = context
	this.action = function() {
		this._scheduled = false
		action()
	}.bind(this)
}

exports.core.DelayedAction.prototype.schedule = function() {
	if (!this._scheduled) {
		this._scheduled = true
		this.context.scheduleAction(this.action)
	}
}

var Modernizr = window.Modernizr
var requestAnimationFrame = Modernizr.prefixed('requestAnimationFrame', window)	|| function(callback) { return setTimeout(callback, 0) }
var cancelAnimationFrame = Modernizr.prefixed('cancelAnimationFrame', window)	|| function(id) { return clearTimeout(id) }

exports.addProperty = function(proto, type, name, defaultValue) {
	var convert
	switch(type) {
		case 'enum':
		case 'int':		convert = function(value) { return parseInt(value, 0) }; break
		case 'bool':	convert = function(value) { return value? true: false }; break
		case 'real':	convert = function(value) { return parseFloat(value) }; break
		case 'string':	convert = function(value) { return String(value) }; break
		default:		convert = function(value) { return value }; break
	}

	if (defaultValue !== undefined) {
		defaultValue = convert(defaultValue)
	} else {
		switch(type) {
			case 'enum': //fixme: add default value here
			case 'int':		defaultValue = 0; break
			case 'bool':	defaultValue = false; break
			case 'real':	defaultValue = 0.0; break
			case 'string':	defaultValue = ""; break
			case 'array':	defaultValue = []; break
			default:
				defaultValue = (type[0].toUpperCase() == type[0])? null: undefined
		}
	}

	var storageName = '__property_' + name

	Object.defineProperty(proto, name, {
		get: function() {
			var p = this[storageName]
			return p !== undefined?
				p.interpolatedValue !== undefined? p.interpolatedValue: p.value:
				defaultValue
		},

		set: function(newValue) {
			newValue = convert(newValue)
			var p = this[storageName]
			if (p === undefined) { //no storage
				if (newValue === defaultValue) //value == defaultValue, no storage allocation
					return

				p = this[storageName] = { value : defaultValue }
			}
			var animation = this.getAnimation(name)
			if (animation && p.value !== newValue) {
				if (p.frameRequest)
					cancelAnimationFrame(p.frameRequest)

				var now = new Date()
				p.started = now.getTime() + now.getMilliseconds() / 1000.0

				var src = p.interpolatedValue !== undefined? p.interpolatedValue: p.value
				var dst = newValue

				var self = this

				var complete = function() {
					cancelAnimationFrame(p.frameRequest)
					p.frameRequest = undefined
					animation.complete = function() { }
					animation.running = false
					p.interpolatedValue = undefined
					p.started = undefined
					self._update(name, dst, src)
				}

				var duration = animation.duration

				var nextFrame = function() {
					var date = new Date()
					var now = date.getTime() + date.getMilliseconds() / 1000.0
					var t = 1.0 * (now - p.started) / duration
					if (t >= 1) {
						complete()
					} else {
						p.interpolatedValue = convert(animation.interpolate(dst, src, t))
						self._update(name, p.interpolatedValue, src)
						p.frameRequest = requestAnimationFrame(nextFrame)
					}
				}

				p.frameRequest = requestAnimationFrame(nextFrame)

				animation.running = true
				animation.complete = complete
			}
			var oldValue = p.value
			if (oldValue !== newValue) {
				p.value = newValue
				if (!animation)
					this._update(name, newValue, oldValue)
			}
		},
		enumerable: true
	})
}

exports.addAliasProperty = function(self, name, getObject, srcProperty) {
	var target = getObject()
	self.connectOnChanged(target, srcProperty, function(value) { self._update(name, value) })

	Object.defineProperty(self, name, {
		get: function() { return target[srcProperty] },
		set: function(value) { target[srcProperty] = value },
		enumerable: true
	})
}

exports.addSignal = function(self, name) {
	self[name] = (function() {
		var args = copyArguments(arguments, 0, name)
		self.emit.apply(self, args)
	})
}

exports.core.createSignalForwarder = function(object, name) {
	return (function() { object.emit.apply(object, copyArguments(arguments, 0, name)) })
}

/** @constructor */
exports.core.EventBinder = function(target) {
	this.target = target
	this.callbacks = {}
	this.enabled = false
}

exports.core.EventBinder.prototype.on = function(event, callback) {
	if (event in this.callbacks)
		throw new Error('double adding of event (' + event + ')')
	this.callbacks[event] = callback
	if (this.enabled)
		this.target.on(event, callback)
}

exports.core.EventBinder.prototype.constructor = exports.core.EventBinder

exports.core.EventBinder.prototype.enable = function(value) {
	if (value != this.enabled) {
		var target = this.target
		this.enabled = value
		if (value) {
			for(var event in this.callbacks)
				target.on(event, this.callbacks[event])
		} else {
			for(var event in this.callbacks)
				target.removeListener(event, this.callbacks[event])
		}
	}
}
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const Tapable = require("tapable");
const ConcatSource = require("webpack-sources").ConcatSource;

const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0);
const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0);
const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1;

module.exports = class Template extends Tapable {
	constructor(outputOptions) {
		super();
		this.outputOptions = outputOptions || {};
	}

	static getFunctionContent(fn) {
		return fn.toString().replace(/^function\s?\(\)\s?\{\n?|\n?\}$/g, "").replace(/^\t/mg, "");
	}

	static toIdentifier(str) {
		if(typeof str !== "string") return "";
		return str.replace(/^([^a-zA-Z$_])/, "_$1").replace(/[^a-zA-Z0-9$]+/g, "_");
	}

	static toComment(str) {
		if(!str) return "";
		return `/*! ${str.replace(/\*\//g, "* /")} */`;
	}

	static toNormalComment(str) {
		if(!str) return "";
		return `/* ${str.replace(/\*\//g, "* /")} */`;
	}

	static toPath(str) {
		if(typeof str !== "string") return "";
		return str.replace(/[^a-zA-Z0-9_!§$()=\-^°]+/g, "-").replace(/^-|-$/, "");
	}

	// map number to a single character a-z, A-Z or <_ + number> if number is too big
	static numberToIdentifer(n) {
		// lower case
		if(n < DELTA_A_TO_Z) return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n);

		// upper case
		n -= DELTA_A_TO_Z;
		if(n < DELTA_A_TO_Z) return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n);

		// fall back to _ + number
		n -= DELTA_A_TO_Z;
		return "_" + n;
	}

	indent(str) {
		if(Array.isArray(str)) {
			return str.map(this.indent.bind(this)).join("\n");
		} else {
			str = str.trimRight();
			if(!str) return "";
			var ind = (str[0] === "\n" ? "" : "\t");
			return ind + str.replace(/\n([^\n])/g, "\n\t$1");
		}
	}

	prefix(str, prefix) {
		if(Array.isArray(str)) {
			str = str.join("\n");
		}
		str = str.trim();
		if(!str) return "";
		const ind = (str[0] === "\n" ? "" : prefix);
		return ind + str.replace(/\n([^\n])/g, "\n" + prefix + "$1");
	}

	asString(str) {
		if(Array.isArray(str)) {
			return str.join("\n");
		}
		return str;
	}

	getModulesArrayBounds(modules) {
		if(!modules.every(moduleIdIsNumber))
			return false;
		var maxId = -Infinity;
		var minId = Infinity;
		modules.forEach(function(module) {
			if(maxId < module.id) maxId = module.id;
			if(minId > module.id) minId = module.id;
		});
		if(minId < 16 + ("" + minId).length) {
			// add minId x ',' instead of 'Array(minId).concat(...)'
			minId = 0;
		}
		var objectOverhead = modules.map(function(module) {
			var idLength = (module.id + "").length;
			return idLength + 2;
		}).reduce(function(a, b) {
			return a + b;
		}, -1);
		var arrayOverhead = minId === 0 ? maxId : 16 + ("" + minId).length + maxId;
		return arrayOverhead < objectOverhead ? [minId, maxId] : false;
	}

	renderChunkModules(chunk, moduleTemplate, dependencyTemplates, prefix) {
		if(!prefix) prefix = "";
		var source = new ConcatSource();
		if(chunk.getNumberOfModules() === 0) {
			source.add("[]");
			return source;
		}
		var removedModules = chunk.removedModules;
		var allModules = chunk.mapModules(function(module) {
			return {
				id: module.id,
				source: moduleTemplate.render(module, dependencyTemplates, chunk)
			};
		});
		if(removedModules && removedModules.length > 0) {
			removedModules.forEach(function(id) {
				allModules.push({
					id: id,
					source: "false"
				});
			});
		}
		var bounds = this.getModulesArrayBounds(allModules);

		if(bounds) {
			// Render a spare array
			var minId = bounds[0];
			var maxId = bounds[1];
			if(minId !== 0) source.add("Array(" + minId + ").concat(");
			source.add("[\n");
			var modules = {};
			allModules.forEach(function(module) {
				modules[module.id] = module;
			});
			for(var idx = minId; idx <= maxId; idx++) {
				var module = modules[idx];
				if(idx !== minId) source.add(",\n");
				source.add("/* " + idx + " */");
				if(module) {
					source.add("\n");
					source.add(module.source);
				}
			}
			source.add("\n" + prefix + "]");
			if(minId !== 0) source.add(")");
		} else {
			// Render an object
			source.add("{\n");
			allModules.sort(function(a, b) {
				var aId = a.id + "";
				var bId = b.id + "";
				if(aId < bId) return -1;
				if(aId > bId) return 1;
				return 0;
			}).forEach(function(module, idx) {
				if(idx !== 0) source.add(",\n");
				source.add("\n/***/ " + JSON.stringify(module.id) + ":\n");
				source.add(module.source);
			});
			source.add("\n\n" + prefix + "}");
		}
		return source;
	}
};

function moduleIdIsNumber(module) {
	return typeof module.id === "number";
}
<commit_msg>fix Template.getFunctionContent for windows line breaks<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const Tapable = require("tapable");
const ConcatSource = require("webpack-sources").ConcatSource;

const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0);
const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0);
const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1;

module.exports = class Template extends Tapable {
	constructor(outputOptions) {
		super();
		this.outputOptions = outputOptions || {};
	}

	static getFunctionContent(fn) {
		return fn.toString().replace(/^function\s?\(\)\s?\{\r?\n?|\r?\n?\}$/g, "").replace(/^\t/mg, "").replace(/\r?\n/g, "\n");
	}

	static toIdentifier(str) {
		if(typeof str !== "string") return "";
		return str.replace(/^([^a-zA-Z$_])/, "_$1").replace(/[^a-zA-Z0-9$]+/g, "_");
	}

	static toComment(str) {
		if(!str) return "";
		return `/*! ${str.replace(/\*\//g, "* /")} */`;
	}

	static toNormalComment(str) {
		if(!str) return "";
		return `/* ${str.replace(/\*\//g, "* /")} */`;
	}

	static toPath(str) {
		if(typeof str !== "string") return "";
		return str.replace(/[^a-zA-Z0-9_!§$()=\-^°]+/g, "-").replace(/^-|-$/, "");
	}

	// map number to a single character a-z, A-Z or <_ + number> if number is too big
	static numberToIdentifer(n) {
		// lower case
		if(n < DELTA_A_TO_Z) return String.fromCharCode(START_LOWERCASE_ALPHABET_CODE + n);

		// upper case
		n -= DELTA_A_TO_Z;
		if(n < DELTA_A_TO_Z) return String.fromCharCode(START_UPPERCASE_ALPHABET_CODE + n);

		// fall back to _ + number
		n -= DELTA_A_TO_Z;
		return "_" + n;
	}

	indent(str) {
		if(Array.isArray(str)) {
			return str.map(this.indent.bind(this)).join("\n");
		} else {
			str = str.trimRight();
			if(!str) return "";
			var ind = (str[0] === "\n" ? "" : "\t");
			return ind + str.replace(/\n([^\n])/g, "\n\t$1");
		}
	}

	prefix(str, prefix) {
		if(Array.isArray(str)) {
			str = str.join("\n");
		}
		str = str.trim();
		if(!str) return "";
		const ind = (str[0] === "\n" ? "" : prefix);
		return ind + str.replace(/\n([^\n])/g, "\n" + prefix + "$1");
	}

	asString(str) {
		if(Array.isArray(str)) {
			return str.join("\n");
		}
		return str;
	}

	getModulesArrayBounds(modules) {
		if(!modules.every(moduleIdIsNumber))
			return false;
		var maxId = -Infinity;
		var minId = Infinity;
		modules.forEach(function(module) {
			if(maxId < module.id) maxId = module.id;
			if(minId > module.id) minId = module.id;
		});
		if(minId < 16 + ("" + minId).length) {
			// add minId x ',' instead of 'Array(minId).concat(...)'
			minId = 0;
		}
		var objectOverhead = modules.map(function(module) {
			var idLength = (module.id + "").length;
			return idLength + 2;
		}).reduce(function(a, b) {
			return a + b;
		}, -1);
		var arrayOverhead = minId === 0 ? maxId : 16 + ("" + minId).length + maxId;
		return arrayOverhead < objectOverhead ? [minId, maxId] : false;
	}

	renderChunkModules(chunk, moduleTemplate, dependencyTemplates, prefix) {
		if(!prefix) prefix = "";
		var source = new ConcatSource();
		if(chunk.getNumberOfModules() === 0) {
			source.add("[]");
			return source;
		}
		var removedModules = chunk.removedModules;
		var allModules = chunk.mapModules(function(module) {
			return {
				id: module.id,
				source: moduleTemplate.render(module, dependencyTemplates, chunk)
			};
		});
		if(removedModules && removedModules.length > 0) {
			removedModules.forEach(function(id) {
				allModules.push({
					id: id,
					source: "false"
				});
			});
		}
		var bounds = this.getModulesArrayBounds(allModules);

		if(bounds) {
			// Render a spare array
			var minId = bounds[0];
			var maxId = bounds[1];
			if(minId !== 0) source.add("Array(" + minId + ").concat(");
			source.add("[\n");
			var modules = {};
			allModules.forEach(function(module) {
				modules[module.id] = module;
			});
			for(var idx = minId; idx <= maxId; idx++) {
				var module = modules[idx];
				if(idx !== minId) source.add(",\n");
				source.add("/* " + idx + " */");
				if(module) {
					source.add("\n");
					source.add(module.source);
				}
			}
			source.add("\n" + prefix + "]");
			if(minId !== 0) source.add(")");
		} else {
			// Render an object
			source.add("{\n");
			allModules.sort(function(a, b) {
				var aId = a.id + "";
				var bId = b.id + "";
				if(aId < bId) return -1;
				if(aId > bId) return 1;
				return 0;
			}).forEach(function(module, idx) {
				if(idx !== 0) source.add(",\n");
				source.add("\n/***/ " + JSON.stringify(module.id) + ":\n");
				source.add(module.source);
			});
			source.add("\n\n" + prefix + "}");
		}
		return source;
	}
};

function moduleIdIsNumber(module) {
	return typeof module.id === "number";
}
<|endoftext|>


<commit_before>/*
  Copyright (c) 2015 Klaralvdalens Datakonsult AB (KDAB).

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

var phantom;

if (!phantom)
  phantom = {};

(function() {
  phantom.require = function(file) {
    if (file === "webpage") {
      return { create: function() { return new phantom.WebPage; } };
    } else if (file === "system") {
      return new phantom.System;
    }
    /// TODO:
    throw Error("require(" + file + ") is not yet implemented");
  };

  phantom.exit = function() {
    native function exit();
    exit();
  };

  phantom.injectJs = function(file) {
    var library = phantom.internal.findLibrary(file);
    if (!library) {
      return false;
    }
    var code = phantom.internal.readFile(library);
    if (!code) {
      return false;
    }
    native function executeJavaScript();
    executeJavaScript(code, library);
    return true;
  };

  // can be overwritten by the user
  phantom.onError = null;

  // internal functionality that is not supposed to be used by users of PhantomJS
  phantom.internal = {
    // this gets assigned to window.onerror by default
    propagateOnError: function(errorMessage, url, lineNumber, columnNumber, error) {
      if (typeof phantom.onError === "function") {
        // keep compatibility with old phantomjs onError handler
        phantom.onError(errorMessage, error.stack, url, lineNumber, columnNumber, error);
      } else {
        native function printError();
        if (error.stack) {
          printError(String(error.stack));
        } else {
          printError(errorMessage + " at " + url + ":" + lineNumber + ":" + columnNumber);
        }
      }
    },
    // callback from webpage.evaluateJavaScript which runs the script and returns the result
    handleEvaluateJavaScript: function(script, queryId) {
      var retval = null;
      var exception = null;
      try {
        func = eval(script);
        retval = func();
      } catch(e) {
        exception = e;
        if (e.stack) {
          exception = e.stack;
        }
      }
      startPhantomJsQuery({
        request: JSON.stringify({
          type: 'returnEvaluateJavaScript',
          retval: JSON.stringify(retval),
          exception: exception ? String(exception) : "",
          queryId: queryId
        }),
        persistent: false,
        onSuccess: function() {},
        onFailure: function() {}
      });
    },
    query: function(request) {
      return new Promise(function(resolve, reject) {
        startPhantomJsQuery({
          request: JSON.stringify(request),
          persistent: false,
          onSuccess: resolve,
          onFailure: function(errorCode, errorMessage) {
            reject(errorMessage, errorCode);
          }
        });
      });
    },
    findLibrary: function(file, libraryPath) {
      native function findLibrary();
      return findLibrary(file, libraryPath ? libraryPath : phantom.libraryPath);
    },
    readFile: function(file) {
      native function readFile();
      return readFile(file);
    },
    onScriptLoadError: function() {
      native function printError();
      printError("Failed to load script \""+ phantom.args[0] + "\". Exiting now.");
      phantom.exit();
    }
  };

  // will be initialized from code executed via PhantomJSApp::OnContextInitialized
  phantom.args = [];
  phantom.libraryPath = "";
})();
<commit_msg>Better support returning DOM objects from page.evaluate.<commit_after>/*
  Copyright (c) 2015 Klaralvdalens Datakonsult AB (KDAB).

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

var phantom;

if (!phantom)
  phantom = {};

(function() {
  phantom.require = function(file) {
    if (file === "webpage") {
      return { create: function() { return new phantom.WebPage; } };
    } else if (file === "system") {
      return new phantom.System;
    }
    /// TODO:
    throw Error("require(" + file + ") is not yet implemented");
  };

  phantom.exit = function() {
    native function exit();
    exit();
  };

  phantom.injectJs = function(file) {
    var library = phantom.internal.findLibrary(file);
    if (!library) {
      return false;
    }
    var code = phantom.internal.readFile(library);
    if (!code) {
      return false;
    }
    native function executeJavaScript();
    executeJavaScript(code, library);
    return true;
  };

  // can be overwritten by the user
  phantom.onError = null;

  // internal functionality that is not supposed to be used by users of PhantomJS
  phantom.internal = {
    // this gets assigned to window.onerror by default
    propagateOnError: function(errorMessage, url, lineNumber, columnNumber, error) {
      if (typeof phantom.onError === "function") {
        // keep compatibility with old phantomjs onError handler
        phantom.onError(errorMessage, error.stack, url, lineNumber, columnNumber, error);
      } else {
        native function printError();
        if (error.stack) {
          printError(String(error.stack));
        } else {
          printError(errorMessage + " at " + url + ":" + lineNumber + ":" + columnNumber);
        }
      }
    },
    // callback from webpage.evaluateJavaScript which runs the script and returns the result
    handleEvaluateJavaScript: function(script, queryId) {
      var retval = null;
      var exception = null;
      try {
        func = eval(script);
        retval = func();
      } catch(e) {
        exception = e;
        if (e.stack) {
          exception = e.stack;
        }
      }
      // native DOM objects cannot be JSON.stringified directly
      // so instead copy the first level of data over
      // TODO: extend depth at will, but make sure we don't fall into
      // cycles
      function prepareJSONStringify(obj) {
        if (typeof(obj) !== "object")
          return obj;
        var ret = {};
        for (var k in obj) {
          ret[k] = obj[k];
        }
        return ret;
      }
      startPhantomJsQuery({
        request: JSON.stringify({
          type: 'returnEvaluateJavaScript',
          retval: JSON.stringify(prepareJSONStringify(retval)),
          exception: exception ? String(exception) : "",
          queryId: queryId
        }),
        persistent: false,
        onSuccess: function() {},
        onFailure: function() {}
      });
    },
    query: function(request) {
      return new Promise(function(resolve, reject) {
        startPhantomJsQuery({
          request: JSON.stringify(request),
          persistent: false,
          onSuccess: resolve,
          onFailure: function(errorCode, errorMessage) {
            reject(errorMessage, errorCode);
          }
        });
      });
    },
    findLibrary: function(file, libraryPath) {
      native function findLibrary();
      return findLibrary(file, libraryPath ? libraryPath : phantom.libraryPath);
    },
    readFile: function(file) {
      native function readFile();
      return readFile(file);
    },
    onScriptLoadError: function() {
      native function printError();
      printError("Failed to load script \""+ phantom.args[0] + "\". Exiting now.");
      phantom.exit();
    }
  };

  // will be initialized from code executed via PhantomJSApp::OnContextInitialized
  phantom.args = [];
  phantom.libraryPath = "";
})();
<|endoftext|>


<commit_before>var _ = require('underscore')
;

var Validate = exports.Validate = function (name, init) {
  this.name = name;
  this.trim_all = true;
  this.is_valid = true;
  this.validations = {};
  if (init)
    init(this);
};

var V = Validate;

V.new = function (name, init) {
  var v = new V(name, init);
  return v;
};

V.prototype.define = function(name, func) {
  var me = this;
  if (me.validations[name])
    throw new Error(name + ' already defined.');

  me.validations[name] = func;

  return me;
};

V.prototype.validate = function (target, flow) {
  var me = this;
  if (!target.sanitized_data)
    target.sanitized_data = {};

  var last_v = null;

  _.find(me.validations, function (func, k) {
    var v = Validation.new(me, k, target);
    func(v);

    last_v = v;
    return !v.is_valid;
  });

  if (!last_v)
    last_v = {is_valid: true};

  if (flow) {
    if (last_v.is_valid && flow.finish)
      flow.finish();
    else if (!last_v.is_valid && flow.invalid)
      flow.invalid(last_v.error_msg, last_v);
  }

  return last_v.is_valid;
};

var Validation = function (validate, key, target) {
  var me         = this;
  me.validate    = validate;
  me.key         = key;
  me.english_key = key.replace(/_/g, ' ');
  me.target      = target;
  me.val         = target.new_data[key];
  me.is_valid    = true;

  if (me.validate.trim_all && me.val && me.val.trim)
    me.sanitized(me.val.trim());
};

Validation.new = function (v, k, t) {
  var v = new Validation(v, k, t);
  return v;
};

Validation.prototype.sanitized = function (v) {
  var me = this;
  me.val = me.target.sanitized_data[me.key] = v;
  return v;
};

Validation.define = function (name, func) {
  Validation.prototype[name] = function () {
    var me = this;
    if (!me.validate.is_valid)
      return me;

    var args = _.toArray(arguments);
    var result = func.apply(me, args);

    if (result[0]) {
      me.sanitized(result[1]);
      return me;
    }

    me.is_valid  = false;
    me.error_msg = me.target.errors = result[1];

    return me;
  };
};

Validation.define('at_least', function (n, msg) {
  var me = this;

  if (me.val &&
      me.val.length &&
        me.val.length >= n)
    return [true, me.val];

  msg = msg || me.english_key + (' must be at least: ' + n);
  return [false, msg];
});

Validation.define('at_most', function (n, msg) {
  var me = this;

  if (me.val &&
      me.val.length &&
        me.val.length <= n)
    return [true, me.val];

  msg = msg || me.english_key + (' can not be more than: ' + n);
  return [false, msg];
});

Validation.define('between', function (min, max, msg) {
  var me = this;
  msg = msg || me.english_key + ' must be between: ' + min + ' and ' + max;

  me
  .at_least(min, msg)
  .at_most(max, msg);

  if(me.is_valid)
    return [true, me.val];
  return [false, msg];

});

Validation.define('at_least_2_words', function (msg) {
  var me = this;
  var v = (me.val || '').trim();

  if(v.indexOf(' ') > 0)
    return [true, v];

  msg = msg || me.english_key + ' must be two words or more.';
  return [false, msg];

});

Validation.define('equals', function (v, msg) {
  var me = this;
  msg = msg || me.english_key + ' must equal: ' + v;
  if (v !== me.val)
    return [false, msg];
  return [true, v];
});

Validation.define('not_empty', function (msg) {
  var me = this;
  msg = msg || me.english_key + ' must not be empty.';
  if (_.isArray(me.val)) {
    var v = _.compact(me.val);
  } else {
    var v = (me.val || '').trim();
  };

  if (v.length > 0)
    return [true, v];
  return [false, msg];
});

Validation.define('length_gte', function (min, msg) {
  var me = this
  , val  = me.val || '';

  msg = msg || 'Length of ' + me.english_key + ' must be greater or equal to: ' + min;

  if (val.trim)
    val = val.trim();

  if (_.isArray(val))
    val = _.compact(val);

  if (val.length >= min)
    return [true, val];

  return [false, msg];
});

Validation.define('match', function (regex, msg) {
  var me = this;
  msg = msg || me.english_key + ' must match: ' + regex;
  if (me.val && !me.val.match)
    return [false, me.english_key + ' is not a string: '];
  if ((me.val || '').match(regex))
    return [true, me.val];
  return [false, msg];
});

Validation.define('not_match', function (regex, msg) {

  var me = this;
  msg = msg || me.english_key + ' must not match: ' + regex;
  if (me.val && !me.val.match)
    return [false, me.english_key + ' is not a string.'];
  if ((me.val || '').match(regex))
    return [false, msg];
  return [true, me.val];
});

<commit_msg>Added: .is_null_if_empty<commit_after>var _ = require('underscore')
;

var Validate = exports.Validate = function (name, init) {
  this.name = name;
  this.trim_all = true;
  this.is_valid = true;
  this.validations = {};
  if (init)
    init(this);
};

var V = Validate;

V.new = function (name, init) {
  var v = new V(name, init);
  return v;
};

V.prototype.define = function(name, func) {
  var me = this;
  if (me.validations[name])
    throw new Error(name + ' already defined.');

  me.validations[name] = func;

  return me;
};

V.prototype.validate = function (target, flow) {
  var me = this;
  if (!target.sanitized_data)
    target.sanitized_data = {};

  var last_v = null;

  _.find(me.validations, function (func, k) {
    var v = Validation.new(me, k, target);
    func(v);

    last_v = v;
    return !v.is_valid;
  });

  if (!last_v)
    last_v = {is_valid: true};

  if (flow) {
    if (last_v.is_valid && flow.finish)
      flow.finish();
    else if (!last_v.is_valid && flow.invalid)
      flow.invalid(last_v.error_msg, last_v);
  }

  return last_v.is_valid;
};

var Validation = function (validate, key, target) {
  var me         = this;
  me.validate    = validate;
  me.key         = key;
  me.english_key = key.replace(/_/g, ' ');
  me.target      = target;
  me.val         = target.new_data[key];
  me.is_valid    = true;

  if (me.validate.trim_all && me.val && me.val.trim)
    me.sanitized(me.val.trim());
};

Validation.new = function (v, k, t) {
  var v = new Validation(v, k, t);
  return v;
};

Validation.prototype.sanitized = function (v) {
  var me = this;
  me.val = me.target.sanitized_data[me.key] = v;
  return v;
};

Validation.define = function (name, func) {
  Validation.prototype[name] = function () {
    var me = this;
    if (!me.validate.is_valid)
      return me;

    var args = _.toArray(arguments);
    var result = func.apply(me, args);

    if (result[0]) {
      me.sanitized(result[1]);
      return me;
    }

    me.is_valid  = false;
    me.error_msg = me.target.errors = result[1];

    return me;
  };
};

Validation.define('at_least', function (n, msg) {
  var me = this;

  if (me.val &&
      me.val.length &&
        me.val.length >= n)
    return [true, me.val];

  msg = msg || me.english_key + (' must be at least: ' + n);
  return [false, msg];
});

Validation.define('at_most', function (n, msg) {
  var me = this;

  if (me.val &&
      me.val.length &&
        me.val.length <= n)
    return [true, me.val];

  msg = msg || me.english_key + (' can not be more than: ' + n);
  return [false, msg];
});

Validation.define('between', function (min, max, msg) {
  var me = this;
  msg = msg || me.english_key + ' must be between: ' + min + ' and ' + max;

  me
  .at_least(min, msg)
  .at_most(max, msg);

  if(me.is_valid)
    return [true, me.val];
  return [false, msg];

});

Validation.define('at_least_2_words', function (msg) {
  var me = this;
  var v = (me.val || '').trim();

  if(v.indexOf(' ') > 0)
    return [true, v];

  msg = msg || me.english_key + ' must be two words or more.';
  return [false, msg];

});

Validation.define('equals', function (v, msg) {
  var me = this;
  msg = msg || me.english_key + ' must equal: ' + v;
  if (v !== me.val)
    return [false, msg];
  return [true, v];
});

Validation.define('is_null_if_empty', function (v, msg) {
  if (!v || (v.trim && v.trim() === ''))
    return [true, null];
  return [true, v];
});

Validation.define('not_empty', function (msg) {
  var me = this;
  msg = msg || me.english_key + ' must not be empty.';
  if (_.isArray(me.val)) {
    var v = _.compact(me.val);
  } else {
    var v = (me.val || '').trim();
  };

  if (v.length > 0)
    return [true, v];
  return [false, msg];
});

Validation.define('length_gte', function (min, msg) {
  var me = this
  , val  = me.val || '';

  msg = msg || 'Length of ' + me.english_key + ' must be greater or equal to: ' + min;

  if (val.trim)
    val = val.trim();

  if (_.isArray(val))
    val = _.compact(val);

  if (val.length >= min)
    return [true, val];

  return [false, msg];
});

Validation.define('match', function (regex, msg) {
  var me = this;
  msg = msg || me.english_key + ' must match: ' + regex;
  if (me.val && !me.val.match)
    return [false, me.english_key + ' is not a string: '];
  if ((me.val || '').match(regex))
    return [true, me.val];
  return [false, msg];
});

Validation.define('not_match', function (regex, msg) {

  var me = this;
  msg = msg || me.english_key + ' must not match: ' + regex;
  if (me.val && !me.val.match)
    return [false, me.english_key + ' is not a string.'];
  if ((me.val || '').match(regex))
    return [false, msg];
  return [true, me.val];
});

<|endoftext|>


<commit_before>var isBrowser = (typeof window !== 'undefined')

if (isBrowser && !AudioContext)
  throw new Error('This browser doesn\'t seem to support web audio API')

var CLOCK_DEFAULTS = {
  toleranceLate: 0.10,
  toleranceEarly: 0.001
}

// ==================== Event ==================== //
var Event = function(clock, deadline, func) {
  this.clock = clock
  this.func = func
  this._cleared = false // Flag used to clear an event inside callback

  this.toleranceLate = clock.toleranceLate
  this.toleranceEarly = clock.toleranceEarly
  this._latestTime = null
  this._earliestTime = null
  this.deadline = null
  this.repeatTime = null

  this.schedule(deadline)
}

// Unschedules the event
Event.prototype.clear = function() {
  this.clock._removeEvent(this)
  this._cleared = true
  return this
}

// Sets the event to repeat every `time` seconds.
Event.prototype.repeat = function(time) {
  if (time === 0)
    throw new Error('delay cannot be 0')
  this.repeatTime = time
  if (!this.clock._hasEvent(this))
    this.schedule(this.deadline + this.repeatTime)
  return this
}

// Sets the time tolerance of the event.
// The event will be executed in the interval `[deadline - early, deadline + late]`
// If the clock fails to execute the event in time, the event will be dropped.
Event.prototype.tolerance = function(values) {
  if (typeof values.late === 'number')
    this.toleranceLate = values.late
  if (typeof values.early === 'number')
    this.toleranceEarly = values.early
  this._refreshEarlyLateDates()
  if (this.clock._hasEvent(this)) {
    this.clock._removeEvent(this)
    this.clock._insertEvent(this)
  }
  return this
}

// Returns true if the event is repeated, false otherwise
Event.prototype.isRepeated = function() { return this.repeatTime !== null }

// Schedules the event to be ran before `deadline`.
// If the time is within the event tolerance, we handle the event immediately.
// If the event was already scheduled at a different time, it is rescheduled.
Event.prototype.schedule = function(deadline) {
  this._cleared = false
  this.deadline = deadline
  this._refreshEarlyLateDates()

  if (this.clock.context.currentTime >= this._earliestTime) {
    this._execute()

  } else if (this.clock._hasEvent(this)) {
    this.clock._removeEvent(this)
    this.clock._insertEvent(this)

  } else this.clock._insertEvent(this)
}

Event.prototype.timeStretch = function(tRef, ratio) {
  if (this.isRepeated())
    this.repeatTime = this.repeatTime * ratio

  var deadline = tRef + ratio * (this.deadline - tRef)
  // If the deadline is too close or past, and the event has a repeat,
  // we calculate the next repeat possible in the stretched space.
  if (this.isRepeated()) {
    while (this.clock.context.currentTime >= deadline - this.toleranceEarly)
      deadline += this.repeatTime
  }
  this.schedule(deadline)
}

// Executes the event
Event.prototype._execute = function() {
  if (this.clock._started === false) return
  this.clock._removeEvent(this)

  if (this.clock.context.currentTime < this._latestTime)
    this.func(this)
  else {
    if (this.onexpired) this.onexpired(this)
    console.warn('event expired')
  }
  // In the case `schedule` is called inside `func`, we need to avoid
  // overrwriting with yet another `schedule`.
  if (!this.clock._hasEvent(this) && this.isRepeated() && !this._cleared)
    this.schedule(this.deadline + this.repeatTime)
}

// Updates cached times
Event.prototype._refreshEarlyLateDates = function() {
  this._latestTime = this.deadline + this.toleranceLate
  this._earliestTime = this.deadline - this.toleranceEarly
}

// ==================== WAAClock ==================== //
var WAAClock = module.exports = function(context, opts) {
  var self = this
  opts = opts || {}
  this.tickMethod = opts.tickMethod || 'ScriptProcessorNode'
  this.toleranceEarly = opts.toleranceEarly || CLOCK_DEFAULTS.toleranceEarly
  this.toleranceLate = opts.toleranceLate || CLOCK_DEFAULTS.toleranceLate
  this.context = context
  this._events = []
  this._started = false
}

// ---------- Public API ---------- //
// Schedules `func` to run after `delay` seconds.
WAAClock.prototype.setTimeout = function(func, delay) {
  return this._createEvent(func, this._absTime(delay))
}

// Schedules `func` to run before `deadline`.
WAAClock.prototype.callbackAtTime = function(func, deadline) {
  return this._createEvent(func, deadline)
}

// Stretches `deadline` and `repeat` of all scheduled `events` by `ratio`, keeping
// their relative distance to `tRef`. In fact this is equivalent to changing the tempo.
WAAClock.prototype.timeStretch = function(tRef, events, ratio) {
  events.forEach(function(event) { event.timeStretch(tRef, ratio) })
  return events
}

// Removes all scheduled events and starts the clock
WAAClock.prototype.start = function() {
  if (this._started === false) {
    var self = this
    this._started = true
    this._events = []

    if (this.tickMethod === 'ScriptProcessorNode') {
      var bufferSize = 256
      // We have to keep a reference to the node to avoid garbage collection
      this._clockNode = this.context.createScriptProcessor(bufferSize, 1, 1)
      this._clockNode.connect(this.context.destination)
      this._clockNode.onaudioprocess = function () {
        process.nextTick(function() { self._tick() })
      }
    } else if (this.tickMethod === 'manual') null // _tick is called manually

    else throw new Error('invalid tickMethod ' + this.tickMethod)
  }
}

// Stops the clock
WAAClock.prototype.stop = function() {
  if (this._started === true) {
    this._started = false
    this._clockNode.disconnect()
  }
}

// ---------- Private ---------- //

// This function is ran periodically, and at each tick it executes
// events for which `currentTime` is included in their tolerance interval.
WAAClock.prototype._tick = function() {
  var event = this._events.shift()

  while(event && event._earliestTime <= this.context.currentTime) {
    event._execute()
    event = this._events.shift()
  }

  // Put back the last event
  if(event) this._events.unshift(event)
}

// Creates an event and insert it to the list
WAAClock.prototype._createEvent = function(func, deadline) {
  return new Event(this, deadline, func)
}

// Inserts an event to the list
WAAClock.prototype._insertEvent = function(event) {
  this._events.splice(this._indexByTime(event._earliestTime), 0, event)
}

// Removes an event from the list
WAAClock.prototype._removeEvent = function(event) {
  var ind = this._events.indexOf(event)
  if (ind !== -1) this._events.splice(ind, 1)
}

// Returns true if `event` is in queue, false otherwise
WAAClock.prototype._hasEvent = function(event) {
 return this._events.indexOf(event) !== -1
}

// Returns the index of the first event whose deadline is >= to `deadline`
WAAClock.prototype._indexByTime = function(deadline) {
  // performs a binary search
  var low = 0
    , high = this._events.length
    , mid
  while (low < high) {
    mid = Math.floor((low + high) / 2)
    if (this._events[mid]._earliestTime < deadline)
      low = mid + 1
    else high = mid
  }
  return low
}

// Converts from relative time to absolute time
WAAClock.prototype._absTime = function(relTime) {
  return relTime + this.context.currentTime
}

// Converts from absolute time to relative time
WAAClock.prototype._relTime = function(absTime) {
  return absTime - this.context.currentTime
}<commit_msg>shoudln't throw an error if web audio not supported<commit_after>var isBrowser = (typeof window !== 'undefined')

var CLOCK_DEFAULTS = {
  toleranceLate: 0.10,
  toleranceEarly: 0.001
}

// ==================== Event ==================== //
var Event = function(clock, deadline, func) {
  this.clock = clock
  this.func = func
  this._cleared = false // Flag used to clear an event inside callback

  this.toleranceLate = clock.toleranceLate
  this.toleranceEarly = clock.toleranceEarly
  this._latestTime = null
  this._earliestTime = null
  this.deadline = null
  this.repeatTime = null

  this.schedule(deadline)
}

// Unschedules the event
Event.prototype.clear = function() {
  this.clock._removeEvent(this)
  this._cleared = true
  return this
}

// Sets the event to repeat every `time` seconds.
Event.prototype.repeat = function(time) {
  if (time === 0)
    throw new Error('delay cannot be 0')
  this.repeatTime = time
  if (!this.clock._hasEvent(this))
    this.schedule(this.deadline + this.repeatTime)
  return this
}

// Sets the time tolerance of the event.
// The event will be executed in the interval `[deadline - early, deadline + late]`
// If the clock fails to execute the event in time, the event will be dropped.
Event.prototype.tolerance = function(values) {
  if (typeof values.late === 'number')
    this.toleranceLate = values.late
  if (typeof values.early === 'number')
    this.toleranceEarly = values.early
  this._refreshEarlyLateDates()
  if (this.clock._hasEvent(this)) {
    this.clock._removeEvent(this)
    this.clock._insertEvent(this)
  }
  return this
}

// Returns true if the event is repeated, false otherwise
Event.prototype.isRepeated = function() { return this.repeatTime !== null }

// Schedules the event to be ran before `deadline`.
// If the time is within the event tolerance, we handle the event immediately.
// If the event was already scheduled at a different time, it is rescheduled.
Event.prototype.schedule = function(deadline) {
  this._cleared = false
  this.deadline = deadline
  this._refreshEarlyLateDates()

  if (this.clock.context.currentTime >= this._earliestTime) {
    this._execute()

  } else if (this.clock._hasEvent(this)) {
    this.clock._removeEvent(this)
    this.clock._insertEvent(this)

  } else this.clock._insertEvent(this)
}

Event.prototype.timeStretch = function(tRef, ratio) {
  if (this.isRepeated())
    this.repeatTime = this.repeatTime * ratio

  var deadline = tRef + ratio * (this.deadline - tRef)
  // If the deadline is too close or past, and the event has a repeat,
  // we calculate the next repeat possible in the stretched space.
  if (this.isRepeated()) {
    while (this.clock.context.currentTime >= deadline - this.toleranceEarly)
      deadline += this.repeatTime
  }
  this.schedule(deadline)
}

// Executes the event
Event.prototype._execute = function() {
  if (this.clock._started === false) return
  this.clock._removeEvent(this)

  if (this.clock.context.currentTime < this._latestTime)
    this.func(this)
  else {
    if (this.onexpired) this.onexpired(this)
    console.warn('event expired')
  }
  // In the case `schedule` is called inside `func`, we need to avoid
  // overrwriting with yet another `schedule`.
  if (!this.clock._hasEvent(this) && this.isRepeated() && !this._cleared)
    this.schedule(this.deadline + this.repeatTime)
}

// Updates cached times
Event.prototype._refreshEarlyLateDates = function() {
  this._latestTime = this.deadline + this.toleranceLate
  this._earliestTime = this.deadline - this.toleranceEarly
}

// ==================== WAAClock ==================== //
var WAAClock = module.exports = function(context, opts) {
  var self = this
  opts = opts || {}
  this.tickMethod = opts.tickMethod || 'ScriptProcessorNode'
  this.toleranceEarly = opts.toleranceEarly || CLOCK_DEFAULTS.toleranceEarly
  this.toleranceLate = opts.toleranceLate || CLOCK_DEFAULTS.toleranceLate
  this.context = context
  this._events = []
  this._started = false
}

// ---------- Public API ---------- //
// Schedules `func` to run after `delay` seconds.
WAAClock.prototype.setTimeout = function(func, delay) {
  return this._createEvent(func, this._absTime(delay))
}

// Schedules `func` to run before `deadline`.
WAAClock.prototype.callbackAtTime = function(func, deadline) {
  return this._createEvent(func, deadline)
}

// Stretches `deadline` and `repeat` of all scheduled `events` by `ratio`, keeping
// their relative distance to `tRef`. In fact this is equivalent to changing the tempo.
WAAClock.prototype.timeStretch = function(tRef, events, ratio) {
  events.forEach(function(event) { event.timeStretch(tRef, ratio) })
  return events
}

// Removes all scheduled events and starts the clock
WAAClock.prototype.start = function() {
  if (this._started === false) {
    var self = this
    this._started = true
    this._events = []

    if (this.tickMethod === 'ScriptProcessorNode') {
      var bufferSize = 256
      // We have to keep a reference to the node to avoid garbage collection
      this._clockNode = this.context.createScriptProcessor(bufferSize, 1, 1)
      this._clockNode.connect(this.context.destination)
      this._clockNode.onaudioprocess = function () {
        process.nextTick(function() { self._tick() })
      }
    } else if (this.tickMethod === 'manual') null // _tick is called manually

    else throw new Error('invalid tickMethod ' + this.tickMethod)
  }
}

// Stops the clock
WAAClock.prototype.stop = function() {
  if (this._started === true) {
    this._started = false
    this._clockNode.disconnect()
  }
}

// ---------- Private ---------- //

// This function is ran periodically, and at each tick it executes
// events for which `currentTime` is included in their tolerance interval.
WAAClock.prototype._tick = function() {
  var event = this._events.shift()

  while(event && event._earliestTime <= this.context.currentTime) {
    event._execute()
    event = this._events.shift()
  }

  // Put back the last event
  if(event) this._events.unshift(event)
}

// Creates an event and insert it to the list
WAAClock.prototype._createEvent = function(func, deadline) {
  return new Event(this, deadline, func)
}

// Inserts an event to the list
WAAClock.prototype._insertEvent = function(event) {
  this._events.splice(this._indexByTime(event._earliestTime), 0, event)
}

// Removes an event from the list
WAAClock.prototype._removeEvent = function(event) {
  var ind = this._events.indexOf(event)
  if (ind !== -1) this._events.splice(ind, 1)
}

// Returns true if `event` is in queue, false otherwise
WAAClock.prototype._hasEvent = function(event) {
 return this._events.indexOf(event) !== -1
}

// Returns the index of the first event whose deadline is >= to `deadline`
WAAClock.prototype._indexByTime = function(deadline) {
  // performs a binary search
  var low = 0
    , high = this._events.length
    , mid
  while (low < high) {
    mid = Math.floor((low + high) / 2)
    if (this._events[mid]._earliestTime < deadline)
      low = mid + 1
    else high = mid
  }
  return low
}

// Converts from relative time to absolute time
WAAClock.prototype._absTime = function(relTime) {
  return relTime + this.context.currentTime
}

// Converts from absolute time to relative time
WAAClock.prototype._relTime = function(absTime) {
  return absTime - this.context.currentTime
}<|endoftext|>


<commit_before>/** ****************************************************************************************************
 * File: login.js
 * Project: hootenanny-ui
 * @author Matt Putipong on 11/14/18
 *******************************************************************************************************/

import '../../css/hoot/login.scss';

import * as d3      from 'd3';
import API          from './managers/api';
import Navbar       from './ui/navbar';

class Login {
    constructor() {
        this.api = new API();
    }

    async render() {
        new Navbar( false, this ).render();

        d3.select( 'body' ).style( 'background-color', '#f6f6f6' );

        let wrapper = d3.select( '#id-sink' )
            .append( 'div' )
            .attr( 'id', 'login-page' );

        let container = wrapper
            .append( 'div' )
            .classed( 'login-container', true );

        let blurb = container
            .append( 'div' );

        blurb
            .append( 'h2' )
            .text( 'What is Hootenanny?' );

        blurb
            .append( 'p' )
            .append( 'em' )
            .text( 'Hootenanny:' );

        blurb
            .append( 'ol' )
            .append( 'li' )
            .text( 'a gathering at which folksingers entertain often with the audience joining in' );

        blurb
            .append( 'p' )
            .append( 'em' )
            .text( 'Conflation' );

        blurb
            .append( 'ol' )
            .append( 'li' )
            .text( 'Fancy word for merge' );

        blurb
            .append( 'p' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny is an open source conflation tool developed to facilitate automated and semi-automated conflation of critical Foundation GEOINT features in the topographic domain. In short, it merges multiple maps of geodata into a single seamless map.' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny conflation occurs at the dataset level, where the user\'s workflow determines the best reference dataset, source content, geometry, and attributes to transfer to the output map. Hootenanny\'s internal processing leverages the key value pair structure of OpenStreetMap (OSM) for improved utility and applicability to broader user groups. Normalized attributes can be used to aid in feature matching, and OSM’s free tagging system allows the map to include an unlimited number of attributes describing each feature.' );

        blurb
            .append( 'p' )
            .html( 'Hootenanny is developed under the open source General Public License (GPL) and maintained on the National Geospatial-Intelligence Agency’s (NGA) GitHub site.' );

        blurb
            .append( 'h2' )
            .text( 'Access & Authentication' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny utilizes a third party to associate your contributions and perform basic authorization. You should have seen a popup dialog directing you to grant Hootenanny access to basic information about who you are. Once you have completed that wokflow, you should not see this again until your session is removed or expires. It is very common for browsers to block popups, see below for allowing us to create a window.' );

        container
            .append( 'h2' )
            .text( 'Enabling Popup Windows: Chrome & Chromium' );

        let table = container
            .append( 'div' )
            .classed( 'hoot-login-table', true );

        let row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell text', true )
            .html( '► Check the right side of the address bar. Click the discrete notification' );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell text', true )
            .html( '► Click \'Always Allow\' and \'Done\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-chrome-1.png' );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-chrome-2.png' );

        container
            .append( 'h2' )
            .text( 'Enabling Popup Windows: Firefox' );

        table = container
            .append( 'div' )
            .classed( 'hoot-login-table', true );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .html( '► You might see a yellow bar with a notification of a popup blocked' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-1.png' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell pad-top', true )
            .html( '► Click \'Options\' and then select \'Allow...\' or \'Show...\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-2.png' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell pad-top', true )
            .html( '► Alternatively if the yellow notification is not present, look to the left in the address bar. Click on the discrete notification, and then select \'Show...\' or toggle \'Block\' to \'Allow\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-3.png' );

        container
            .append( 'h2' )
            .text( 'Without Popup Windows' );

        container
            .append( 'p' )
            .html( '► Click ' )
            .append( 'a' )
            .attr( 'href', '#!' )
            .text( 'here' )
            .on( 'click', () => window.location = this.oauthRedirectUrl );

        this.oauthRedirectUrl = await this.api.getOAuthRedirectUrl();

        this.launchOAuthLogin();

        return this;
    }

    findGetParameter( parameterName ) {
        var result = null,
            tmp    = [];

        location.search
            .substr( 1 )
            .split( '&' )
            .forEach( function( item ) {
                tmp = item.split( '=' );
                if ( tmp[ 0 ] === parameterName ) result = decodeURIComponent( tmp[ 1 ] );
            } );

        return result;
    }

    launchOAuthLogin() {
        // in parent
        window.open( this.oauthRedirectUrl, 'hootenannyLoginRedirect', 'width=500,height=800,toolbar=no,status=no,menubar=no' );

        window.oAuthDone = ( e, user_object ) => {
            if ( e ) {
                console.warn( 'Failed to verify oauth tokens w/ provider:' );
                console.warn( 'XMLHttpRequest.status', e.status || null );
                console.warn( 'XMLHttpRequest.responseText ', e.responseText || null );

                window.alert( 'Failed to complete oauth handshake. Check console for details & retry.' );
                window.history.pushState( {}, document.title, window.location.pathname );

            } else {
                if ( localStorage ) {
                    localStorage.setItem( 'user', JSON.stringify( user_object ) );
                }
            }
        };
    }

    verifyOAuth( oauthToken, oauthVerifier ) {
        // in popup
        this.api.verifyOAuth( oauthToken, oauthVerifier )
            .then( resp => {
                if ( opener ) {
                    window.onbeforeunload = function() {
                        opener.oAuthDone( null, resp );
                    };

                    let pathname = opener.location.pathname;

                    alert( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );
                    // redirect parent
                    opener.location.replace( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );

                    // close self
                    window.close();
                } else {
                    let pathname = window.location.pathname;

                    window.location.replace( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );
                }
            } )
            .catch( err => {
                window.alert( 'wait error' );
                window.alert( err );
                if ( opener ) {
                    window.onbeforeunload = function() {
                        opener.oAuthDone( err, null );
                    };

                    self.close();
                } else {
                    window.alert( 'Failed to complete oauth handshake. Check console for details & retry.' );
                    // clear oauth params.
                    window.history.pushState( {}, document.title, window.location.pathname );
                }
            } );
    }
}

window.d3    = d3;
window.Login = Login;

export default Login;
<commit_msg>fix same page login<commit_after>/** ****************************************************************************************************
 * File: login.js
 * Project: hootenanny-ui
 * @author Matt Putipong on 11/14/18
 *******************************************************************************************************/

import '../../css/hoot/login.scss';

import * as d3      from 'd3';
import API          from './managers/api';
import Navbar       from './ui/navbar';

class Login {
    constructor() {
        this.api = new API();
    }

    async render() {
        new Navbar( false, this ).render();

        d3.select( 'body' ).style( 'background-color', '#f6f6f6' );

        let wrapper = d3.select( '#id-sink' )
            .append( 'div' )
            .attr( 'id', 'login-page' );

        let container = wrapper
            .append( 'div' )
            .classed( 'login-container', true );

        let blurb = container
            .append( 'div' );

        blurb
            .append( 'h2' )
            .text( 'What is Hootenanny?' );

        blurb
            .append( 'p' )
            .append( 'em' )
            .text( 'Hootenanny:' );

        blurb
            .append( 'ol' )
            .append( 'li' )
            .text( 'a gathering at which folksingers entertain often with the audience joining in' );

        blurb
            .append( 'p' )
            .append( 'em' )
            .text( 'Conflation' );

        blurb
            .append( 'ol' )
            .append( 'li' )
            .text( 'Fancy word for merge' );

        blurb
            .append( 'p' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny is an open source conflation tool developed to facilitate automated and semi-automated conflation of critical Foundation GEOINT features in the topographic domain. In short, it merges multiple maps of geodata into a single seamless map.' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny conflation occurs at the dataset level, where the user\'s workflow determines the best reference dataset, source content, geometry, and attributes to transfer to the output map. Hootenanny\'s internal processing leverages the key value pair structure of OpenStreetMap (OSM) for improved utility and applicability to broader user groups. Normalized attributes can be used to aid in feature matching, and OSM’s free tagging system allows the map to include an unlimited number of attributes describing each feature.' );

        blurb
            .append( 'p' )
            .html( 'Hootenanny is developed under the open source General Public License (GPL) and maintained on the National Geospatial-Intelligence Agency’s (NGA) GitHub site.' );

        blurb
            .append( 'h2' )
            .text( 'Access & Authentication' );

        blurb
            .append( 'p' )
            .text( 'Hootenanny utilizes a third party to associate your contributions and perform basic authorization. You should have seen a popup dialog directing you to grant Hootenanny access to basic information about who you are. Once you have completed that wokflow, you should not see this again until your session is removed or expires. It is very common for browsers to block popups, see below for allowing us to create a window.' );

        container
            .append( 'h2' )
            .text( 'Enabling Popup Windows: Chrome & Chromium' );

        let table = container
            .append( 'div' )
            .classed( 'hoot-login-table', true );

        let row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell text', true )
            .html( '► Check the right side of the address bar. Click the discrete notification' );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell text', true )
            .html( '► Click \'Always Allow\' and \'Done\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-chrome-1.png' );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-chrome-2.png' );

        container
            .append( 'h2' )
            .text( 'Enabling Popup Windows: Firefox' );

        table = container
            .append( 'div' )
            .classed( 'hoot-login-table', true );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .html( '► You might see a yellow bar with a notification of a popup blocked' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-1.png' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell pad-top', true )
            .html( '► Click \'Options\' and then select \'Allow...\' or \'Show...\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-2.png' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell pad-top', true )
            .html( '► Alternatively if the yellow notification is not present, look to the left in the address bar. Click on the discrete notification, and then select \'Show...\' or toggle \'Block\' to \'Allow\'' );

        row = table
            .append( 'div' )
            .classed( 'hoot-login-row', true );

        row
            .append( 'div' )
            .classed( 'hoot-login-cell', true )
            .append( 'img' )
            .attr( 'src', './img/login-popup-firefox-3.png' );

        container
            .append( 'h2' )
            .text( 'Without Popup Windows' );

        container
            .append( 'p' )
            .html( '► Click ' )
            .append( 'a' )
            .attr( 'href', '#!' )
            .text( 'here' )
            .on( 'click', () => window.location = this.oauthRedirectUrl );

        this.oauthRedirectUrl = await this.api.getOAuthRedirectUrl();

        this.launchOAuthLogin();

        return this;
    }

    findGetParameter( parameterName ) {
        var result = null,
            tmp    = [];

        location.search
            .substr( 1 )
            .split( '&' )
            .forEach( function( item ) {
                tmp = item.split( '=' );
                if ( tmp[ 0 ] === parameterName ) result = decodeURIComponent( tmp[ 1 ] );
            } );

        return result;
    }

    launchOAuthLogin() {
        // in parent
        window.open( this.oauthRedirectUrl, 'hootenannyLoginRedirect', 'width=500,height=800,toolbar=no,status=no,menubar=no' );

        window.oAuthDone = ( e, user_object ) => {
            if ( e ) {
                console.warn( 'Failed to verify oauth tokens w/ provider:' );
                console.warn( 'XMLHttpRequest.status', e.status || null );
                console.warn( 'XMLHttpRequest.responseText ', e.responseText || null );

                window.alert( 'Failed to complete oauth handshake. Check console for details & retry.' );
                window.history.pushState( {}, document.title, window.location.pathname );

            } else {
                if ( localStorage ) {
                    localStorage.setItem( 'user', JSON.stringify( user_object ) );
                }
            }
        };
    }

    verifyOAuth( oauthToken, oauthVerifier ) {
        // in popup
        this.api.verifyOAuth( oauthToken, oauthVerifier )
            .then( resp => {
                if ( opener ) {
                    window.onbeforeunload = function() {
                        opener.oAuthDone( null, resp );
                    };

                    let pathname = opener.location.pathname;

                    alert( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );
                    // redirect parent
                    opener.location.replace( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );

                    // close self
                    window.close();
                } else {
                    localStorage.setItem( 'user', JSON.stringify( resp ) );

                    let pathname = window.location.pathname;

                    window.location.replace( pathname.substr( 0, pathname.lastIndexOf( '/' ) + 1 ) );
                }
            } )
            .catch( err => {
                window.alert( 'wait error' );
                window.alert( err );
                if ( opener ) {
                    window.onbeforeunload = function() {
                        opener.oAuthDone( err, null );
                    };

                    self.close();
                } else {
                    window.alert( 'Failed to complete oauth handshake. Check console for details & retry.' );
                    // clear oauth params.
                    window.history.pushState( {}, document.title, window.location.pathname );
                }
            } );
    }
}

window.d3    = d3;
window.Login = Login;

export default Login;
<|endoftext|>