<commit_before>/**
 * Provides custom elements and functionality used with widgets
 *
 * @version 0.6.10b 2012-05-04
 * @author Gregor Kofler
 *
 * currently providing
 *
 * vxJS.widget.shared.overlay()
 * vxJS.widget.shared.hiLite(text, node, className)
 * vxJS.widget.shared.shadeTableRows(parameterObject)
 * vxJS.widget.shared.list(parameterObject)
 *
 * @todo list: Safari doesn't follow keystrokes
 */

"use strict";

vxJS.widget.shared = {

	overlay: function() {
		var that;

		return function(config) {
			var size, resizeListenerId, shown, elem, body = vxJS.dom.getBody();

			if(that) {
				return that;
			}

			var resize = function() {
				var sizeTo = vxJS.dom.getViewportSize();

				if(sizeTo.x <= size.x && sizeTo.y <= size.y) {
					return;
				}

				size = sizeTo;
				vxJS.dom.setElementSize(elem, size);
			};

			var show = function() {
				if(shown || !body) {
					return;
				}

				shown = true;

				if(!elem) {
					elem = "div".setProp("class", "vxJS_overlay").create();
					vxJS.dom.setElementPosition(elem, new Coord());

					body.appendChild(elem);									// IE requires attached element to check properties
					elem.style.position = vxJS.dom.allowsFixedPosition() ? "fixed" : "absolute";
				}

				else {
					body.appendChild(elem);
				}
				size = vxJS.dom.getViewportSize();
				vxJS.dom.setElementSize(elem, size);

				resizeListenerId = vxJS.event.addListener(window, "resize", resize);
			};

			var hide = function() {
				if(!shown || !elem) {
					return;
				}

				shown = false;
				vxJS.event.removeListener(resizeListenerId);
				elem = body.removeChild(elem);
			};

			that = {
				show: show,
				hide: hide
			};

			return that;
		};
	}(),

	/**
	 * hilight text beneath a given node
	 */
	hiLite: function(text, node, cN) {

		var	rex = new RegExp(text, "gi"), fixRex = new RegExp("^" + text, "i"),
			chunks, hits, frag = document.createDocumentFragment(), i;

		if(!cN)		{ cN = "hiLite"; }
		if(!node)	{ node = document; }

		vxJS.dom.walk(node, function(n) {
			if(n.nodeType !== 3) {
				return;
			}

			hits = n.nodeValue.match(rex);

			if(hits && hits.length) {

				chunks = n.nodeValue.split(rex);

				if(chunks.length < 2) {
					if(fixRex.test(n.nodeValue)) {
						frag.appendChild("span".setProp("class", cN).create(hits[0]));
						frag.appendChild(document.createTextNode(chunks[0]));
					}
					else {
						frag.appendChild(document.createTextNode(chunks[0]));
						frag.appendChild("span".setProp("class", cN).create(hits[0]));
					}
				}
				else {
					for(i = 0; i < chunks.length-1; ++i) {
						frag.appendChild(document.createTextNode(chunks[i]));
						frag.appendChild("span".setProp("class", cN).create(hits.shift()));
					}
					frag.appendChild(document.createTextNode(chunks[i]));
				}
				n.parentNode.replaceChild(frag, n);
			}
		});
	},

	/**
	 * shade table rows by assigning alternating class names
	 */
	shadeTableRows: function(param) {
		var t, r, c, i, j, l;
		if(!param || !(t = param.element)) {
			return;
		}

		if (t.nodeName.toUpperCase() !== "TBODY") {
			if (!t.tBodies || !(t = t.tBodies[0])) {
				return;
			}
		}
		if(!(r = t.rows)) {
			return;
		}
		c = param.classNames && param.classNames.length && param.classNames.length > 1 ? param.classNames : ["row0", "row1"];

		for(i = 0, j = 0, l = r.length; l--; ++j) {
			if(r[j].style.display !== "none") {
				r[j].className = c[i++ % c.length];
			}
		}
	},

	/**
	 * a list widget, used with vxJS.widget.autoSuggest(), vxJS.widget.queryPopup(), etc.
	 *
	 * served events: "choose", "toggle", "cancel"
	 */
	list: function(param) {
		param = param || {};

		var	ul = "ul".setProp("class", param.className || "vxJS_list").create(),
			entries, multiple = param.multiple || false, selected = [], current, that = {},
			kc, kdFlag;

		ul.style.position = "relative";

		var setCurrent = function(n) {
			if(current === n) {
				return;
			}
			if(current) {
				vxJS.dom.removeClassName(current, "current");
			}
			vxJS.dom.addClassName(n, "current");
			current = n;

			if(!multiple) {
				selected[0] = n;
			}
		};

		var focus = function() {
			ul.tabIndex = -1;
			ul.focus();
		};

		var toggleSelected = function(n) {
			var i;
			if(!multiple) {
				return;
			}
			for(i = selected.length; i--;) {
				if(selected[i] === n) {
					selected.splice(i, 1);
					n.className = n.className.replace(/([ ]?selected)/, "");
					vxJS.serve(that, "toggle");
					return;
				}
			}
			n.className += n.className ? " selected" : "selected";
			selected.push(n);
			vxJS.serve(that, "toggle");
		};

		var scrollIn = function() {
			var	d = vxJS.dom.getElementOffset(current, ul).y,
				liH = d + vxJS.dom.getElementSize(current).y,
				ulH = vxJS.dom.getElementSize(ul).y;

			if(d < ul.scrollTop) {
				ul.scrollTop = d;
			}

			else if(liH > ulH + ul.scrollTop) {
				ul.scrollTop = liH - ulH;
			}
		};

		var hiLite = function(n) {
			if(n !== ul) {
				setCurrent(n.nodeName.toUpperCase() !== "LI" ? vxJS.dom.getParentElement(n, "li") : n);
			}
		};

		var findEntry = function(n) {
			var i;
			for(i = entries.length; i--;) {
				if(entries[i].elem === n) {
					return entries[i];
				}
			}
		};

		var fill = function(newEntries) {
			var i;
			entries = newEntries || [];

			if(!entries.length) {
				ul.style.display = "none";
				return;
			}

			vxJS.dom.deleteChildNodes(ul);
			for(i = 0; i < entries.length; ++i) {
				entries[i].elem = "li".create(entries[i].elements ? vxJS.dom.parse(entries[i].elements) : entries[i].text);
				entries[i].elem.style.position = "relative";
				ul.appendChild(entries[i].elem);
			}

			ul.style.display = "";
			setCurrent(ul.firstChild);
			scrollIn();
		};

		var keypress = function(e) {
			if(!kdFlag) {
				switch (kc) {
					case 38:
						that.up();
						break;
					case 40:
						that.down();
						break;
				}
			}
			kdFlag = false;
			vxJS.event.preventDefault(e);
		};

		var mousedown = function() {
			if(!param.hoverSelect) {
				hiLite(this);
			}
			if(this !== ul) {
				vxJS.event.serve(that, "choose");
			}
		};

		var click = function() {
			if(this !== ul) {
				toggleSelected(this.nodeName.toUpperCase() !== "LI" ? vxJS.dom.getParentElement(this, "li") : this);
			}
		};

		var keydown = function(e) {

			kdFlag = true;
			kc = e.keyCode;

			switch (kc) {
				case 13:
					vxJS.event.serve(that, "choose"); break;
				case 27:
					vxJS.event.serve(that, "cancel"); break;
				case 38:
					that.up(); break;
				case 40:
					that.down(); break;
				case 36:
					that.first(); break;
				case 35:
					that.last(); break;
				case 32:
					toggleSelected(current); break;
			}
			vxJS.event.preventDefault(e);
		};

		vxJS.event.addListener(ul, "mousedown", mousedown);
		vxJS.event.addListener(ul, "click", click);
		vxJS.event.addListener(ul, "keydown", keydown);
		vxJS.event.addListener(ul, "keypress", keypress);

		vxJS.event.addMousewheelListener(ul, function(e) { that[vxJS.event.getMousewheelDirection(e)](); });

		if(param.hoverSelect) {
			vxJS.event.addListener(ul, "mouseover", function() {
					hiLite(this);
				}
			);
		}
		fill(param.entries);

		/**
		 * public properties and functions
		 */
		that.getSelected = function() {
			var s = [], i;
			if(selected.length) {
				for(i = 0; i < selected.length; ++i) {
					s.push(findEntry(selected[i]));
				}
				return s;
			}
		};

		that.up = function() {
			if(current === ul.firstChild) {
				return;
			}
			if(!current && ul.lastChild) {
				setCurrent(ul.lastChild);
			}
			else {
				setCurrent(current.previousSibling);
			}
			scrollIn();
		};

		that.down = function() {
			if(current === ul.lastChild) {
				return;
			}
			if(!current && ul.firstChild) {
				setCurrent(ul.firstChild);
			}
			else {
				setCurrent(current.nextSibling);
			}
			scrollIn();
		};

		that.first = function() {
			if(current === ul.firstChild) {
				return;
			}
			setCurrent(ul.firstChild);
			scrollIn();
		};

		that.last = function() {
			if(current === ul.lastChild) {
				return;
			}
			setCurrent(ul.lastChild);
			scrollIn();
		};

		that.getRows	= function() { return entries ? entries.length : 0; };
		that.focus		= focus;
		that.fill		= fill;
		that.element	= ul;

		return that;
	}
};<commit_msg>vxJS.widget.shared.hiLite() properly escapes special characters within regular expressions.<commit_after>/**
 * Provides custom elements and functionality used with widgets
 *
 * @version 0.6.11 2012-07-02
 * @author Gregor Kofler
 *
 * currently providing
 *
 * vxJS.widget.shared.overlay()
 * vxJS.widget.shared.hiLite(text, node, className)
 * vxJS.widget.shared.shadeTableRows(parameterObject)
 * vxJS.widget.shared.list(parameterObject)
 *
 * @todo list: Safari doesn't follow keystrokes
 */

"use strict";

vxJS.widget.shared = {

	overlay: function() {
		var that;

		return function(config) {
			var size, resizeListenerId, shown, elem, body = vxJS.dom.getBody();

			if(that) {
				return that;
			}

			var resize = function() {
				var sizeTo = vxJS.dom.getViewportSize();

				if(sizeTo.x <= size.x && sizeTo.y <= size.y) {
					return;
				}

				size = sizeTo;
				vxJS.dom.setElementSize(elem, size);
			};

			var show = function() {
				if(shown || !body) {
					return;
				}

				shown = true;

				if(!elem) {
					elem = "div".setProp("class", "vxJS_overlay").create();
					vxJS.dom.setElementPosition(elem, new Coord());

					body.appendChild(elem);									// IE requires attached element to check properties
					elem.style.position = vxJS.dom.allowsFixedPosition() ? "fixed" : "absolute";
				}

				else {
					body.appendChild(elem);
				}
				size = vxJS.dom.getViewportSize();
				vxJS.dom.setElementSize(elem, size);

				resizeListenerId = vxJS.event.addListener(window, "resize", resize);
			};

			var hide = function() {
				if(!shown || !elem) {
					return;
				}

				shown = false;
				vxJS.event.removeListener(resizeListenerId);
				elem = body.removeChild(elem);
			};

			that = {
				show: show,
				hide: hide
			};

			return that;
		};
	}(),

	/**
	 * hilight text beneath a given node
	 */
	hiLite: function(text, node, cN) {

		var	rex = new RegExp(text, "gi"), fixRex = new RegExp("^" + text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), "i"),
			chunks, hits, frag = document.createDocumentFragment(), i;

		if(!cN)		{ cN = "hiLite"; }
		if(!node)	{ node = document; }

		vxJS.dom.walk(node, function(n) {
			if(n.nodeType !== 3) {
				return;
			}

			hits = n.nodeValue.match(rex);

			if(hits && hits.length) {

				chunks = n.nodeValue.split(rex);

				if(chunks.length < 2) {
					if(fixRex.test(n.nodeValue)) {
						frag.appendChild("span".setProp("class", cN).create(hits[0]));
						frag.appendChild(document.createTextNode(chunks[0]));
					}
					else {
						frag.appendChild(document.createTextNode(chunks[0]));
						frag.appendChild("span".setProp("class", cN).create(hits[0]));
					}
				}
				else {
					for(i = 0; i < chunks.length-1; ++i) {
						frag.appendChild(document.createTextNode(chunks[i]));
						frag.appendChild("span".setProp("class", cN).create(hits.shift()));
					}
					frag.appendChild(document.createTextNode(chunks[i]));
				}
				n.parentNode.replaceChild(frag, n);
			}
		});
	},

	/**
	 * shade table rows by assigning alternating class names
	 */
	shadeTableRows: function(param) {
		var t, r, c, i, j, l;
		if(!param || !(t = param.element)) {
			return;
		}

		if (t.nodeName.toUpperCase() !== "TBODY") {
			if (!t.tBodies || !(t = t.tBodies[0])) {
				return;
			}
		}
		if(!(r = t.rows)) {
			return;
		}
		c = param.classNames && param.classNames.length && param.classNames.length > 1 ? param.classNames : ["row0", "row1"];

		for(i = 0, j = 0, l = r.length; l--; ++j) {
			if(r[j].style.display !== "none") {
				r[j].className = c[i++ % c.length];
			}
		}
	},

	/**
	 * a list widget, used with vxJS.widget.autoSuggest(), vxJS.widget.queryPopup(), etc.
	 *
	 * served events: "choose", "toggle", "cancel"
	 */
	list: function(param) {
		param = param || {};

		var	ul = "ul".setProp("class", param.className || "vxJS_list").create(),
			entries, multiple = param.multiple || false, selected = [], current, that = {},
			kc, kdFlag;

		ul.style.position = "relative";

		var setCurrent = function(n) {
			if(current === n) {
				return;
			}
			if(current) {
				vxJS.dom.removeClassName(current, "current");
			}
			vxJS.dom.addClassName(n, "current");
			current = n;

			if(!multiple) {
				selected[0] = n;
			}
		};

		var focus = function() {
			ul.tabIndex = -1;
			ul.focus();
		};

		var toggleSelected = function(n) {
			var i;
			if(!multiple) {
				return;
			}
			for(i = selected.length; i--;) {
				if(selected[i] === n) {
					selected.splice(i, 1);
					n.className = n.className.replace(/([ ]?selected)/, "");
					vxJS.serve(that, "toggle");
					return;
				}
			}
			n.className += n.className ? " selected" : "selected";
			selected.push(n);
			vxJS.serve(that, "toggle");
		};

		var scrollIn = function() {
			var	d = vxJS.dom.getElementOffset(current, ul).y,
				liH = d + vxJS.dom.getElementSize(current).y,
				ulH = vxJS.dom.getElementSize(ul).y;

			if(d < ul.scrollTop) {
				ul.scrollTop = d;
			}

			else if(liH > ulH + ul.scrollTop) {
				ul.scrollTop = liH - ulH;
			}
		};

		var hiLite = function(n) {
			if(n !== ul) {
				setCurrent(n.nodeName.toUpperCase() !== "LI" ? vxJS.dom.getParentElement(n, "li") : n);
			}
		};

		var findEntry = function(n) {
			var i;
			for(i = entries.length; i--;) {
				if(entries[i].elem === n) {
					return entries[i];
				}
			}
		};

		var fill = function(newEntries) {
			var i;
			entries = newEntries || [];

			if(!entries.length) {
				ul.style.display = "none";
				return;
			}

			vxJS.dom.deleteChildNodes(ul);
			for(i = 0; i < entries.length; ++i) {
				entries[i].elem = "li".create(entries[i].elements ? vxJS.dom.parse(entries[i].elements) : entries[i].text);
				entries[i].elem.style.position = "relative";
				ul.appendChild(entries[i].elem);
			}

			ul.style.display = "";
			setCurrent(ul.firstChild);
			scrollIn();
		};

		var keypress = function(e) {
			if(!kdFlag) {
				switch (kc) {
					case 38:
						that.up();
						break;
					case 40:
						that.down();
						break;
				}
			}
			kdFlag = false;
			vxJS.event.preventDefault(e);
		};

		var mousedown = function() {
			if(!param.hoverSelect) {
				hiLite(this);
			}
			if(this !== ul) {
				vxJS.event.serve(that, "choose");
			}
		};

		var click = function() {
			if(this !== ul) {
				toggleSelected(this.nodeName.toUpperCase() !== "LI" ? vxJS.dom.getParentElement(this, "li") : this);
			}
		};

		var keydown = function(e) {

			kdFlag = true;
			kc = e.keyCode;

			switch (kc) {
				case 13:
					vxJS.event.serve(that, "choose"); break;
				case 27:
					vxJS.event.serve(that, "cancel"); break;
				case 38:
					that.up(); break;
				case 40:
					that.down(); break;
				case 36:
					that.first(); break;
				case 35:
					that.last(); break;
				case 32:
					toggleSelected(current); break;
			}
			vxJS.event.preventDefault(e);
		};

		vxJS.event.addListener(ul, "mousedown", mousedown);
		vxJS.event.addListener(ul, "click", click);
		vxJS.event.addListener(ul, "keydown", keydown);
		vxJS.event.addListener(ul, "keypress", keypress);

		vxJS.event.addMousewheelListener(ul, function(e) { that[vxJS.event.getMousewheelDirection(e)](); });

		if(param.hoverSelect) {
			vxJS.event.addListener(ul, "mouseover", function() {
					hiLite(this);
				}
			);
		}
		fill(param.entries);

		/**
		 * public properties and functions
		 */
		that.getSelected = function() {
			var s = [], i;
			if(selected.length) {
				for(i = 0; i < selected.length; ++i) {
					s.push(findEntry(selected[i]));
				}
				return s;
			}
		};

		that.up = function() {
			if(current === ul.firstChild) {
				return;
			}
			if(!current && ul.lastChild) {
				setCurrent(ul.lastChild);
			}
			else {
				setCurrent(current.previousSibling);
			}
			scrollIn();
		};

		that.down = function() {
			if(current === ul.lastChild) {
				return;
			}
			if(!current && ul.firstChild) {
				setCurrent(ul.firstChild);
			}
			else {
				setCurrent(current.nextSibling);
			}
			scrollIn();
		};

		that.first = function() {
			if(current === ul.firstChild) {
				return;
			}
			setCurrent(ul.firstChild);
			scrollIn();
		};

		that.last = function() {
			if(current === ul.lastChild) {
				return;
			}
			setCurrent(ul.lastChild);
			scrollIn();
		};

		that.getRows	= function() { return entries ? entries.length : 0; };
		that.focus		= focus;
		that.fill		= fill;
		that.element	= ul;

		return that;
	}
};<|endoftext|>


<commit_before>WAF.define('ClickMe', function() {

	var widget = WAF.require('waf-core/widget');

	//creating the widget
 	var ClickMe = widget.create('ClickMe');

 	// adding a dom property to the widget, this will also create a bindable attribute
 	ClickMe.addDomHtmlProperty('value');

 	//OPTION 1
 	//
 	// Use the autoFireEvent to associate the dom event 'click' to the wakanda Action event string (you can also associate many dom events to the same wakanda event if needed)
 	// In this case, I am not sending any particular data to the event.

 	ClickMe.autoFireDomEvent('click', "Action");

 	ClickMe.prototype.init = function() {

    	// adding a general class to the widget, just to be visually interesting
        this.addClass('waf-skin-box');

	}

 	/* OPTION 2
 	//
 	// Use JQuery 'click'  +   fire(Event)
	// In this case I am customising the data sent by the fire(Event)

	ClickMe.prototype.init = function() {

    	// adding a general class to the widget
        this.addClass('waf-skin-box');

        //creating an internal event that will change the color of the content to yellow
 		$(this.node).on('click', function() {
			this.fire(new Event.Action({
                value: 'I was clicked !'
            }));
		}.bind(this));
	}

	*/

	return ClickMe;

});<commit_msg>Update widget.js<commit_after>WAF.define('ClickMe', function() {

	var widget = WAF.require('waf-core/widget');

	//creating the widget
 	var ClickMe = widget.create('ClickMe');

 	// adding a dom property to the widget, this will also create a bindable attribute
 	ClickMe.addDomHtmlProperty('value');

 	//OPTION 1
 	//
 	// Use the autoFireEvent to associate the dom event 'click' to the wakanda Action event string (you can also associate many dom events to the same wakanda event if needed)
 	// In this case, I am not sending any particular data to the event.

 	ClickMe.autoFireDomEvent('click', "JustClicked");

 	ClickMe.prototype.init = function() {

    	// adding a general class to the widget, just to be visually interesting
        this.addClass('waf-skin-box');

	}

 	/* OPTION 2
 	//
 	// Use JQuery 'click'  +   fire(Event)
	// In this case I am customising the data sent by the fire(Event)

	ClickMe.prototype.init = function() {

    	// adding a general class to the widget
        this.addClass('waf-skin-box');

        //creating an internal event that will change the color of the content to yellow
 		$(this.node).on('click', function() {
			this.fire('JustClicked', {
                value: 'I was clicked !'
            }));
		}.bind(this));
	}

	*/

	return ClickMe;

});
<|endoftext|>


<commit_before>const EventEmitter = require("events").EventEmitter
const uwp = require("uwp")
uwp.projectNamespace("Windows") /* global Windows */
const debug = require("debug")("winble")
const _ = require("lodash")

const events = new EventEmitter()

module.exports = _.assign({},
  nobleBindings(
    bluetoothScanner(
      deviceWatcher(new EventEmitter()),
      events)),
  lifecycle(events),
  bluetoothDevice(events),
  events,
  {on: _.bind(EventEmitter.prototype.on, events)})

function nobleBindings (bluetoothScanner) {
  return {
    startScanning () {
      bluetoothScanner.start()
    },
    stopScanning () {
      bluetoothScanner.stop()
    },
  }
}

function bluetoothDevice (eventEmitter) {
  let bluetoothDevice

  return {connect, discoverServices, discoverCharacteristics, read, write, notify}

  function connect (peripheralUuid) {
    const uwpBluetoothAddress = Number("0x" + peripheralUuid)
    Windows.Devices.Bluetooth.BluetoothLEDevice.fromBluetoothAddressAsync(uwpBluetoothAddress).done(
      function (_bluetoothDevice) {
        debug("Bluetooth device connected", peripheralUuid)
        bluetoothDevice = _bluetoothDevice
        eventEmitter.emit("connect", peripheralUuid, null)
      },
      function (err) {
        debug("Connection failed to Bluetooth device", peripheralUuid)
        eventEmitter.emit("connect", peripheralUuid, err)
      })
  }

  function discoverServices (peripheralUuid, serviceUuids) {
    // TODO: use the parameters as they should be
    const services = bluetoothDevice.gattServices.map(service => service.uuid.split("-").join(""))
    eventEmitter.emit("servicesDiscover", peripheralUuid, services)
  }

  function discoverCharacteristics (peripheralUuid, serviceUuid, characteristicUuids) {
    // TODO: use peripheralUuid and characteristicUuids as they should
    const service = bluetoothDevice.getGattService(serviceUuid)
    const characteristics = service.getAllCharacteristics().map(characteristic => ({
      uuid: characteristic.uuid.split("-").join(""),
      properties: [], // TODO: fill properties
    }))
    eventEmitter.emit("characteristicsDiscover", peripheralUuid, serviceUuid, characteristics)
  }

  function read (peripheralUuid, serviceUuid, characteristicUuid) {
    bluetoothDevice.getGattService(serviceUuid).getCharacteristics(characteristicUuid)[0]
      .readValueAsync()
      .done(data => eventEmitter.emit("read", peripheralUuid, serviceUuid, characteristicUuid, data, false))
  }

  function write (peripheralUuid, serviceUuid, characteristicUuid, data, withoutResponse) {
    bluetoothDevice.getGattService(serviceUuid).getCharacteristics(characteristicUuid)[0]
      .writeValueAsync(Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(data))
      .done(() => eventEmitter.emit("write", peripheralUuid, serviceUuid, characteristicUuid))
  }

  function notify (peripheralUuid, serviceUuid, characteristicUuid, notify) {
    eventEmitter.emit("notify", peripheralUuid, serviceUuid, characteristicUuid, notify)
  }
}

function lifecycle (eventEmitter) {
  return {init}

  function init () {
    process.nextTick(powerOn)
    process.on("SIGINT", onSigInt)
    process.on("exit", onProcessExit)
  }

  function powerOn () {
    debug("powered on")
    eventEmitter.emit("stateChange", "poweredOn")
  }

  function onSigInt () {
    // copied over from https://github.com/sandeepmistry/noble/blob/master/lib/hci-socket/bindings.js#L86
    var sigIntListeners = process.listeners("SIGINT")
    if (sigIntListeners[sigIntListeners.length - 1] === onSigInt) {
      // we are the last listener, so exit
      // this will trigger onProcessExit, and clean up
      process.exit(1)
    }
  }

  function onProcessExit () {
    uwp.close()
    debug("uwp closed")
  }
}

function bluetoothScanner (deviceWatcher, eventEmitter) {
  deviceWatcher.onNewDevice(onNewDevice)
  let scanning = false

  return {start, stop}

  function start () {
    scanning = true
    if (deviceWatcher.startable()) deviceWatcher.start()
  }

  function stop () {
    scanning = false
  }

  function onNewDevice (device) {
    if (!scanning) return
    if (device.id.indexOf("BTHLEDevice") < 0) return // is this really trust-worthy?
    Windows.Devices.Bluetooth.BluetoothLEDevice.fromIdAsync(device.id).done(function (bluetoothDevice) {
      debug("discovered Bluetoothe Low Energy device:", device.id)
      eventEmitter.emit("discover",
        bluetoothDevice.bluetoothAddress.toString(16),
        bluetoothDevice.address,
        bluetoothDevice.addressType)
    })
  }
}

function deviceWatcher (eventEmitter) {
  const windowsDeviceWatcher = Windows.Devices.Enumeration.DeviceInformation.createWatcher()
  windowsDeviceWatcher.addEventListener("added", device => eventEmitter.emit("onNewDevice", device))

  const statuses = {created: 0, started: 1, enumerationCompleted: 2, stopping: 3, stopped: 4, aborted: 5}
  const startableStatuses = _(statuses).pick("created", "stopped", "aborted").values()

  return {
    startable: () => _.includes(startableStatuses, windowsDeviceWatcher.status),
    start: () => windowsDeviceWatcher.start(),
    stop: () => windowsDeviceWatcher.stop(),
    onNewDevice: callback => eventEmitter.on("onNewDevice", callback),
  }
}
<commit_msg>feat: support for multiple devices at the same time; parameters to discover* are not ignored<commit_after>const EventEmitter = require("events").EventEmitter
const uwp = require("uwp")
uwp.projectNamespace("Windows") /* global Windows */
const debug = require("debug")("winble")
const _ = require("lodash")

const events = new EventEmitter()

module.exports = _.assign({},
  nobleBindings(
    bluetoothScanner(
      deviceWatcher(new EventEmitter()),
      events)),
  lifecycle(events),
  bluetoothDeviceManager(events),
  events,
  {on: _.bind(EventEmitter.prototype.on, events)})

function nobleBindings (bluetoothScanner) {
  return {
    startScanning () {
      bluetoothScanner.start()
    },
    stopScanning () {
      bluetoothScanner.stop()
    },
  }
}

function bluetoothDeviceManager (eventEmitter) {
  const devices = {}

  return {connect, discoverServices, discoverCharacteristics, read, write, notify}

  function connect (peripheralUuid) {
    const uwpBluetoothAddress = Number("0x" + peripheralUuid)
    Windows.Devices.Bluetooth.BluetoothLEDevice.fromBluetoothAddressAsync(uwpBluetoothAddress).done(
      function (bluetoothDevice) {
        debug("Bluetooth device connected", peripheralUuid)
        devices[peripheralUuid] = bluetoothDevice
        eventEmitter.emit("connect", peripheralUuid, null)
      },
      function (err) {
        debug("Connection failed to Bluetooth device", peripheralUuid)
        eventEmitter.emit("connect", peripheralUuid, err)
      })
  }

  function discoverServices (peripheralUuid, serviceUuids) {
    const discoveredServiceUuids = _(devices[peripheralUuid].gattServices)
      .map("uuid")
      .map(stripDashes)
      .intersection(serviceUuids)
      .value()
    eventEmitter.emit("servicesDiscover", peripheralUuid, discoveredServiceUuids)
  }

  function discoverCharacteristics (peripheralUuid, serviceUuid, characteristicUuids) {
    const discoveredCharacteristicUuids = _(devices[peripheralUuid])
      .thru(device => device.getGattService(serviceUuid))
      .thru(service => service.getAllCharacteristics())
      .map("uuid")
      .map(stripDashes)
      .intersection(characteristicUuids)
      .map(characteristicUuid => ({uuid: characteristicUuid, properties: []}))
      .value()
    eventEmitter.emit("characteristicsDiscover", peripheralUuid, serviceUuid, discoveredCharacteristicUuids)
  }

  function read (peripheralUuid, serviceUuid, characteristicUuid) {
    devices[peripheralUuid].getGattService(serviceUuid).getCharacteristics(characteristicUuid)[0]
      .readValueAsync()
      .done(data => eventEmitter.emit("read", peripheralUuid, serviceUuid, characteristicUuid, data, false))
  }

  function write (peripheralUuid, serviceUuid, characteristicUuid, data, withoutResponse) {
    // TODO: what is withoutResponse for?
    devices[peripheralUuid].getGattService(serviceUuid).getCharacteristics(characteristicUuid)[0]
      .writeValueAsync(Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(data))
      .done(() => eventEmitter.emit("write", peripheralUuid, serviceUuid, characteristicUuid))
  }

  function notify (peripheralUuid, serviceUuid, characteristicUuid, notify) {
    // TODO: is there anything else to do here?
    eventEmitter.emit("notify", peripheralUuid, serviceUuid, characteristicUuid, notify)
  }
}

function lifecycle (eventEmitter) {
  return {init}

  function init () {
    process.nextTick(powerOn)
    process.on("SIGINT", onSigInt)
    process.on("exit", onProcessExit)
  }

  function powerOn () {
    debug("powered on")
    eventEmitter.emit("stateChange", "poweredOn")
  }

  function onSigInt () {
    // copied over from https://github.com/sandeepmistry/noble/blob/master/lib/hci-socket/bindings.js#L86
    var sigIntListeners = process.listeners("SIGINT")
    if (sigIntListeners[sigIntListeners.length - 1] === onSigInt) {
      // we are the last listener, so exit
      // this will trigger onProcessExit, and clean up
      process.exit(1)
    }
  }

  function onProcessExit () {
    uwp.close()
    debug("uwp closed")
  }
}

function bluetoothScanner (deviceWatcher, eventEmitter) {
  deviceWatcher.onNewDevice(onNewDevice)
  let scanning = false

  return {start, stop}

  function start () {
    scanning = true
    if (deviceWatcher.startable()) deviceWatcher.start()
  }

  function stop () {
    scanning = false
  }

  function onNewDevice (device) {
    if (!scanning) return
    if (device.id.indexOf("BTHLEDevice") < 0) return // is this really trust-worthy?
    Windows.Devices.Bluetooth.BluetoothLEDevice.fromIdAsync(device.id).done(function (bluetoothDevice) {
      debug("discovered Bluetoothe Low Energy device:", device.id)
      eventEmitter.emit("discover",
        bluetoothDevice.bluetoothAddress.toString(16),
        bluetoothDevice.address,
        bluetoothDevice.addressType)
    })
  }
}

function deviceWatcher (eventEmitter) {
  const windowsDeviceWatcher = Windows.Devices.Enumeration.DeviceInformation.createWatcher()
  windowsDeviceWatcher.addEventListener("added", device => eventEmitter.emit("onNewDevice", device))

  const statuses = {created: 0, started: 1, enumerationCompleted: 2, stopping: 3, stopped: 4, aborted: 5}
  const startableStatuses = _(statuses).pick("created", "stopped", "aborted").values()

  return {
    startable: () => _.includes(startableStatuses, windowsDeviceWatcher.status),
    start: () => windowsDeviceWatcher.start(),
    stop: () => windowsDeviceWatcher.stop(),
    onNewDevice: callback => eventEmitter.on("onNewDevice", callback),
  }
}

function stripDashes (uuid) {
  return uuid.splt("-").join("")
}
<|endoftext|>


<commit_before>var wizardsById = {};
var defaultId = '_defaultId';

Template.registerHelper('pathForStep', function(id) {
  var activeStep = this.wizard.activeStep(false);
  if (activeStep.id === id || !this.data() || this.wizard.indexOf(id) > this.wizard.indexOf(activeStep.id)) {
    return null;
  } if (!this.wizard.route) {
    return '#' + id;
  }

  return WizardRouter.path(this.wizard.route, id);
});

Template.wizard.created = function() {
  var id = this.data.id || defaultId;
  this.wizard = wizardsById[id] = new Wizard(this.data);
};

Template.wizard.destroyed = function() {
  var id = this.data.id || defaultId;

  if (wizardsById[id]) {
    wizardsById[id].destroy();
    delete wizardsById[id];
  }
};

Template.wizard.helpers({
  innerContext: function(outerContext) {
    var context = this
    , wizard = Template.instance().wizard;

    return _.extend({
      wizard: wizard,
      step: wizard.activeStep(),
    }, outerContext);
  },
  activeStepTemplate: function() {
    var activeStep = this.wizard.activeStep();
    return activeStep && (activeStep.template || this.wizard.stepTemplate) || null;
  }
});

Template.__wizard_steps.events({
  'click a': function(e, tpl) {
    if (!this.wizard.route) {
      e.preventDefault();
      this.wizard.show(this.id);
    }
  }
});

Template.__wizard_steps.helpers({
  activeStepClass: function(id) {
    var activeStep = this.wizard.activeStep();
    return (activeStep && activeStep.id == id) && 'active' || '';
  }
});

Template.wizardButtons.events({
  'click .wizard-back-button': function(e) {
    e.preventDefault();
    this.previous(AutoForm.getFormValues(this.activeStep(false).formId));
  }
});

Template.wizardButtons.helpers({
  showBackButton: function() {
    return this.backButton && !this.isFirstStep();
  }
});

var _options = [
  'id',
  'route',
  'steps',
  'stepsTemplate',
  'stepTemplate',
  'buttonClasses',
  'nextButton',
  'backButton',
  'confirmButton',
  'persist',
  'clearOnDestroy'
];

var _defaults = {
  stepsTemplate: '__wizard_steps',
  stepTemplate: '__wizard_step',
  nextButton: 'Next',
  backButton: 'Back',
  confirmButton: 'Confirm',
  persist: true
}

Wizard = function(options) {
  this._dep = new Tracker.Dependency();

  options = _.chain(options).pick(_options).defaults(_defaults).value();
  _.extend(this, options);

  this._stepsByIndex = [];
  this._stepsById = {};

  this.store = new CacheStore(this.id, {
    persist: this.persist !== false
  });

  this.initialize();
};

Wizard.get = function(id) {
  return wizardsById[id || defaultId];
};

Wizard.extendOptions = function(options, defaults) {
  _options = _options.concat(options);
  _.extend(_defaults, defaults);
};

Wizard.prototype = {

  constructor: Wizard,

  initialize: function() {
    var self = this;

    _.each(this.steps, function(step) {
      self._initStep(step);
    });

    this._comp = Tracker.autorun(function() {
      var step;
      if (self.route)
        step = WizardRouter.getStep();

      self._setActiveStep(step);
    });
  },

  _initStep: function(step) {
    var self = this;

    if (!step.id) {
      throw new Meteor.Error('step-id-required', 'Step.id is required');
    }

    if (!step.formId) {
      step.formId = step.id + '-form';
    }

    if (step.data) {
      this.setData(step.id, step.data);
    }

    this._stepsByIndex.push(step.id);
    this._stepsById[step.id] = _.extend(step, {
      wizard: self,
      data: function() {
        return self.store.get(step.id);
      }
    });

    AutoForm.addHooks(step.formId, {
      onSubmit: function(data) {
        this.event.preventDefault();

        if(step.onSubmit) {
          step.onSubmit.call(this, data, self);
        } else {
          self.next(data);
        }
      }
    }, true);
  },

  _setActiveStep: function(step) {
    // show the first step if not bound to a route
    if(!step) {
      return this.setStep(0);
    }

    var index = this.indexOf(step)
      , previousStep = this.getStep(index - 1);

    // initial route or non existing step, redirect to first step
    if(index === -1) {
      return this.setStep(0);
    }

    // invalid step
    if(index > 0 && previousStep && !previousStep.data()) {
      return this.setStep(0);
    }

    // valid
    this.setStep(step);
  },

  setData: function(id, data) {
    this.store.set(id, data);
  },

  clearData: function() {
    this.store.clear();
  },

  mergedData: function() {
    var data = {};
    _.each(this._stepsById, function(step) {
      _.extend(data, step.data());
    });
    return data;
  },

  next: function(data) {
    var activeIndex = _.indexOf(this._stepsByIndex, this._activeStepId);

    if(data) {
      this.setData(this._activeStepId, data);
    }

    this.show(activeIndex + 1);
  },

  previous: function(data) {
    var activeIndex = _.indexOf(this._stepsByIndex, this._activeStepId);

    if(data) {
     this.setData(this._activeStepId, data);
    }

    this.show(activeIndex - 1);
  },

  show: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    if(!id) return false;

    if(this.route) {
      WizardRouter.go(this.route, id);
    } else {
      this.setStep(id);
    }

    return true;
  },

  getStep: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    return id in this._stepsById && this._stepsById[id];
  },

  activeStep: function(reactive) {
    if(reactive !== false) {
      this._dep.depend();
    }
    return this._stepsById[this._activeStepId];
  },

  setStep: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    if(!id) return false;

    this._activeStepId = id;
    this._dep.changed();
    return this._stepsById[this._activeStepId];
  },

  isActiveStep: function(id) {
    return id === this._activeStepId;
  },

  isFirstStep: function(id) {
    id = id || this._activeStepId;
    return this.indexOf(id) === 0;
  },

  isLastStep: function(id) {
    id = id || this._activeStepId;
    return this.indexOf(id) === this._stepsByIndex.length - 1;
  },

  indexOf: function(id) {
    return _.indexOf(this._stepsByIndex, id);
  },

  destroy: function() {
    this._comp.stop();

    if(this.clearOnDestroy) this.clearData();
  }
};
<commit_msg>Fix back button to not lose current step data<commit_after>var wizardsById = {};
var defaultId = '_defaultId';

Template.registerHelper('pathForStep', function(id) {
  var activeStep = this.wizard.activeStep(false);
  if (activeStep.id === id || !this.data() || this.wizard.indexOf(id) > this.wizard.indexOf(activeStep.id)) {
    return null;
  } if (!this.wizard.route) {
    return '#' + id;
  }

  return WizardRouter.path(this.wizard.route, id);
});

Template.wizard.created = function() {
  var id = this.data.id || defaultId;
  this.wizard = wizardsById[id] = new Wizard(this.data);
};

Template.wizard.destroyed = function() {
  var id = this.data.id || defaultId;

  if (wizardsById[id]) {
    wizardsById[id].destroy();
    delete wizardsById[id];
  }
};

Template.wizard.helpers({
  innerContext: function(outerContext) {
    var context = this
    , wizard = Template.instance().wizard;

    return _.extend({
      wizard: wizard,
      step: wizard.activeStep(),
    }, outerContext);
  },
  activeStepTemplate: function() {
    var activeStep = this.wizard.activeStep();
    return activeStep && (activeStep.template || this.wizard.stepTemplate) || null;
  }
});

Template.__wizard_steps.events({
  'click a': function(e, tpl) {
    if (!this.wizard.route) {
      e.preventDefault();
      this.wizard.show(this.id);
    }
  }
});

Template.__wizard_steps.helpers({
  activeStepClass: function(id) {
    var activeStep = this.wizard.activeStep();
    return (activeStep && activeStep.id == id) && 'active' || '';
  }
});

Template.wizardButtons.events({
  'click .wizard-back-button': function(e) {
    e.preventDefault();
    this.previous(AutoForm.getFormValues(this.activeStep(false).formId).insertDoc);
  }
});

Template.wizardButtons.helpers({
  showBackButton: function() {
    return this.backButton && !this.isFirstStep();
  }
});

var _options = [
  'id',
  'route',
  'steps',
  'stepsTemplate',
  'stepTemplate',
  'buttonClasses',
  'nextButton',
  'backButton',
  'confirmButton',
  'persist',
  'clearOnDestroy'
];

var _defaults = {
  stepsTemplate: '__wizard_steps',
  stepTemplate: '__wizard_step',
  nextButton: 'Next',
  backButton: 'Back',
  confirmButton: 'Confirm',
  persist: true
}

Wizard = function(options) {
  this._dep = new Tracker.Dependency();

  options = _.chain(options).pick(_options).defaults(_defaults).value();
  _.extend(this, options);

  this._stepsByIndex = [];
  this._stepsById = {};

  this.store = new CacheStore(this.id, {
    persist: this.persist !== false
  });

  this.initialize();
};

Wizard.get = function(id) {
  return wizardsById[id || defaultId];
};

Wizard.extendOptions = function(options, defaults) {
  _options = _options.concat(options);
  _.extend(_defaults, defaults);
};

Wizard.prototype = {

  constructor: Wizard,

  initialize: function() {
    var self = this;

    _.each(this.steps, function(step) {
      self._initStep(step);
    });

    this._comp = Tracker.autorun(function() {
      var step;
      if (self.route)
        step = WizardRouter.getStep();

      self._setActiveStep(step);
    });
  },

  _initStep: function(step) {
    var self = this;

    if (!step.id) {
      throw new Meteor.Error('step-id-required', 'Step.id is required');
    }

    if (!step.formId) {
      step.formId = step.id + '-form';
    }

    if (step.data) {
      this.setData(step.id, step.data);
    }

    this._stepsByIndex.push(step.id);
    this._stepsById[step.id] = _.extend(step, {
      wizard: self,
      data: function() {
        return self.store.get(step.id);
      }
    });

    AutoForm.addHooks(step.formId, {
      onSubmit: function(data) {
        this.event.preventDefault();

        if(step.onSubmit) {
          step.onSubmit.call(this, data, self);
        } else {
          self.next(data);
        }
      }
    }, true);
  },

  _setActiveStep: function(step) {
    // show the first step if not bound to a route
    if(!step) {
      return this.setStep(0);
    }

    var index = this.indexOf(step)
      , previousStep = this.getStep(index - 1);

    // initial route or non existing step, redirect to first step
    if(index === -1) {
      return this.setStep(0);
    }

    // invalid step
    if(index > 0 && previousStep && !previousStep.data()) {
      return this.setStep(0);
    }

    // valid
    this.setStep(step);
  },

  setData: function(id, data) {
    this.store.set(id, data);
  },

  clearData: function() {
    this.store.clear();
  },

  mergedData: function() {
    var data = {};
    _.each(this._stepsById, function(step) {
      _.extend(data, step.data());
    });
    return data;
  },

  next: function(data) {
    var activeIndex = _.indexOf(this._stepsByIndex, this._activeStepId);

    if(data) {
      this.setData(this._activeStepId, data);
    }

    this.show(activeIndex + 1);
  },

  previous: function(data) {
    var activeIndex = _.indexOf(this._stepsByIndex, this._activeStepId);

    if(data) {
     this.setData(this._activeStepId, data);
    }

    this.show(activeIndex - 1);
  },

  show: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    if(!id) return false;

    if(this.route) {
      WizardRouter.go(this.route, id);
    } else {
      this.setStep(id);
    }

    return true;
  },

  getStep: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    return id in this._stepsById && this._stepsById[id];
  },

  activeStep: function(reactive) {
    if(reactive !== false) {
      this._dep.depend();
    }
    return this._stepsById[this._activeStepId];
  },

  setStep: function(id) {
    if(typeof id === 'number') {
      id = id in this._stepsByIndex && this._stepsByIndex[id];
    }

    if(!id) return false;

    this._activeStepId = id;
    this._dep.changed();
    return this._stepsById[this._activeStepId];
  },

  isActiveStep: function(id) {
    return id === this._activeStepId;
  },

  isFirstStep: function(id) {
    id = id || this._activeStepId;
    return this.indexOf(id) === 0;
  },

  isLastStep: function(id) {
    id = id || this._activeStepId;
    return this.indexOf(id) === this._stepsByIndex.length - 1;
  },

  indexOf: function(id) {
    return _.indexOf(this._stepsByIndex, id);
  },

  destroy: function() {
    this._comp.stop();

    if(this.clearOnDestroy) this.clearData();
  }
};
<|endoftext|>


<commit_before>define(function(require, module, exports) {
    main.consumes = ["Plugin", "ui", "Dialog"];
    main.provides = ["Wizard", "WizardPage"];
    return main;

    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var Dialog = imports.Dialog;
        var ui = imports.ui;

        /***** Initialization *****/

        function Wizard(developer, deps, options) {
            var plugin = new Dialog(developer, deps.concat(main.consumes), {
                name: "dialog.wizard",
                allowClose: options.allowClose || false,
                title: options.title,
                modal: true,
                custom: true,
                elements: [
                    { type: "button", id: "cancel", caption: "Cancel", visible: false, onclick: cancel },
                    { type: "filler" },
                    { type: "button", id: "previous", caption: "Previous", visible: false, onclick: previous },
                    { type: "button", id: "next", caption: "Next", color: "green", "default": true, onclick: next },
                    { type: "button", id: "finish", caption: "Finish", visible: false, onclick: finish }
                ]
            });

            var emit = plugin.getEmitter();

            var path = [];
            var current = -1;
            var body, startPage, lastPage;

            var drawn = false;
            function draw(options) {
                if (drawn) return;
                drawn = true;

                body = { html: document.createElement("div") };
                options.html.parentNode.replaceChild(body.html, options.html);
            }

            /***** Methods *****/

            function cancel(){
                plugin.hide();
                emit("cancel", { activePage: lastPage });
            }

            function previous(){
                current--;
                activate(path[current]);
                emit("previous");
            }

            function next(){
                path.splice(current + 1);
                var page = emit("next", {
                    activePage: path[path.length - 1]
                });
                current = path.push(page) - 1;

                activate(page);
            }

            function finish(){
                plugin.hide();
                emit("finish", { activePage: lastPage });
            }

            function activate(page) {
                var idx = path.indexOf(page);
                if (idx == -1) throw new Error();

                plugin.update([
                    { id: "previous", visible: idx > 0 },
                    { id: "next", visible: !page.last },
                    { id: "finish", visible: page.last }
                ]);

                if (lastPage)
                    lastPage.hide();
                page.show(body);

                lastPage = page;
            }

            function show(reset, options) {
                if (!options)
                    options = {};

                return plugin.queue(function(){
                    if (reset || current == -1) {
                        path = [startPage];
                        current = 0;
                        activate(startPage);
                    }

                }, options.queue === false);
            }

            /***** Lifecycle *****/

            plugin.on("draw", function(options) {
                draw(options);
            });

            /***** Register and define API *****/

            plugin.freezePublicAPI.baseclass();

            /**
             *
             */
            plugin.freezePublicAPI({
                /**
                 *
                 */
                get activePage(){ return path[current]; },

                /**
                 *
                 */
                get startPage(){ return startPage; },
                set startPage(v){ startPage = v; },

                /**
                 *
                 */
                get showCancel(){
                    return plugin.getElement("cancel").visible;
                },
                set showCancel(value) {
                    plugin.update([
                        { id: "cancel", visible: value }
                    ]);
                },

                /**
                 *
                 */
                get showFinish(){
                    return plugin.getElement("finish").visible;
                },
                set showFinish(value) {
                    plugin.update([
                        { id: "finish", visible: value }
                    ]);
                },

                /**
                 *
                 */
                show: show,

                /**
                 *
                 */
                cancel: cancel,

                /**
                 *
                 */
                previous: previous,

                /**
                 *
                 */
                next: next,

                /**
                 *
                 */
                finish: finish
            });

            return plugin;
        }

        function WizardPage(options) {
            var plugin = new Plugin("Ajax.org", main.consumes);
            var emit = plugin.getEmitter();

            var name = options.name;
            var last = options.last;
            var container;

            var drawn;
            function draw(){
                if (drawn) return;
                drawn = true;

                container = document.createElement("div");
                plugin.addOther(function(){
                    container.parentNode.removeChild(container);
                });

                emit.sticky("draw", { html: container });
            }

            /***** Methods *****/

            function hide(){
                container.parentNode.removeChild(container);
            }

            function show(options) {
                draw();
                options.html.appendChild(container);
            }

            /***** Register and define API *****/

            plugin.freezePublicAPI.baseclass();

            /**
             *
             */
            plugin.freezePublicAPI({
                /**
                 *
                 */
                get name(){ return name; },

                /**
                 *
                 */
                get container(){ return container; },

                /**
                 *
                 */
                get last(){ return last; },
                set last(v){ last = v; },

                /**
                 *
                 */
                get allowClose(){ return plugin.allowClose; },
                set allowClose(v){ plugin.allowClose = v; },

                /**
                 *
                 */
                show: show,

                /**
                 *
                 */
                hide: hide
            });

            return plugin;
        }

        register("", {
            "Wizard" : Wizard,
            "WizardPage" : WizardPage,
        });
    }
});<commit_msg>cleanup<commit_after>define(function(require, module, exports) {
    main.consumes = ["Plugin", "ui", "Dialog"];
    main.provides = ["Wizard", "WizardPage"];
    return main;

    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var Dialog = imports.Dialog;
        var ui = imports.ui;

        /***** Initialization *****/

        function Wizard(developer, deps, options) {
            var plugin = new Dialog(developer, deps.concat(main.consumes), {
                name: "dialog.wizard",
                allowClose: options.allowClose || false,
                title: options.title,
                modal: true,
                custom: true,
                elements: [
                    { type: "button", id: "cancel", caption: "Cancel", visible: false, onclick: cancel },
                    { type: "filler" },
                    { type: "button", id: "previous", caption: "Previous", visible: false, onclick: previous },
                    { type: "button", id: "next", caption: "Next", color: "green", "default": true, onclick: next },
                    { type: "button", id: "finish", caption: "Finish", visible: false, onclick: finish }
                ]
            });

            var emit = plugin.getEmitter();

            var path = [];
            var current = -1;
            var body, startPage, lastPage;

            var drawn = false;
            function draw(options) {
                if (drawn) return;
                drawn = true;

                body = { html: document.createElement("div") };
                options.html.parentNode.replaceChild(body.html, options.html);
            }

            /***** Methods *****/

            function cancel(){
                plugin.hide();
                emit("cancel", { activePage: lastPage });
            }

            function previous(){
                current--;
                activate(path[current]);
                emit("previous");
            }

            function next(){
                path.splice(current + 1);
                var page = emit("next", {
                    activePage: path[path.length - 1]
                });
                current = path.push(page) - 1;

                activate(page);
            }

            function finish(){
                plugin.hide();
                emit("finish", { activePage: lastPage });
            }

            function activate(page) {
                var idx = path.indexOf(page);
                if (idx == -1) throw new Error();

                plugin.update([
                    { id: "previous", visible: idx > 0 },
                    { id: "next", visible: !page.last },
                    { id: "finish", visible: page.last }
                ]);

                if (lastPage)
                    lastPage.hide();
                page.show(body);

                lastPage = page;
            }

            function show(reset, options) {
                if (!options)
                    options = {};

                return plugin.queue(function(){
                    if (reset || current == -1) {
                        path = [startPage];
                        current = 0;
                        activate(startPage);
                    }

                }, options.queue === false);
            }

            /***** Lifecycle *****/

            plugin.on("draw", function(options) {
                draw(options);
            });

            /***** Register and define API *****/

            plugin.freezePublicAPI.baseclass();

            /**
             *
             */
            plugin.freezePublicAPI({
                /**
                 *
                 */
                get activePage(){ return path[current]; },

                /**
                 *
                 */
                get startPage(){ return startPage; },
                set startPage(v){ startPage = v; },

                /**
                 *
                 */
                get showCancel(){
                    return plugin.getElement("cancel").visible;
                },
                set showCancel(value) {
                    plugin.update([
                        { id: "cancel", visible: value }
                    ]);
                },

                /**
                 *
                 */
                get showFinish(){
                    return plugin.getElement("finish").visible;
                },
                set showFinish(value) {
                    plugin.update([
                        { id: "finish", visible: value }
                    ]);
                },

                /**
                 *
                 */
                show: show,

                /**
                 *
                 */
                cancel: cancel,

                /**
                 *
                 */
                previous: previous,

                /**
                 *
                 */
                next: next,

                /**
                 *
                 */
                finish: finish
            });

            return plugin;
        }

        function WizardPage(options) {
            var plugin = new Plugin("Ajax.org", main.consumes);
            var emit = plugin.getEmitter();

            var name = options.name;
            var last = options.last;
            var container;

            var drawn;
            function draw(){
                if (drawn) return;
                drawn = true;

                container = document.createElement("div");
                plugin.addOther(function(){
                    container.parentNode.removeChild(container);
                });

                emit.sticky("draw", { html: container });
            }

            /***** Methods *****/

            function hide(){
                container.parentNode.removeChild(container);
            }

            function show(options) {
                draw();
                options.html.appendChild(container);
            }

            /***** Register and define API *****/

            plugin.freezePublicAPI.baseclass();

            /**
             *
             */
            plugin.freezePublicAPI({
                /**
                 *
                 */
                get name(){ return name; },

                /**
                 *
                 */
                get container(){ return container; },

                /**
                 *
                 */
                get last(){ return last; },
                set last(v){ last = v; },

                /**
                 *
                 */
                show: show,

                /**
                 *
                 */
                hide: hide
            });

            return plugin;
        }

        register("", {
            "Wizard" : Wizard,
            "WizardPage" : WizardPage,
        });
    }
});<|endoftext|>


<commit_before>var utility = require('./utility');
var processors = require('./processors');
var getData = utility.getData;
var db = require('./db');
var r = require('./redis');
var redis = r.client;
var jobs = r.jobs;
var kue = r.kue;
var logger = utility.logger;
var generateJob = utility.generateJob;
var async = require('async');
var operations = require('./operations');
var insertMatch = operations.insertMatch;
var queueReq = operations.queueReq;
var fullhistory = require('./tasks/fullhistory');
var updatenames = require('./tasks/updatenames');
var selector = require('./selector');
var domain = require('domain');

var trackedPlayers = {};
var ratingPlayers = {};

var d = domain.create();
d.on('error', function() {
    clearActiveJobs(function(err) {
        process.exit(err);
    });
});
d.run(function() {
    console.log("[WORKER] starting worker");
    build(function() {
        startScan();
        jobs.promote();
        jobs.process('api', processors.processApi);
        jobs.process('mmr', processors.processMmr);
        setInterval(fullhistory, 60 * 60 * 1000, function() {});
        setInterval(updatenames, 5 * 60 * 1000, function() {});
        setInterval(build, 5 * 60 * 1000, function() {});
    });
});

function build(cb) {
    console.log("rebuilding sets");
    db.players.find(selector("tracked"), function(err, docs) {
        if (err) {
            return build(cb);
        }
        var t = {};
        var r = {};
        var b = [];
        docs.forEach(function(player) {
            t[player.account_id] = true;
        });
        async.map(utility.getRetrieverUrls(), function(url, cb) {
            getData(url, function(err, body) {
                for (var key in body.accounts) {
                    b.push(body.accounts[key]);
                }
                for (var key in body.accountToIdx) {
                    r[key] = url + "?account_id=" + key;
                }
                cb(err);
            });
        }, function(err) {
            if (err) {
                return build(cb);
            }
            trackedPlayers = t;
            ratingPlayers = r;
            redis.set("bots", JSON.stringify(b));
            redis.set("ratingPlayers", JSON.stringify(r));
            redis.set("trackedPlayers", JSON.stringify(t));
            return cb(err);
        });
    });
}

function startScan() {
    if (process.env.START_SEQ_NUM === "AUTO") {
        var container = generateJob("api_history", {});
        getData(container.url, function(err, data) {
            if (err) {
                return startScan();
            }
            scanApi(data.result.matches[0].match_seq_num);
        });
    }
    else if (process.env.START_SEQ_NUM) {
        scanApi(process.env.START_SEQ_NUM);
    }
    else {
        redis.get("match_seq_num", function(err, result) {
            if (!err && result) {
                scanApi(result);
            }
            else {
                return startScan();
            }
        });
    }
}

function clearActiveJobs(cb) {
    jobs.active(function(err, ids) {
        if (err) {
            return cb(err);
        }
        async.mapSeries(ids, function(id, cb) {
            kue.Job.get(id, function(err, job) {
                if (err) {
                    return cb(err);
                }
                if ((new Date() - job.updated_at) > 5 * 60 * 1000) {
                    console.log("unstuck job %s", id);
                    job.inactive();
                }
                cb(err);
            });
        }, function(err) {
            cb(err);
        });
    });
}

function scanApi(seq_num) {
    var container = generateJob("api_sequence", {
        start_at_match_seq_num: seq_num
    });
    getData(container.url, function(err, data) {
        if (err) {
            return scanApi(seq_num);
        }
        var resp = data.result.matches;
        logger.info("[API] seq_num: %s, found %s matches", seq_num, resp.length);
        async.mapSeries(resp, function(match, cb) {
            var tracked = false;
            async.map(match.players, function(p, cb) {
                if (p.account_id in trackedPlayers) {
                    tracked = true;
                }
                if (p.account_id in ratingPlayers && match.lobby_type === 7) {
                    queueReq("mmr", {
                        match_id: match.match_id,
                        account_id: p.account_id,
                        url: ratingPlayers[p.account_id]
                    }, function(err) {
                        cb(err);
                    });
                }
                else {
                    cb();
                }
            }, function(err) {
                //done looping players
                if (tracked) {
                    insertMatch(match, function(err) {
                        cb(err);
                    });
                }
                else {
                    cb(err);
                }
            });
        }, function(err) {
            if (!err && resp.length) {
                seq_num = resp[resp.length - 1].match_seq_num + 1;
                redis.set("match_seq_num", seq_num);
                //wait 100ms for each match less than 100
                var delay = (100 - resp.length) * 100;
                return setTimeout(function() {
                    scanApi(seq_num);
                }, delay);
            }
            else {
                return scanApi(seq_num);
            }
        });
    });
}
<commit_msg>requeueing active jobs on exit<commit_after>var utility = require('./utility');
var processors = require('./processors');
var getData = utility.getData;
var db = require('./db');
var r = require('./redis');
var redis = r.client;
var jobs = r.jobs;
var kue = r.kue;
var logger = utility.logger;
var generateJob = utility.generateJob;
var async = require('async');
var operations = require('./operations');
var insertMatch = operations.insertMatch;
var queueReq = operations.queueReq;
var fullhistory = require('./tasks/fullhistory');
var updatenames = require('./tasks/updatenames');
var selector = require('./selector');
var domain = require('domain');

var trackedPlayers = {};
var ratingPlayers = {};

var d = domain.create();
d.on('error', function() {
    clearActiveJobs(function(err) {
        process.exit(err);
    });
});
d.run(function() {
    console.log("[WORKER] starting worker");
    build(function() {
        startScan();
        jobs.promote();
        jobs.process('api', processors.processApi);
        jobs.process('mmr', processors.processMmr);
        setInterval(fullhistory, 60 * 60 * 1000, function() {});
        setInterval(updatenames, 5 * 60 * 1000, function() {});
        setInterval(build, 5 * 60 * 1000, function() {});
    });
});

function build(cb) {
    console.log("rebuilding sets");
    db.players.find(selector("tracked"), function(err, docs) {
        if (err) {
            return build(cb);
        }
        var t = {};
        var r = {};
        var b = [];
        docs.forEach(function(player) {
            t[player.account_id] = true;
        });
        async.map(utility.getRetrieverUrls(), function(url, cb) {
            getData(url, function(err, body) {
                for (var key in body.accounts) {
                    b.push(body.accounts[key]);
                }
                for (var key in body.accountToIdx) {
                    r[key] = url + "?account_id=" + key;
                }
                cb(err);
            });
        }, function(err) {
            if (err) {
                return build(cb);
            }
            trackedPlayers = t;
            ratingPlayers = r;
            redis.set("bots", JSON.stringify(b));
            redis.set("ratingPlayers", JSON.stringify(r));
            redis.set("trackedPlayers", JSON.stringify(t));
            return cb(err);
        });
    });
}

function startScan() {
    if (process.env.START_SEQ_NUM === "AUTO") {
        var container = generateJob("api_history", {});
        getData(container.url, function(err, data) {
            if (err) {
                return startScan();
            }
            scanApi(data.result.matches[0].match_seq_num);
        });
    }
    else if (process.env.START_SEQ_NUM) {
        scanApi(process.env.START_SEQ_NUM);
    }
    else {
        redis.get("match_seq_num", function(err, result) {
            if (!err && result) {
                scanApi(result);
            }
            else {
                return startScan();
            }
        });
    }
}

function clearActiveJobs(cb) {
    jobs.active(function(err, ids) {
        if (err) {
            return cb(err);
        }
        async.mapSeries(ids, function(id, cb) {
            kue.Job.get(id, function(err, job) {
                if (err) {
                    return cb(err);
                }
                console.log("requeued job %s", id);
                job.inactive();
                cb(err);
            });
        }, function(err) {
            cb(err);
        });
    });
}

function scanApi(seq_num) {
    var container = generateJob("api_sequence", {
        start_at_match_seq_num: seq_num
    });
    getData(container.url, function(err, data) {
        if (err) {
            return scanApi(seq_num);
        }
        var resp = data.result.matches;
        logger.info("[API] seq_num: %s, found %s matches", seq_num, resp.length);
        async.mapSeries(resp, function(match, cb) {
            var tracked = false;
            async.map(match.players, function(p, cb) {
                if (p.account_id in trackedPlayers) {
                    tracked = true;
                }
                if (p.account_id in ratingPlayers && match.lobby_type === 7) {
                    queueReq("mmr", {
                        match_id: match.match_id,
                        account_id: p.account_id,
                        url: ratingPlayers[p.account_id]
                    }, function(err) {
                        cb(err);
                    });
                }
                else {
                    cb();
                }
            }, function(err) {
                //done looping players
                if (tracked) {
                    insertMatch(match, function(err) {
                        cb(err);
                    });
                }
                else {
                    cb(err);
                }
            });
        }, function(err) {
            if (!err && resp.length) {
                seq_num = resp[resp.length - 1].match_seq_num + 1;
                redis.set("match_seq_num", seq_num);
                //wait 100ms for each match less than 100
                var delay = (100 - resp.length) * 100;
                return setTimeout(function() {
                    scanApi(seq_num);
                }, delay);
            }
            else {
                return scanApi(seq_num);
            }
        });
    });
}
<|endoftext|>


<commit_before>const moment        = require('moment');
const cheerio       = require('cheerio');
const fetch         = require('node-fetch');
const SpotifyWebApi = require('spotify-web-api-node');

const now = moment();
const then = moment().subtract(1, 'week');

const user = process.env.SPOTIFY_USER_ID;
const playlist = process.env.SPOTIFY_PLAYLIST_ID;

const spotifyApi = new SpotifyWebApi({
  "clientId": process.env.SPOTIFY_CLIENT_ID,
  "clientSecret": process.env.SPOTIFY_CLIENT_SECRET,
  "redirectUri": process.env.SPOTIFY_REDIRECT_URI,
  'refreshToken' : process.env.SPOTIFY_REFRESH_TOKEN
});

const refreshAccessToken = async () => {
  const { body: { access_token: accessToken } } = await spotifyApi.refreshAccessToken();
  spotifyApi.setAccessToken(accessToken);
};

const cleanUpMeta = meta => {
  return meta
    .replace(/\[.*?\]/g, '')
    .replace(' & ', ' ')
    .replace('(Album Version)', '')
    .replace('(Single Edit)', '')
    .replace('(Radio Edit)', '')
    .replace('- Single', '')
    .trim();
};

const fetchEpisodes = async () => {
  const html = await fetch('http://www.marketplace.org/latest-music').then(res => res.text());
  const $ = cheerio.load(html);
  return $('.episode-music').map((i, episode) => {
    const dateParts = $(episode).prev().text().split(':')[0].split('/');
    const tracks = $(episode).children().map((i, track) => {
      const title = cleanUpMeta($(track).find('.episode-music-title').text());
      const artist = cleanUpMeta($(track).find('.episode-music-artist').text());
      return { title, artist };
    }).get();
    return { date: `${dateParts[2]}-${dateParts[0]}-${dateParts[1]}`, tracks };
  }).get();
};

const getTracks = episodes => {
  return episodes
    .filter(episode => moment(episode.date).isBetween(then, now, 'day', []))
    .reduce((tracks, episode) => [ ...tracks, ...episode.tracks ], []).sort();
};

const searchTrack = (title, artist, query, deep) => {
  return spotifyApi.searchTracks(query, { limit: 50 })
    .then(({ body: { tracks } }) => ({ title, artist, tracks }));
};

const searchTracks = tracks => {
  const searches = tracks.map(({ title, artist }) => searchTrack(title, artist, `track:${title} artist:${artist}`, true));
  return Promise.all(searches);
};

const addTracks = async results => {
  const uris = results.reduce((ids, {tracks}) => (tracks.total > 0) ? [ ...ids, tracks.items[0].uri ] : [ ...ids ], []);
  return spotifyApi.replaceTracksInPlaylist(user, playlist, uris);
};

const init = async () => {
  if (now.isoWeekday() === 3) {
    await refreshAccessToken();
    const episodes = await fetchEpisodes();
    const tracks = getTracks(episodes);
    const results = await searchTracks(tracks);
    addTracks(results);
  }
};

init();
<commit_msg>sick update bro!<commit_after>const moment        = require('moment');
const cheerio       = require('cheerio');
const fetch         = require('node-fetch');
const SpotifyWebApi = require('spotify-web-api-node');

const now = moment();
const then = moment().subtract(1, 'week');

const user = process.env.SPOTIFY_USER_ID;
const playlist = process.env.SPOTIFY_PLAYLIST_ID;

const spotifyApi = new SpotifyWebApi({
  "clientId": process.env.SPOTIFY_CLIENT_ID,
  "clientSecret": process.env.SPOTIFY_CLIENT_SECRET,
  "redirectUri": process.env.SPOTIFY_REDIRECT_URI,
  'refreshToken' : process.env.SPOTIFY_REFRESH_TOKEN
});

const refreshAccessToken = async () => {
  const { body: { access_token: accessToken } } = await spotifyApi.refreshAccessToken();
  spotifyApi.setAccessToken(accessToken);
};

const cleanUpMeta = meta => {
  return meta
    .replace(/\[.*?\]/g, '')
    .replace(' & ', ' ')
    .replace('(Album Version)', '')
    .replace('(Single Edit)', '')
    .replace('(Radio Edit)', '')
    .replace('- Single', '')
    .trim();
};

const fetchEpisodes = async () => {
  const html = await fetch('http://www.marketplace.org/latest-music').then(res => res.text());
  const $ = cheerio.load(html);
  return $('.episode-music').map((i, episode) => {
    const dateParts = $(episode).prev().text().split(':')[0].split('/');
    const tracks = $(episode).children().map((i, track) => {
      const title = cleanUpMeta($(track).find('.episode-music-title').text());
      const artist = cleanUpMeta($(track).find('.episode-music-artist').text());
      return { title, artist };
    }).get();
    return { date: `${dateParts[2]}-${dateParts[0]}-${dateParts[1]}`, tracks };
  }).get();
};

const getTracks = episodes => {
  return episodes
    .filter(episode => moment(episode.date).isBetween(then, now, 'day', []))
    .reduce((tracks, episode) => [ ...tracks, ...episode.tracks ], []).sort();
};

const searchTrack = (title, artist, query, deep) => {
  return spotifyApi.searchTracks(query, { limit: 50 })
    .then(({ body: { tracks } }) => ({ title, artist, tracks }));
};

const searchTracks = tracks => {
  const searches = tracks.map(({ title, artist }) => searchTrack(title, artist, `track:${title} artist:${artist}`, true));
  return Promise.all(searches);
};

const addTracks = async results => {
  const uris = results.reduce((ids, {tracks}) => (tracks.total > 0) ? [ ...ids, tracks.items[0].uri ] : [ ...ids ], []);
  return spotifyApi.replaceTracksInPlaylist(user, playlist, uris);
};

const init = async () => {
  if (now.isoWeekday() === 3) {
    await refreshAccessToken();
    const episodes = await fetchEpisodes();
    const tracks = getTracks(episodes);
    const results = await searchTracks(tracks);
    addTracks(results);
  }
};

init();
<|endoftext|>


<commit_before>require('babel-register')
var skiff = require('./lib/skiff') // this must be first
require('./config/kue') // this must be second

var _ = require('lodash')
var Promise = require('bluebird')
var queue = require('kue').createQueue()
var rollbar = skiff.rollbar
var sails = skiff.sails

// define new jobs here.
// each job should return a promise.
// use Promise.method if the job is synchronous.
//
// use the classMethod job to run any class method that takes a single hash argument.
//
var jobDefinitions = {
  test: Promise.method(function (job) {
    console.log(new Date().toString().magenta)
    throw new Error('whoops!')
  }),

  classMethod: function (job) {
    sails.log.debug(format('Job %s: %s.%s', job.id, job.data.className, job.data.methodName))
    return global[job.data.className][job.data.methodName](_.omit(job.data, 'className', 'methodName'))
  }
}

var processJobs = function () {
  // load jobs
  _.forIn(jobDefinitions, function (promise, name) {
    queue.process(name, 10, function (job, ctx, done) {
      // put common behavior for all jobs here

      var label = `Job ${job.id}: `
      sails.log.debug(label + name)

      promise(job).then(function () {
        sails.log.debug(label + 'done')
        done()
      })
      .catch(function (err) {
        const error = typeof err === 'string' ? new Error(err) : err
        if (error) {
          sails.log.error(label + error.message.red)
          rollbar.handleError(error)
          done(error)
        } else {
          const newError = new Error('kue job failed without error')
          const data = {custom: {jobData: job.data}}
          rollbar.handleErrorWithPayloadData(newError, data)
          done(newError)
        }
      })
    })
  })
}

setTimeout(() => {
  skiff.lift({
    start: processJobs,
    stop: done => {
      queue.shutdown(5000, done)
    }
  })
}, Number(process.env.DELAY_START || 0) * 1000)
<commit_msg>handle bad background jobs better<commit_after>require('babel-register') // this must be first
const skiff = require('./lib/skiff') // this must be second
require('./config/kue') // this must be third

const Promise = require('bluebird')
const queue = require('kue').createQueue()
const rollbar = skiff.rollbar
const sails = skiff.sails
const lodash = require('lodash')
const { forIn, omit } = lodash

// define new jobs here.
// each job should return a promise.
// use Promise.method if the job is synchronous.
//
// use the classMethod job to run any class method that takes a single hash argument.
//
var jobDefinitions = {
  test: Promise.method(function (job) {
    console.log(new Date().toString().magenta)
    throw new Error('whoops!')
  }),

  classMethod: function (job) {
    const { id, data, data: { className, methodName } } = job
    sails.log.debug(`Job ${id}: ${className}.${methodName}`)
    const fn = global[className][methodName]

    // we wrap the method call in a promise so that if it throws an error
    // immediately, e.g. if the method is not a function, the catch below will
    // handle it
    return Promise.resolve()
    .then(() => fn(omit(data, 'className', 'methodName')))
  }
}

var processJobs = function () {
  // load jobs
  forIn(jobDefinitions, function (promise, name) {
    queue.process(name, 10, function (job, ctx, done) {
      // put common behavior for all jobs here

      var label = `Job ${job.id}: `
      sails.log.debug(label + name)

      promise(job).then(function () {
        sails.log.debug(label + 'done')
        done()
      })
      .catch(err => {
        const data = {custom: {jobData: job.data}}
        const error = typeof err === 'string'
          ? new Error(err)
          : (err || new Error('kue job failed without error'))
        sails.log.error(label + error.message.red)
        rollbar.handleErrorWithPayloadData(error, data)
        done(error)
      })
    })
  })
}

setTimeout(() => {
  skiff.lift({
    start: processJobs,
    stop: done => {
      queue.shutdown(5000, done)
    }
  })
}, Number(process.env.DELAY_START || 0) * 1000)
<|endoftext|>


<commit_before>"use strict";

(function () {

	// ----------------- ES5 features for older browsers ----------------- //
	if (!Array.prototype.forEach) {
		Array.prototype.forEach =  function(block, thisObject) {
			var len = this.length > 0;
			for (var i = 0; i < len; i++)
				if (i in this) block.call(thisObject, this[i], i, this);
		};
	}

	// ----------------- private api ----------------- //
	var wrench	= {},
			routing = {routes: {}},
			w 			= window,
			d 			= document;

	// Routing
	routing.changeRoute = function (route, params) {
		var routeUrl 	= route,
				i 				= 1;
		if (typeof params !== 'undefined') {
			routeUrl += ":";
	  	for (var param in params) {
	    	if (params.hasOwnProperty(param)) {
					if (i != 1) routeUrl += "+";
					routeUrl += param + "=" + params[param];
					i++;
				}
			}
		}

		if (w.location.hash != "" &&
				w.location.hash != "#" &&
				w.location.hash.indexOf(route) == -1) {
			route = w.location.hash + ";" + routeUrl;
		}
		else {
			route = routeUrl;
		}

		w.location.hash = route;
	};

	routing.locate = function () {
		var currentRoutes = w.location.hash.replace("#", "").split(";"),
				params 				= {},
				route 				= '';

		if (currentRoutes.length > 0 && currentRoutes[0] != "") {

			currentRoutes.forEach(function (routeString) {
				var rawParams = routeString.split(":");
				func = rawParams.shift();
				if (rawParams.length > 0 && rawParams[0] != undefined) {
					rawParams[0].split("+").forEach(function (param) {
						params[param.split("=")[0]] = param.split("=")[1]
					});
				}

				if (func in routing.routes) routing.routes[func](params);
			});

		}
		else if ("/" in routing.routes) {
			routing.routes["/"]();
		}

	};

	// wrench application object
	var wrenchApp = {
		callRoute: function (route, params) {
			routing.changeRoute(route, params);
		},
		run: function () {
			var app = this;

			window.addEventListener('load', function () {
				routing.locate();
				if (typeof app.init === 'function') app.init();
			}, false);
			// window.onhashchange = routing.locate;

			return app;
		}
  };

	// ----------------- public api ----------------- //
	wrench.VERSION = "0.0.1";

	// Turn an object into a wrench application
	wrench.appify = function (properties) {
		function F() {}
		F.prototype = wrenchApp;
		var app = new F();
  	for (var prop in properties)
    	if (properties.hasOwnProperty(prop))
      	app[prop] = properties[prop];
		return app;
	};

	// connects a route to a function
	// can be used like so: var users = route("users", function () {});
	// or: var users = route("users").to(function () {});
	// all routed functions will be called with a params hash
	//  as the only argument
	window.route = function (route, func) {
		if (typeof func === 'function') {
			routing.routes[route] = func;
			return function (params) {
				routing.changeRoute(route, params);
				func(params);
			};
		}
		else {
			return {
					to: function (func) {
						routing.routes[route] = func;
						return function (params) {
							routing.changeRoute(route, params);
							func(params);
						};
					}
				};
		}
	};

	window.wrench = wrench;
}());<commit_msg>working out indention<commit_after>"use strict";

(function () {

  // ----------------- ES5 features for older browsers ----------------- //
  if (!Array.prototype.forEach) {
    Array.prototype.forEach =  function(block, thisObject) {
      var len = this.length > 0;
      for (var i = 0; i < len; i++)
        if (i in this) block.call(thisObject, this[i], i, this);
    };
  }

  // ----------------- private api ----------------- //
  var wrench  = {},
      routing = {routes: {}},
      w       = window,
      d       = document;

  // Routing
  routing.changeRoute = function (route, params) {
    var routeUrl  = route,
        i         = 1;
    if (typeof params !== 'undefined') {
      routeUrl += ":";
      for (var param in params) {
        if (params.hasOwnProperty(param)) {
          if (i != 1) routeUrl += "+";
          routeUrl += param + "=" + params[param];
          i++;
        }
      }
    }

    if (w.location.hash != "" &&
        w.location.hash != "#" &&
        w.location.hash.indexOf(route) == -1) {
      route = w.location.hash + ";" + routeUrl;
    }
    else {
      route = routeUrl;
    }

    w.location.hash = route;
  };

  routing.locate = function () {
    var currentRoutes = w.location.hash.replace("#", "").split(";"),
        params        = {},
        route         = '';

    if (currentRoutes.length > 0 && currentRoutes[0] != "") {

			currentRoutes.forEach(function (routeString) {
				var rawParams = routeString.split(":");
				func = rawParams.shift();
				if (rawParams.length > 0 && rawParams[0] != undefined) {
					rawParams[0].split("+").forEach(function (param) {
						params[param.split("=")[0]] = param.split("=")[1]
					});
				}

				if (func in routing.routes) routing.routes[func](params);
			});

		}
		else if ("/" in routing.routes) {
			routing.routes["/"]();
		}

	};

	// wrench application object
	var wrenchApp = {
		callRoute: function (route, params) {
			routing.changeRoute(route, params);
		},
		run: function () {
			var app = this;

			window.addEventListener('load', function () {
				routing.locate();
				if (typeof app.init === 'function') app.init();
			}, false);
			// window.onhashchange = routing.locate;

			return app;
		}
  };

	// ----------------- public api ----------------- //
	wrench.VERSION = "0.0.1";

	// Turn an object into a wrench application
	wrench.appify = function (properties) {
		function F() {}
		F.prototype = wrenchApp;
		var app = new F();
  	for (var prop in properties)
    	if (properties.hasOwnProperty(prop))
      	app[prop] = properties[prop];
		return app;
	};

	// connects a route to a function
	// can be used like so: var users = route("users", function () {});
	// or: var users = route("users").to(function () {});
	// all routed functions will be called with a params hash
	//  as the only argument
	window.route = function (route, func) {
		if (typeof func === 'function') {
			routing.routes[route] = func;
			return function (params) {
				routing.changeRoute(route, params);
				func(params);
			};
		}
		else {
			return {
					to: function (func) {
						routing.routes[route] = func;
						return function (params) {
							routing.changeRoute(route, params);
							func(params);
						};
					}
				};
		}
	};

	window.wrench = wrench;
}());<|endoftext|>


<commit_before>/* eslint-disable import/no-extraneous-dependencies, no-console */
import { resolve } from 'path';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import { HotModuleReplacementPlugin, DefinePlugin } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';

export const basePath = resolve(`${__dirname}/notaperiod`);
export const distributionAppPath = resolve(`${__dirname}/dist/notaperiod`);

export default function (options = {}) {
  const environment = options.prod ? 'production' : 'dev';

  console.log(`=> Building notaperiod for '${environment}' environment.`);

  return ({
    entry: [
      'react-hot-loader/patch',
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      basePath,
    ],
    devServer: { inline: true },
    output: {
      path: distributionAppPath,
      filename: 'app.js',
    },
    module: {
      loaders: [
        {
          test: /\.css$/,
          loaders: ['style', 'css'],
        },
        {
          test: /\.js$/,
          exclude: /(node_modules)/,
          loader: ['babel', 'eslint'],
        },
      ],
    },
    plugins: [
      new CopyWebpackPlugin([{ from: `${basePath}/designs`, to: `${distributionAppPath}/designs` }]),
      new DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(environment),
        },
      }),
      new HotModuleReplacementPlugin(),
      new HtmlWebpackPlugin({
        template: `${basePath}/template/index.html`,
      }),
    ],
    devtool: environment === 'dev' ? 'cheap-module-eval-source-map' : 'hidden-source-map',
  });
}
<commit_msg>Omit source-maps in production<commit_after>/* eslint-disable import/no-extraneous-dependencies, no-console */
import { resolve } from 'path';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import { HotModuleReplacementPlugin, DefinePlugin } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';

export const basePath = resolve(`${__dirname}/notaperiod`);
export const distributionAppPath = resolve(`${__dirname}/dist/notaperiod`);

export default function (options = {}) {
  const environment = options.prod ? 'production' : 'dev';

  console.log(`=> Building notaperiod for '${environment}' environment.`);

  const environmentConfigurations = environment === 'dev' ? { devtool: 'cheap-module-eval-source-map' } : {};

  return ({
    entry: [
      'react-hot-loader/patch',
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      basePath,
    ],
    devServer: { inline: true },
    output: {
      path: distributionAppPath,
      filename: 'app.js',
    },
    module: {
      loaders: [
        {
          test: /\.css$/,
          loaders: ['style', 'css'],
        },
        {
          test: /\.js$/,
          exclude: /(node_modules)/,
          loader: ['babel', 'eslint'],
        },
      ],
    },
    plugins: [
      new CopyWebpackPlugin([{ from: `${basePath}/designs`, to: `${distributionAppPath}/designs` }]),
      new DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(environment),
        },
      }),
      new HotModuleReplacementPlugin(),
      new HtmlWebpackPlugin({
        template: `${basePath}/template/index.html`,
      }),
    ],
    ...environmentConfigurations,
  });
}
<|endoftext|>