<commit_before>import cluster from 'cluster';
import EventEmitter from 'events';
import dgram from 'dgram';
import net from 'net';
import tls from 'tls';
import ws from 'ws';
import {Balancer} from './balancer';
import {Config} from './config';
import * as MiddlewareManager from './middleware';
import {createRelay} from './relay';
import {logger} from '../utils';
import {tcp, http, socks} from '../proxies';

/**
 * @description
 *   gather and manage connections.
 *
 * @events
 *   .on('close');
 */
export class Hub extends EventEmitter {

  _isFirstWorker = cluster.worker ? (cluster.worker.id <= 1) : true;

  _fastestServer = null;

  _server = null;

  _udpServer = null; // server side only

  _relays = [];

  _udpRelays = {}; // server side only

  constructor(config) {
    super();
    this._onConnection = this._onConnection.bind(this);
    this._onUdpAssociate = this._onUdpAssociate.bind(this);
    this._onClose = this._onClose.bind(this);
    if (config !== undefined) {
      Config.init(config);
    }
  }

  terminate() {
    this._onClose();
  }

  async run() {
    if (this._server !== null) {
      this.terminate();
    }
    if (__IS_CLIENT__) {
      Balancer.start(__SERVERS__);
      this._isFirstWorker && logger.info('[balancer] started');
      this._selectServer();
    }
    await this._createServer();
    if (this._isFirstWorker) {
      logger.info(`[hub] blinksocks ${__IS_CLIENT__ ? 'client' : 'server'} is running at ${__LOCAL_PROTOCOL__}://${__LOCAL_HOST__}:${__LOCAL_PORT__}`);
    }
  }

  async _createServer() {
    try {
      if (__IS_CLIENT__) {
        this._server = await this._createServerOnClient();
      } else {
        this._server = await this._createServerOnServer();
        this._udpServer = await this._createUdpServer();
      }
    } catch (err) {
      logger.error('[hub] fail to create server:', err);
      process.exit(-1);
    }
  }

  async _createServerOnClient() {
    return new Promise((resolve, reject) => {
      let server = null;
      switch (__LOCAL_PROTOCOL__) {
        case 'tcp':
          server = tcp.createServer();
          break;
        case 'socks':
        case 'socks5':
        case 'socks4':
        case 'socks4a':
          server = socks.createServer();
          server.on('udpAssociate', this._onUdpAssociate);
          break;
        case 'http':
        case 'https':
          server = http.createServer();
          break;
        default:
          return reject(Error(`unsupported protocol: "${__LOCAL_PROTOCOL__}"`));
          break;
      }
      const address = {
        host: __LOCAL_HOST__,
        port: __LOCAL_PORT__
      };
      server.on('proxyConnection', this._onConnection);
      server.listen(address, () => resolve(server));
    });
  }

  async _createServerOnServer() {
    return new Promise((resolve, reject) => {
      const address = {
        host: __LOCAL_HOST__,
        port: __LOCAL_PORT__
      };
      switch (__LOCAL_PROTOCOL__) {
        case 'tcp': {
          const server = net.createServer();
          server.on('connection', this._onConnection);
          server.listen(address, () => resolve(server));
          break;
        }
        case 'ws': {
          const server = new ws.Server({
            ...address,
            perMessageDeflate: false
          });
          server.on('connection', (ws, req) => {
            ws.remoteAddress = req.connection.remoteAddress;
            ws.remotePort = req.connection.remotePort;
            this._onConnection(ws);
          });
          server.on('listening', () => resolve(server));
          break;
        }
        case 'tls': {
          const server = tls.createServer({key: [__TLS_KEY__], cert: [__TLS_CERT__]});
          server.on('secureConnection', this._onConnection);
          server.listen(address, () => resolve(server));
          break;
        }
        default:
          return reject(Error(`unsupported protocol: "${__LOCAL_PROTOCOL__}"`));
          break;
      }
    });
  }

  async _createUdpServer() {
    return new Promise((resolve, reject) => {
      const relays = this._udpRelays;
      const server = dgram.createSocket('udp4');

      server.on('message', (msg, rinfo) => {
        const {address, port} = rinfo;
        const key = `${address}:${port}`;
        if (relays[key] === undefined) {
          // TODO(refactor): pass remoteXXX to relay directly
          server.remoteAddress = address;
          server.remotePort = port;
          relays[key] = createRelay('udp', server);
          relays[key].on('close', () => delete relays[key]);
        }
        relays[key]._inbound.onReceive(msg, rinfo);
      });

      server.on('error', reject);

      // monkey patch for Socket.close() to prevent closing shared udp socket on server side
      // eslint-disable-next-line
      server.close = ((close) => (...args) => {
        // close.call(server, ...args);
      })(server.close);

      server.bind({address: __LOCAL_HOST__, port: __LOCAL_PORT__}, () => resolve(server));
    });
  }

  _selectServer() {
    const server = Balancer.getFastest();
    if (this._fastestServer === null || server.id !== this._fastestServer.id) {
      this._fastestServer = server;
      Config.initServer(server);
      MiddlewareManager.reset();
      logger.info(`[balancer] use server: ${__SERVER_HOST__}:${__SERVER_PORT__}`);
    }
  }

  _onUdpAssociate(tcpSocket, done) {
    const socket = dgram.createSocket('udp4');

    let relay = null;

    socket.on('message', (msg, rinfo) => {
      const parsed = socks.parseSocks5UdpRequest(msg);
      if (parsed !== null) {
        const {host, port, data} = parsed;
        // create relay only once
        if (relay === null) {
          socket.remoteAddress = rinfo.address;
          socket.remotePort = rinfo.port;
          relay = createRelay('udp', socket, {host, port});
        }
        socket.emit('packet', data, rinfo);
      } else {
        logger.warn(`[hub] [${rinfo.address}:${rinfo.port}] drop invalid udp packet: ${msg.slice(0, 60).toString('hex')}`);
      }
    });

    socket.on('error', (err) => {
      logger.error('[hub] fail to create udp socket:', err);
    });

    // monkey patch for Socket.send() to support Socks5 protocol
    socket.send = ((send) => (data, port, host, ...args) => {
      const packet = socks.encodeSocks5UdpResponse({host, port, data});
      send.call(socket, packet, port, host, ...args);
    })(socket.send);

    socket.bind({address: __LOCAL_HOST__/*, port: random */}, () => {
      const {address, port} = socket.address();
      done({bindAddress: address, bindPort: port});
    });

    tcpSocket.on('close', () => {
      relay.destroy();
      relay = null;
    });
  }

  _onConnection(context, proxyRequest = null) {
    if (__IS_CLIENT__) {
      this._selectServer();
    }
    logger.verbose(`[hub] [${context.remoteAddress}:${context.remotePort}] connected`);
    const relay = createRelay(__TRANSPORT__, context, proxyRequest);
    relay.on('close', () => {
      this._relays = this._relays.filter((r) => r.id !== relay.id);
    });
    this._relays.push(relay);
  }

  _onClose() {
    // relays
    this._relays.forEach((relay) => relay.destroy());
    MiddlewareManager.cleanup();
    // balancer
    if (__IS_CLIENT__) {
      Balancer.destroy();
      this._isFirstWorker && logger.info('[balancer] stopped');
    }
    // server
    this._server.close();
    this._isFirstWorker && logger.info('[hub] shutdown');
    // udp server
    if (__IS_SERVER__ && this._udpServer !== null) {
      this._udpServer._handle && this._udpServer.close();
      Object.keys(this._udpRelays).forEach((key) => this._udpRelays[key].destroy());
    }
    this.emit('close');
  }

}
<commit_msg>core: refine _onClose() and terminate()<commit_after>import cluster from 'cluster';
import EventEmitter from 'events';
import dgram from 'dgram';
import net from 'net';
import tls from 'tls';
import ws from 'ws';
import {Balancer} from './balancer';
import {Config} from './config';
import * as MiddlewareManager from './middleware';
import {createRelay} from './relay';
import {logger} from '../utils';
import {tcp, http, socks} from '../proxies';

/**
 * @description
 *   gather and manage connections.
 *
 * @events
 *   .on('close');
 */
export class Hub extends EventEmitter {

  _isFirstWorker = cluster.worker ? (cluster.worker.id <= 1) : true;

  _fastestServer = null;

  _server = null;

  _udpServer = null; // server side only

  _relays = [];

  _udpRelays = {}; // server side only

  constructor(config) {
    super();
    this._onConnection = this._onConnection.bind(this);
    this._onUdpAssociate = this._onUdpAssociate.bind(this);
    if (config !== undefined) {
      Config.init(config);
    }
  }

  terminate() {
    // relays
    this._relays.forEach((relay) => relay.destroy());
    MiddlewareManager.cleanup();
    // balancer
    if (__IS_CLIENT__) {
      Balancer.destroy();
      this._isFirstWorker && logger.info('[balancer] stopped');
    }
    // server
    this._server.close();
    this._isFirstWorker && logger.info('[hub] shutdown');
    // udp server
    if (__IS_SERVER__ && this._udpServer !== null) {
      this._udpServer._handle && this._udpServer.close();
      Object.keys(this._udpRelays).forEach((key) => this._udpRelays[key].destroy());
    }
    this.emit('close');
  }

  async run() {
    if (this._server !== null) {
      this.terminate();
    }
    if (__IS_CLIENT__) {
      Balancer.start(__SERVERS__);
      this._isFirstWorker && logger.info('[balancer] started');
      this._selectServer();
    }
    await this._createServer();
    if (this._isFirstWorker) {
      logger.info(`[hub] blinksocks ${__IS_CLIENT__ ? 'client' : 'server'} is running at ${__LOCAL_PROTOCOL__}://${__LOCAL_HOST__}:${__LOCAL_PORT__}`);
    }
  }

  async _createServer() {
    try {
      if (__IS_CLIENT__) {
        this._server = await this._createServerOnClient();
      } else {
        this._server = await this._createServerOnServer();
        this._udpServer = await this._createUdpServer();
      }
    } catch (err) {
      logger.error('[hub] fail to create server:', err);
      process.exit(-1);
    }
  }

  async _createServerOnClient() {
    return new Promise((resolve, reject) => {
      let server = null;
      switch (__LOCAL_PROTOCOL__) {
        case 'tcp':
          server = tcp.createServer();
          break;
        case 'socks':
        case 'socks5':
        case 'socks4':
        case 'socks4a':
          server = socks.createServer();
          server.on('udpAssociate', this._onUdpAssociate);
          break;
        case 'http':
        case 'https':
          server = http.createServer();
          break;
        default:
          return reject(Error(`unsupported protocol: "${__LOCAL_PROTOCOL__}"`));
          break;
      }
      const address = {
        host: __LOCAL_HOST__,
        port: __LOCAL_PORT__
      };
      server.on('proxyConnection', this._onConnection);
      server.listen(address, () => resolve(server));
    });
  }

  async _createServerOnServer() {
    return new Promise((resolve, reject) => {
      const address = {
        host: __LOCAL_HOST__,
        port: __LOCAL_PORT__
      };
      switch (__LOCAL_PROTOCOL__) {
        case 'tcp': {
          const server = net.createServer();
          server.on('connection', this._onConnection);
          server.listen(address, () => resolve(server));
          break;
        }
        case 'ws': {
          const server = new ws.Server({
            ...address,
            perMessageDeflate: false
          });
          server.on('connection', (ws, req) => {
            ws.remoteAddress = req.connection.remoteAddress;
            ws.remotePort = req.connection.remotePort;
            this._onConnection(ws);
          });
          server.on('listening', () => resolve(server));
          break;
        }
        case 'tls': {
          const server = tls.createServer({key: [__TLS_KEY__], cert: [__TLS_CERT__]});
          server.on('secureConnection', this._onConnection);
          server.listen(address, () => resolve(server));
          break;
        }
        default:
          return reject(Error(`unsupported protocol: "${__LOCAL_PROTOCOL__}"`));
          break;
      }
    });
  }

  async _createUdpServer() {
    return new Promise((resolve, reject) => {
      const relays = this._udpRelays;
      const server = dgram.createSocket('udp4');

      server.on('message', (msg, rinfo) => {
        const {address, port} = rinfo;
        const key = `${address}:${port}`;
        if (relays[key] === undefined) {
          // TODO(refactor): pass remoteXXX to relay directly
          server.remoteAddress = address;
          server.remotePort = port;
          relays[key] = createRelay('udp', server);
          relays[key].on('close', () => delete relays[key]);
        }
        relays[key]._inbound.onReceive(msg, rinfo);
      });

      server.on('error', reject);

      // monkey patch for Socket.close() to prevent closing shared udp socket on server side
      // eslint-disable-next-line
      server.close = ((close) => (...args) => {
        // close.call(server, ...args);
      })(server.close);

      server.bind({address: __LOCAL_HOST__, port: __LOCAL_PORT__}, () => resolve(server));
    });
  }

  _selectServer() {
    const server = Balancer.getFastest();
    if (this._fastestServer === null || server.id !== this._fastestServer.id) {
      this._fastestServer = server;
      Config.initServer(server);
      MiddlewareManager.reset();
      logger.info(`[balancer] use server: ${__SERVER_HOST__}:${__SERVER_PORT__}`);
    }
  }

  _onUdpAssociate(tcpSocket, done) {
    const socket = dgram.createSocket('udp4');

    let relay = null;

    socket.on('message', (msg, rinfo) => {
      const parsed = socks.parseSocks5UdpRequest(msg);
      if (parsed !== null) {
        const {host, port, data} = parsed;
        // create relay only once
        if (relay === null) {
          socket.remoteAddress = rinfo.address;
          socket.remotePort = rinfo.port;
          relay = createRelay('udp', socket, {host, port});
        }
        socket.emit('packet', data, rinfo);
      } else {
        logger.warn(`[hub] [${rinfo.address}:${rinfo.port}] drop invalid udp packet: ${msg.slice(0, 60).toString('hex')}`);
      }
    });

    socket.on('error', (err) => {
      logger.error('[hub] fail to create udp socket:', err);
    });

    // monkey patch for Socket.send() to support Socks5 protocol
    socket.send = ((send) => (data, port, host, ...args) => {
      const packet = socks.encodeSocks5UdpResponse({host, port, data});
      send.call(socket, packet, port, host, ...args);
    })(socket.send);

    socket.bind({address: __LOCAL_HOST__/*, port: random */}, () => {
      const {address, port} = socket.address();
      done({bindAddress: address, bindPort: port});
    });

    tcpSocket.on('close', () => {
      relay.destroy();
      relay = null;
    });
  }

  _onConnection(context, proxyRequest = null) {
    if (__IS_CLIENT__) {
      this._selectServer();
    }
    logger.verbose(`[hub] [${context.remoteAddress}:${context.remotePort}] connected`);
    const relay = createRelay(__TRANSPORT__, context, proxyRequest);
    relay.on('close', () => {
      this._relays = this._relays.filter((r) => r.id !== relay.id);
    });
    this._relays.push(relay);
  }

}
<|endoftext|>


<commit_before>(function () {
	"use strict";

	window.cx = window.cx || {};

	// this code is used when this plugin is loaded before the cx.js main file is loaded
	if (!cx.plugin) {
		cx.pluginsToLoad = cx.pluginsToLoad || {};
		cx.plugin = function (name, plugin) {
			cx.pluginsToLoad[name] = plugin;
		}
	}

	// Registered base views
	var views = {};

	// Instances of views, each connected to an element
	var viewInstances = [];

	var root = document.body;

	cx.plugin('views', {
		init: function () {
			// Associate all elements with a data-view attribute with an instance of that view
			createViews(document.body);

			// Setup a handler for event delegation to all views and their actions
			bindRoot();

			// we need to know which views have already loaded to only call staticInit once per type of view
			var initializedViews = {};

			for (var i in viewInstances) {
				var viewInstance = viewInstances[i];
				if (viewInstance.staticInit && !(viewInstance.name in initializedViews)) {
					viewInstance.staticInit();
					initializedViews[viewInstance.name] = true;
				}
				if (viewInstance.init) {
					var jsonData = util.getJsonAttributes(viewInstance.elm);
					var tagData = util.getDataAttributes(viewInstance.elm);
					var params = util.merge(jsonData, tagData);
					viewInstance.params = params;
					viewInstance.init(params);
				}
			}
		},

		info: function () {
			console.info('loaded views');
			console.dir(views);
		},

		extend: function () {
			return {
				view: registerView,
				createViews: createViews
			}
		}
	});

	function bindRoot() {
		root.addEventListener('click', onRootEvent);
	}

	/**
	 * This method is used for delegating all events on elements with a data-action attribute.
	 * It will route the event to the closest parent view (with a data-view attribute)
	 *
	 * @param e
	 */
	function onRootEvent(e) {
		var elmAction = e.target;
		if (!e.target.hasAttribute('data-action')) {
			// if the elment that triggered the event doesn't have a data-action
			// look if any of its parents has it
			elmAction = util.closest(elmAction, '[data-action]');
			if (!elmAction) {
				// there are no data-actions defined for this event, ignore the event
				return;
			}
		}
		e.stopPropagation();
		e.preventDefault();
		var elmView = util.closest(elmAction, '[data-view]');
		var actionName = elmAction.getAttribute('data-action');
		var params = util.getDataAttributes(elmAction);
		delete params['action'];
		emitAction(elmView, actionName, params, e);
	}

	function emitAction(elm, actionName, params, e) {
		var actionNameKey = util.toCamelCase('on-' + actionName);
		if (elm.view) {
			if (typeof elm.view[actionNameKey] !== 'undefined') {
				elm.view[actionNameKey](params, e);
			} else {
				console.log('Action ', actionNameKey, 'not found on view', elm.view);
			}
		} else {
			console.log('cx: View', elm.getAttribute('data-view'), 'not found when triggering action', actionName, 'for element', elm);
		}
	}

	/**
	 * Will convert the view object a into function which can be instantiated
	 * when the view is later called upon
	 *
	 * Example call:
	 *
	 *      registerView('myView', { onSelect: function(params, e) {}, [...] });
	 *
	 * with the global alias
	 *
	 *      cx.view(..., ...);
	 *
	 * @param name String
	 * @param viewObject Object with Actions
	 */
	function registerView(name, viewObject) {
		var View = function () {
		};

		if (typeof(viewObject) == 'object') {
			for (var key in viewObject) {
				if (viewObject.hasOwnProperty(key)) {
					View.prototype[key] = viewObject[key];
				}
			}
		} else if (typeof(viewObject) == 'function') {
			View = viewObject;
		}

		View.prototype.find = function (selector) {
			// convert the nodeList to a more manageable array
			var nodeList = this.elm.querySelectorAll(selector);
			var arr = Array.prototype.slice.call(nodeList);
			return arr;
		};

		View.prototype.findFirst = function (selector) {
			return this.elm.querySelector(selector);
		}

		/**
		 * Find and return an area or write to
		 * its innerHTML if valueOrPromise is specified
		 * @param name
		 * @param {valueOrPromise} If specified this value will be set as the new innerHTML for
		 * the element (and first unwrapped if it is a promise). Can also be set to an html element in which
		 * case the html elements innerHTML property will be used. This way an area can be assigned to another area easily.
		 * @returns {HTMLElement}
		 */
		View.prototype.area = function (name, valueOrPromise) {
			var elmArea = this.elm.querySelector('[data-area=' + name + ']');
			if (valueOrPromise) {
				// unbox the value if it is a promise
				if (valueOrPromise.then) {
					valueOrPromise.then(function (value) {
						elmArea.innerHTML = value;
					});
					// or if an element, use its innerHTML
				} else if (valueOrPromise['innerHTML']) {
					elmArea.innerHTML = valueOrPromise.innerHTML;
				} else {
					elmArea.innerHTML = valueOrPromise;
				}
			} else {
				return elmArea;
			}
		};

		View.prototype.areas = function (name) {
			return this.elm.querySelectorAll('[data-area=' + name + ']');
		};

		views[name] = View;
	}

	function createViews(elmRoot) {
		var nsViews = elmRoot.querySelectorAll('[data-view]');
		createViewForElement(elmRoot);
		Array.prototype.forEach.call(nsViews, createViewForElement);
	}

	/**
	 * Will look at the data-view property of the element given
	 * and instantiate a view that was registered with that name
	 * and define a view property on the element referencing to the view
	 *
	 * @param elm
	 */
	function createViewForElement(elm) {
		var viewName = elm.getAttribute('data-view');
		if (viewName in views) {
			var View = views[viewName];
			var view = new View();
			view.elm = elm;
			view.name = viewName;

			Object.defineProperty(elm, 'view', {value: view});
			viewInstances.push(view);
		} else {
			console.log('cx: View', viewName, 'used but not found on element', elm);
		}
	}

	var util = {
		// http://stackoverflow.com/questions/15329167/closest-ancestor-matching-selector-using-native-dom
		closest: function (elm, selector) {
			var matchesSelector = elm.matches || elm.webkitMatchesSelector || elm.mozMatchesSelector || elm.msMatchesSelector;
			while (elm) {
				if (matchesSelector.bind(elm)(selector)) {
					return elm;
				} else {
					elm = elm.parentElement;
				}
			}
			return false;
		},

		// http://stackoverflow.com/questions/4187032/get-list-of-data-attributes-using-javascript-jquery
		toCamelCase: function (str) {
			return str.replace(/-(.)/g, function ($0, $1) {
				return $1.toUpperCase();
			});
		},

		// http://stackoverflow.com/questions/4187032/get-list-of-data-attributes-using-javascript-jquery
		getDataAttributes: function (elm) {
			var data = {};
			[].forEach.call(elm.attributes, function (attr) {
				if (/^data-/.test(attr.name)) {
					data[util.toCamelCase(attr.name.substr(5))] = attr.value;
				}
			});
			return data;
		},

		getJsonAttributes: function (elm) {
			var scriptTag = elm.querySelector('script[type="application/json"]');
			if (scriptTag) {
				var strJson = scriptTag.innerHTML;
				var json = JSON.parse(strJson);
				return json;
			} else {
				return [];
			}
		},

		merge: function (obj1, obj2) {
			var merged = {};
			for (var x in obj1) {
				if (obj1.hasOwnProperty(x)) {
					merged[x] = obj1[x];
				}
			}
			for (var y in obj2) {
				if (obj2.hasOwnProperty(y)) {
					merged[y] = obj2[y];
				}
			}
			return merged;
		}

	}

})();<commit_msg>Fix params not being set if no init method is defined<commit_after>(function () {
	"use strict";

	window.cx = window.cx || {};

	// this code is used when this plugin is loaded before the cx.js main file is loaded
	if (!cx.plugin) {
		cx.pluginsToLoad = cx.pluginsToLoad || {};
		cx.plugin = function (name, plugin) {
			cx.pluginsToLoad[name] = plugin;
		}
	}

	// Registered base views
	var views = {};

	// Instances of views, each connected to an element
	var viewInstances = [];

	var root = document.body;

	cx.plugin('views', {
		init: function () {
			// Associate all elements with a data-view attribute with an instance of that view
			createViews(document.body);

			// Setup a handler for event delegation to all views and their actions
			bindRoot();

			// we need to know which views have already loaded to only call staticInit once per type of view
			var initializedViews = {};

			for (var i in viewInstances) {
				var viewInstance = viewInstances[i];
				if (viewInstance.staticInit && !(viewInstance.name in initializedViews)) {
					viewInstance.staticInit();
					initializedViews[viewInstance.name] = true;
				}

				var jsonData = util.getJsonAttributes(viewInstance.elm);
				var tagData = util.getDataAttributes(viewInstance.elm);
				var params = util.merge(jsonData, tagData);
				viewInstance.params = params;

				if (viewInstance.init) {
					viewInstance.init(params);
				}
			}
		},

		info: function () {
			console.info('loaded views');
			console.dir(views);
		},

		extend: function () {
			return {
				view: registerView,
				createViews: createViews
			}
		}
	});

	function bindRoot() {
		root.addEventListener('click', onRootEvent);
	}

	/**
	 * This method is used for delegating all events on elements with a data-action attribute.
	 * It will route the event to the closest parent view (with a data-view attribute)
	 *
	 * @param e
	 */
	function onRootEvent(e) {
		var elmAction = e.target;
		if (!e.target.hasAttribute('data-action')) {
			// if the elment that triggered the event doesn't have a data-action
			// look if any of its parents has it
			elmAction = util.closest(elmAction, '[data-action]');
			if (!elmAction) {
				// there are no data-actions defined for this event, ignore the event
				return;
			}
		}
		e.stopPropagation();
		e.preventDefault();
		var elmView = util.closest(elmAction, '[data-view]');
		var actionName = elmAction.getAttribute('data-action');
		var params = util.getDataAttributes(elmAction);
		delete params['action'];
		emitAction(elmView, actionName, params, e);
	}

	function emitAction(elm, actionName, params, e) {
		var actionNameKey = util.toCamelCase('on-' + actionName);
		if (elm.view) {
			if (typeof elm.view[actionNameKey] !== 'undefined') {
				elm.view[actionNameKey](params, e);
			} else {
				console.log('Action ', actionNameKey, 'not found on view', elm.view);
			}
		} else {
			console.log('cx: View', elm.getAttribute('data-view'), 'not found when triggering action', actionName, 'for element', elm);
		}
	}

	/**
	 * Will convert the view object a into function which can be instantiated
	 * when the view is later called upon
	 *
	 * Example call:
	 *
	 *      registerView('myView', { onSelect: function(params, e) {}, [...] });
	 *
	 * with the global alias
	 *
	 *      cx.view(..., ...);
	 *
	 * @param name String
	 * @param viewObject Object with Actions
	 */
	function registerView(name, viewObject) {
		var View = function () {
		};

		if (typeof(viewObject) == 'object') {
			for (var key in viewObject) {
				if (viewObject.hasOwnProperty(key)) {
					View.prototype[key] = viewObject[key];
				}
			}
		} else if (typeof(viewObject) == 'function') {
			View = viewObject;
		}

		View.prototype.find = function (selector) {
			// convert the nodeList to a more manageable array
			var nodeList = this.elm.querySelectorAll(selector);
			var arr = Array.prototype.slice.call(nodeList);
			return arr;
		};

		View.prototype.findFirst = function (selector) {
			return this.elm.querySelector(selector);
		}

		/**
		 * Find and return an area or write to
		 * its innerHTML if valueOrPromise is specified
		 * @param name
		 * @param {valueOrPromise} If specified this value will be set as the new innerHTML for
		 * the element (and first unwrapped if it is a promise). Can also be set to an html element in which
		 * case the html elements innerHTML property will be used. This way an area can be assigned to another area easily.
		 * @returns {HTMLElement}
		 */
		View.prototype.area = function (name, valueOrPromise) {
			var elmArea = this.elm.querySelector('[data-area=' + name + ']');
			if (valueOrPromise) {
				// unbox the value if it is a promise
				if (valueOrPromise.then) {
					valueOrPromise.then(function (value) {
						elmArea.innerHTML = value;
					});
					// or if an element, use its innerHTML
				} else if (valueOrPromise['innerHTML']) {
					elmArea.innerHTML = valueOrPromise.innerHTML;
				} else {
					elmArea.innerHTML = valueOrPromise;
				}
			} else {
				return elmArea;
			}
		};

		View.prototype.areas = function (name) {
			return this.elm.querySelectorAll('[data-area=' + name + ']');
		};

		views[name] = View;
	}

	function createViews(elmRoot) {
		var nsViews = elmRoot.querySelectorAll('[data-view]');
		createViewForElement(elmRoot);
		Array.prototype.forEach.call(nsViews, createViewForElement);
	}

	/**
	 * Will look at the data-view property of the element given
	 * and instantiate a view that was registered with that name
	 * and define a view property on the element referencing to the view
	 *
	 * @param elm
	 */
	function createViewForElement(elm) {
		var viewName = elm.getAttribute('data-view');
		if (viewName in views) {
			var View = views[viewName];
			var view = new View();
			view.elm = elm;
			view.name = viewName;

			Object.defineProperty(elm, 'view', {value: view});
			viewInstances.push(view);
		} else {
			console.log('cx: View', viewName, 'used but not found on element', elm);
		}
	}

	var util = {
		// http://stackoverflow.com/questions/15329167/closest-ancestor-matching-selector-using-native-dom
		closest: function (elm, selector) {
			var matchesSelector = elm.matches || elm.webkitMatchesSelector || elm.mozMatchesSelector || elm.msMatchesSelector;
			while (elm) {
				if (matchesSelector.bind(elm)(selector)) {
					return elm;
				} else {
					elm = elm.parentElement;
				}
			}
			return false;
		},

		// http://stackoverflow.com/questions/4187032/get-list-of-data-attributes-using-javascript-jquery
		toCamelCase: function (str) {
			return str.replace(/-(.)/g, function ($0, $1) {
				return $1.toUpperCase();
			});
		},

		// http://stackoverflow.com/questions/4187032/get-list-of-data-attributes-using-javascript-jquery
		getDataAttributes: function (elm) {
			var data = {};
			[].forEach.call(elm.attributes, function (attr) {
				if (/^data-/.test(attr.name)) {
					data[util.toCamelCase(attr.name.substr(5))] = attr.value;
				}
			});
			return data;
		},

		getJsonAttributes: function (elm) {
			var scriptTag = elm.querySelector('script[type="application/json"]');
			if (scriptTag) {
				var strJson = scriptTag.innerHTML;
				var json = JSON.parse(strJson);
				return json;
			} else {
				return [];
			}
		},

		merge: function (obj1, obj2) {
			var merged = {};
			for (var x in obj1) {
				if (obj1.hasOwnProperty(x)) {
					merged[x] = obj1[x];
				}
			}
			for (var y in obj2) {
				if (obj2.hasOwnProperty(y)) {
					merged[y] = obj2[y];
				}
			}
			return merged;
		}

	}

})();<|endoftext|>


<commit_before>'use strict';

/*
 * Default configuration
 */

var primary = {

  targetEl: '#bookingjs',
  name: '',
  avatar: '',
  autoload: true,
  disableRemoteLoad: false,
  disableConfirmPage: false,
  includeStyles: true,
  showCredits: true,
  goToFirstEvent: true,
  bookingGraph: 'instant',
  debug: false,
  bookingFields: {
    name: {
      placeholder: 'Full name',
      prefilled: false,
      locked: false
    },
    email: {
      placeholder: 'E-mail',
      prefilled: false,
      locked: false
    },
    comment: {
      enabled: true,
      placeholder: 'Comment',
      prefilled: false,
      required: false,
      locked: false
    },
    phone: {
      enabled: false,
      placeholder: 'Phone number',
      prefilled: false,
      required: false,
      locked: false
    },
    voip: {
      enabled: false,
      placeholder: 'Skype username',
      prefilled: false,
      required: false,
      locked: false
    },
    location: {
      enabled: false,
      placeholder: 'Location',
      prefilled: false,
      required: false,
      locked: false
    }
  },
  timekitConfig: {
    app: 'bookingjs'
  },
  timekitFindTime: {
    future: '4 weeks',
    length: '1 hour'
  },
  timekitCreateBooking: { },
  timekitUpdateBooking: { },
  fullCalendar: {
    header: {
      left: '',
      center: '',
      right: 'today, prev, next'
    },
    views: {
      agenda: {
        displayEventEnd: false
      }
    },
    allDaySlot: false,
    scrollTime: '08:00:00',
    timezone: 'local',
    nowIndicator: true
  },
  localization: {
    showTimezoneHelper: true,
    timeDateFormat: '12h-mdy-sun',
    strings: {
      submitText: 'Book it',
      successMessageTitle: 'Thanks!',
      timezoneHelperLoading: 'Loading..',
      timezoneHelperDifferent: 'Your timezone is %s hours %s of %s (calendar shown in your local time)',
      timezoneHelperSame: 'You are in the same timezone as %s'
    }
  },
  callbacks: {}

};

// Preset: bookingGraph = 'instant'
var bookingInstant = {

  timekitCreateBooking: {
    graph: 'instant',
    action: 'confirm',
    event: {
      invite: true,
      my_rsvp: 'accepted',
      sync_provider: true
    }
  },
  localization: {
    strings: {
      successMessageBody: 'An invitation has been sent to:
 %s

 Please accept the invitation to confirm the booking.'
    }
  }

};

// Preset: bookingGraph = 'confirm_decline'
var bookingConfirmDecline = {

  timekitCreateBooking: {
    graph: 'confirm_decline',
    action: 'create',
    event: {
      invite: true,
      my_rsvp: 'accepted',
      sync_provider: true
    }
  },
  localization: {
    strings: {
      successMessageBody: "We have received your request and we'll be in touch when we have reviewed it.

Have a great day!"
    }
  }

};

// Preset: bookingGraph = 'group_customer'
var bookingGroupCustomer = {

  timekitCreateBooking: {
    graph: 'group_customer',
    action: 'create',
  },
  localization: {
    strings: {
      successMessageBody: "Your seat has been reserved and we've sent you a confirmation by email.

Have a great day!"
    }
  }

};

// Preset: bookingGraph = 'group_customer_payment'
var bookingGroupCustomerPayment = {

  timekitCreateBooking: {
    graph: 'group_customer_payment',
    action: 'create',
  },
  localization: {
    strings: {
      successMessageBody: "We have received your payment and reserved a seat for you.

Have a great day!"
    }
  }

};

// Preset: timeDateFormat = '24h-dmy-mon'
var timeDateFormat24hdmymon = {

  fullCalendar: {
    timeFormat: 'HH:mm',
    firstDay: 1,
    views: {
      basic: {
        columnFormat: 'dddd D/M'
      },
      agenda: {
        columnFormat: 'ddd\n D/M',
        slotLabelFormat: 'HH:mm'
      }
    }
  },
  localization: {
    bookingDateFormat: 'D. MMMM YYYY',
    bookingTimeFormat: 'HH:mm',
    emailTimeFormat: 'H:i'
  }

};

// Preset: timeDateFormat = '12h-mdy-sun'
var timeDateFormat12hmdysun = {

  fullCalendar: {
    timeFormat: 'h:mma',
    firstDay: 0,
    views: {
      basic: {
        columnFormat: 'dddd M/D',
      },
      agenda: {
        columnFormat: 'ddd\n M/D',
        slotLabelFormat: 'h:mma'
      }
    },
  },
  localization: {
    bookingDateFormat: 'MMMM D, YYYY',
    bookingTimeFormat: 'h:mma',
    emailTimeFormat: 'h:ia'
  }

};

// Export objects
module.exports = {
  primary: primary,
  presets: {
    timeDateFormat: {
      '24h-dmy-mon': timeDateFormat24hdmymon,
      '12h-mdy-sun': timeDateFormat12hmdysun
    },
    bookingGraph: {
      'instant': bookingInstant,
      'confirm_decline': bookingConfirmDecline,
      'group_customer': bookingGroupCustomer,
      'group_customer_payment': bookingGroupCustomerPayment
    }
  }
};
<commit_msg>add default settings for instant payment graph<commit_after>'use strict';

/*
 * Default configuration
 */

var primary = {

  targetEl: '#bookingjs',
  name: '',
  avatar: '',
  autoload: true,
  disableRemoteLoad: false,
  disableConfirmPage: false,
  includeStyles: true,
  showCredits: true,
  goToFirstEvent: true,
  bookingGraph: 'instant',
  debug: false,
  bookingFields: {
    name: {
      placeholder: 'Full name',
      prefilled: false,
      locked: false
    },
    email: {
      placeholder: 'E-mail',
      prefilled: false,
      locked: false
    },
    comment: {
      enabled: true,
      placeholder: 'Comment',
      prefilled: false,
      required: false,
      locked: false
    },
    phone: {
      enabled: false,
      placeholder: 'Phone number',
      prefilled: false,
      required: false,
      locked: false
    },
    voip: {
      enabled: false,
      placeholder: 'Skype username',
      prefilled: false,
      required: false,
      locked: false
    },
    location: {
      enabled: false,
      placeholder: 'Location',
      prefilled: false,
      required: false,
      locked: false
    }
  },
  timekitConfig: {
    app: 'bookingjs'
  },
  timekitFindTime: {
    future: '4 weeks',
    length: '1 hour'
  },
  timekitCreateBooking: { },
  timekitUpdateBooking: { },
  fullCalendar: {
    header: {
      left: '',
      center: '',
      right: 'today, prev, next'
    },
    views: {
      agenda: {
        displayEventEnd: false
      }
    },
    allDaySlot: false,
    scrollTime: '08:00:00',
    timezone: 'local',
    nowIndicator: true
  },
  localization: {
    showTimezoneHelper: true,
    timeDateFormat: '12h-mdy-sun',
    strings: {
      submitText: 'Book it',
      successMessageTitle: 'Thanks!',
      timezoneHelperLoading: 'Loading..',
      timezoneHelperDifferent: 'Your timezone is %s hours %s of %s (calendar shown in your local time)',
      timezoneHelperSame: 'You are in the same timezone as %s'
    }
  },
  callbacks: {}

};

// Preset: bookingGraph = 'instant'
var bookingInstant = {

  timekitCreateBooking: {
    graph: 'instant',
    action: 'confirm',
    event: {
      invite: true,
      my_rsvp: 'accepted',
      sync_provider: true
    }
  },
  localization: {
    strings: {
      successMessageBody: 'An invitation has been sent to:
 %s

 Please accept the invitation to confirm the booking.'
    }
  }

};

// Preset: bookingGraph = 'instant_payment'
var bookingInstantPayment = {

  timekitCreateBooking: {
    graph: 'instant_payment',
    action: 'tentative',
    event: {
      invite: true,
      my_rsvp: 'accepted',
      sync_provider: true
    }
  },
  localization: {
    strings: {
      successMessageBody: "We have received your payment and reserved your timeslot.

Have a great day!"
    }
  }

};

// Preset: bookingGraph = 'confirm_decline'
var bookingConfirmDecline = {

  timekitCreateBooking: {
    graph: 'confirm_decline',
    action: 'create',
    event: {
      invite: true,
      my_rsvp: 'accepted',
      sync_provider: true
    }
  },
  localization: {
    strings: {
      successMessageBody: "We have received your request and we'll be in touch when we have reviewed it.

Have a great day!"
    }
  }

};

// Preset: bookingGraph = 'group_customer'
var bookingGroupCustomer = {

  timekitCreateBooking: {
    graph: 'group_customer',
    action: 'create',
  },
  localization: {
    strings: {
      successMessageBody: "Your seat has been reserved and we've sent you a confirmation by email.

Have a great day!"
    }
  }

};

// Preset: bookingGraph = 'group_customer_payment'
var bookingGroupCustomerPayment = {

  timekitCreateBooking: {
    graph: 'group_customer_payment',
    action: 'create',
  },
  localization: {
    strings: {
      successMessageBody: "We have received your payment and reserved a seat for you.

Have a great day!"
    }
  }

};

// Preset: timeDateFormat = '24h-dmy-mon'
var timeDateFormat24hdmymon = {

  fullCalendar: {
    timeFormat: 'HH:mm',
    firstDay: 1,
    views: {
      basic: {
        columnFormat: 'dddd D/M'
      },
      agenda: {
        columnFormat: 'ddd\n D/M',
        slotLabelFormat: 'HH:mm'
      }
    }
  },
  localization: {
    bookingDateFormat: 'D. MMMM YYYY',
    bookingTimeFormat: 'HH:mm',
    emailTimeFormat: 'H:i'
  }

};

// Preset: timeDateFormat = '12h-mdy-sun'
var timeDateFormat12hmdysun = {

  fullCalendar: {
    timeFormat: 'h:mma',
    firstDay: 0,
    views: {
      basic: {
        columnFormat: 'dddd M/D',
      },
      agenda: {
        columnFormat: 'ddd\n M/D',
        slotLabelFormat: 'h:mma'
      }
    },
  },
  localization: {
    bookingDateFormat: 'MMMM D, YYYY',
    bookingTimeFormat: 'h:mma',
    emailTimeFormat: 'h:ia'
  }

};

// Export objects
module.exports = {
  primary: primary,
  presets: {
    timeDateFormat: {
      '24h-dmy-mon': timeDateFormat24hdmymon,
      '12h-mdy-sun': timeDateFormat12hmdysun
    },
    bookingGraph: {
      'instant': bookingInstant,
      'instant_payment': bookingInstantPayment,
      'confirm_decline': bookingConfirmDecline,
      'group_customer': bookingGroupCustomer,
      'group_customer_payment': bookingGroupCustomerPayment
    }
  }
};
<|endoftext|>


<commit_before>(function ($, window, undefined) {
  $.fn.destaque = function(params) {
    params = $.extend($.fn.destaque.options, params);
    return new Destaque(this, params);
  };

  $.fn.destaque.options = {
    currentSlide: 0,
    slideSpeed: 1000,
    slideMovement: 150,
    slideDirection: "toLeft",
    elementSpeed: 1000,
    autoSlideDelay: 3000,
    resumeSlideDelay: 250,
    stopOnMouseOver: true,
    easingType: "easeInOutExpo",
    itemDefaultZIndex: 10,
    itemSelector: "div.item",
    itemLoadedClass: "loaded",
    activeItemClass: "active",
    itemForegroundElementSelector: ".foreground img",

    // Callbacks
    onInit: function (slideshow) {},
    onResize: function (slideshow) {},
    onPause: function(slideshow) {},
    onResume: function(slideshow) {},
    onPageUpdate: function(slideshow, pageData) {},
    onSlideLoad: function(slideshow, element) {},
    beforePageUpdate: function(slideshow, pageData) {}
  };

  var Destaque = function (element, params) {
    this.element = $(element);
    this.params = params;

    this.params.inited = true
    this.params.mouseOver = false;
    this.params.animating = false;

    this.params.autoId = 0;
    this.params.autoTimeStamp = 0;

    this._initElements();
    this._initListeners();

    this.params.onInit(this);
  };

  Destaque.prototype = {
    _initListeners: function() {
      this._initPaginationListeners();
      this._initMouseListeners();
      this._initKeyboardListeners();
      this._initMobileListeners();
      this._initWindowListeners();
    },

    _initPaginationListeners: function () {
      var self = this;

      if (!this.params.controlsSelector) {
        return;
      }

      $(this.params.controlsSelector).each(function() {
        $(this).bind("click.destaque", function(e) {
          e.preventDefault();

          var direction = $(e.currentTarget).attr("rel") === "prev" ? "toRight" : "toLeft";
          self.slideSetAndMove(direction);
        });
      });
    },

    _initMobileListeners: function() {
      var self = this;

      this.element.bind("swipeleft.destaque", function(e) {
        self.slideSetAndMove("toLeft");
       });

      this.element.bind("swiperight.destaque", function (e) {
        self.slideSetAndMove("toRight");
      });
    },

    _initMouseListeners: function () {
      var params = this.params;
      var self = this;

      if (params.slideSum > 1) {
        if (params.stopOnMouseOver) {
          this.element.mouseover(function() {
            self.pause();
          });

          this.element.mouseleave(function() {
            self.resume();
          });
        }
      }
    },

    _initKeyboardListeners: function() {
      var self = this;

      $(window).bind("keydown.destaque", function(e) {
        if (e.keyCode === 37) {
          self.slideSetAndMove("toRight");
        } else {
          if (e.keyCode === 39) {
            self.slideSetAndMove("toLeft");
          }
        }
      });
    },

    _initWindowListeners: function() {
      var self = this;

      $(window).bind("resize.destaque", function() {
        self.params.onResize(self);
      });
    },

    _initElements: function () {
      var element = this.element;
      var params = this.params;

      if (params.inited) {
        params.baseSize = element.width();
        params.slideSum = element.find(this.params.itemSelector).length;
        params.elementDirection = params.slideDirection === "toLeft" ? "fromLeft" : "toLeft";

        this._initSlide();

        params.inited = false;
      }
    },

    _initSlide: function() {
      var self = this;
      var params = this.params;

      this.element.find(params.itemSelector).each(function() {
        $(this).data("zIndex", params.itemDefaultZIndex).css({zIndex: params.itemDefaultZIndex});
      });

      this.element.find(params.itemForegroundElementSelector).each(function (idx, e) {
        var e = $(this);

        var initialX = parseInt(e.css("left"), 10) + (self.element.width() - params.baseSize) / 2;
        var initialY = parseInt(e.css("top"), 10);

        e.css({top: initialY, left: params.baseSize});
        e.data("xPos", initialX).data("yPos", initialY).data("slidePos", 9999);
      });

      this._loadSlide();
      this._currentSlide().addClass(params.activeItemClass).show();
      this._slideElementsIn();

      params.autoTimeStamp = new Date().getTime();
    },

    pause: function() {
      var params = this.params;

      if (!params.animating) {
        params.mouseOver = true;
        clearTimeout(params.autoId);
        params.onPause(this);
      }
    },

    resume: function() {
      var self = this;
      var params = this.params;

      if (!params.animating) {
        params.mouseOver = false;

        var timeout = 0;

        if (params.autoSlideDelay - (new Date().getTime() - params.autoTimeStamp) < 0) {
          timeout = params.resumeSlideDelay;
        } else {
          timeout = params.autoSlideDelay - (new Date().getTime() - params.autoTimeStamp);
        }

        params.autoId = setTimeout(function() {
          self._slideElementsOut();
        }, timeout);

        params.onResume(this);
      }
    },

    slideSetAndMove: function(direction) {
      var params = this.params;

      if (!params.animating) {
        if (direction === "toLeft") {
          params.slideDirection = "toLeft";
          params.elementDirection = "fromLeft";
          this.element.removeClass("right").addClass("left");
        } else {
          params.slideDirection = "toRight";
          params.elementDirection = "fromRight";
          this.element.removeClass("left").addClass("right");
        }

        this._setElementPositions(true);
        this._slideElementsOut();
      }
    },

    _setElementPositions: function(justCalculate) {
      var params = this.params;
      var element, pos = 0;

      this._currentSlide().find(params.itemForegroundElementSelector).each(function(i) {
        element = $(this);

        if (params.elementDirection === "fromLeft") {
          pos = params.baseSize + (i * params.baseSize) - (i > 0 ? parseInt(element.data("xPos"), 10) : 0);
        } else {
          pos = -params.baseSize - (i * params.baseSize) + (i > 0 ? parseInt(element.data("xPos"), 10) : 0);
        }

        element.data("slidePos", pos);

        if (!justCalculate) {
          element.css({left: pos});
        }
      });
    },

    _moveSlide: function() {
      var self = this;
      var params = this.params;

      var current = this.element.find(params.itemSelector + "." + params.activeItemClass);
      var next = null;
      var outPosition = 0;
      var pos = 0;

      params.animating = true;

      if (params.slideDirection === "toLeft") {
        outPosition = -params.slideMovement;
        next = this.element.find(params.itemSelector).eq(params.currentSlide < this.element.find(params.itemSelector).length - 1 ? params.currentSlide + 1 : 0);

        if (next.length) {
          next.css({left: params.baseSize, zIndex: params.itemDefaultZIndex + 1}).show();
        }
      } else {
        outPosition = params.slideMovement;
        next = this.element.find(params.itemSelector).eq(params.currentSlide < 1 ? this.element.find(params.itemSelector).length - 1 : params.currentSlide - 1);

        if (next.length) {
          next.css({left: -params.baseSize, zIndex: params.itemDefaultZIndex + 1}).show();
        }
      }

      next.stop().animate({left: pos}, params.slideSpeed, params.easingType, function() {
        next.addClass(params.activeItemClass);
      });

      current.css({zIndex: params.itemDefaultZIndex}).stop().animate({left: outPosition}, params.slideSpeed, params.easingType, function() {
        $(this).removeClass(params.activeItemClass).hide();
      });

      this._updateCurrentSlide();
    },

    _updatePagers: function(callback) {
      var params = this.params;
      var data = {
        currentPage: params.currentSlide,
        totalPages:  params.slideSum
      };

      return params[callback](this, data);
    },

    _currentSlide: function() {
      var params = this.params;
      return this.element.find(params.itemSelector).eq(params.currentSlide);
    },

    _previousSlide: function() {
      var params = this.params;

      if (params.slideDirection === "toLeft") {
        if (params.currentSlide > 0) {
          return params.currentSlide - 1;
        } else {
          return this.element.find(params.itemSelector).length - 1;
        }
      } else {
        if (params.currentSlide < this.element.find(params.itemSelector).length - 1) {
          return params.currentSlide + 1;
        } else {
          return 0;
        }
      }
    },

    _nextSlide: function() {
      var params = this.params;

      if (params.slideDirection === "toLeft") {
        if (params.currentSlide < this.element.find(params.itemSelector).length - 1) {
          return params.currentSlide + 1;
        } else {
          return 0;
        }
      } else {
        if (params.currentSlide > 0) {
          return params.currentSlide - 1;
        } else {
          return this.element.find(params.itemSelector).length - 1;
        }
      }
    },

    _updateCurrentSlide: function() {
      this.params.currentSlide = this._nextSlide();

      this._loadSlide();
      this._slideElementsIn();
    },

    _loadSlide: function() {
      var self = this;
      var params = this.params;

      var slidesIdx = [
        params.currentSlide,
        this._previousSlide(),
        this._nextSlide()
      ];

      for (var i = 0; i < slidesIdx.length; i++) {
        this.element.find(params.itemSelector).eq(slidesIdx[i]).each(function(i) {
          if (!$(this).hasClass(params.itemLoadedClass)) {
            var element = $(this);

            element.addClass(params.itemLoadedClass);
            params.onSlideLoad(self, element);
          }
        });
      }
    },

    _slideElementsIn: function() {
      var self = this;
      var params = this.params;

      var current = this.element.find(params.itemSelector).eq(params.currentSlide);
      var elementNum = current.find(params.itemForegroundElementSelector).length;

      this._setElementPositions();

      current.each(function() {
        $(this).find(params.itemForegroundElementSelector).each(function(i) {
          if (params.inited) {
            $(this).css({left: $(this).data("xPos"), top: $(this).data("yPos")});

            if (elementNum - 1 === i && !params.mouseOver && params.slideSum > 1) {
              params.autoId = setTimeout(function() {
                self._slideElementsOut();
              }, params.autoSlideDelay);

              params.animating = false;
            }
          } else {
            params.animating = true;

            $(this).stop().css({left: $(this).data("slidePos"), top: $(this).data("yPos")});
            $(this).animate({left: $(this).data("xPos")}, params.elementSpeed, params.easingType, function() {
              if (elementNum - 1 === i && params.slideSum > 1) {
                if (!params.mouseOver) {
                  params.autoId = setTimeout(function() {
                    self._slideElementsOut();
                  }, params.autoSlideDelay);
                }

                params.autoTimeStamp = new Date().getTime();
                params.animating = false;
              }
            });
          }
        });
      });
    },

    _slideElementsOut: function() {
      var params = this.params;

      if (params.slideSum < 2) {
        return;
      }

      if (this._updatePagers("beforePageUpdate") === false) {
        return;
      };

      params.animating = true;
      clearTimeout(params.autoId);

      this.element.find(params.itemSelector + "." + params.activeItemClass + " " + params.itemForegroundElementSelector).each(function() {
        var pos = params.elementDirection === "fromLeft" ? $(this).data("slidePos") * -1 + params.baseSize - $(this).width() : $(this).data("slidePos") * -1;
        $(this).stop().animate({left: pos}, params.elementSpeed, params.easingType);
      });

      this._moveSlide();
      this._updatePagers("onPageUpdate");
    }
  };
})(jQuery, window);
<commit_msg>Fixing a problem that occurs when specifing an invalid slide index.<commit_after>(function ($, window, undefined) {
  $.fn.destaque = function(params) {
    params = $.extend($.fn.destaque.options, params);
    return new Destaque(this, params);
  };

  $.fn.destaque.options = {
    currentSlide: 0,
    slideSpeed: 1000,
    slideMovement: 150,
    slideDirection: "toLeft",
    elementSpeed: 1000,
    autoSlideDelay: 3000,
    resumeSlideDelay: 250,
    stopOnMouseOver: true,
    easingType: "easeInOutExpo",
    itemDefaultZIndex: 10,
    itemSelector: "div.item",
    itemLoadedClass: "loaded",
    activeItemClass: "active",
    itemForegroundElementSelector: ".foreground img",

    // Callbacks
    onInit: function (slideshow) {},
    onResize: function (slideshow) {},
    onPause: function(slideshow) {},
    onResume: function(slideshow) {},
    onPageUpdate: function(slideshow, pageData) {},
    onSlideLoad: function(slideshow, element) {},
    beforePageUpdate: function(slideshow, pageData) {}
  };

  var Destaque = function (element, params) {
    this.element = $(element);
    this.params = params;

    this.params.inited = true
    this.params.mouseOver = false;
    this.params.animating = false;

    this.params.autoId = 0;
    this.params.autoTimeStamp = 0;

    this._initElements();
    this._initListeners();

    this.params.onInit(this);
  };

  Destaque.prototype = {
    _initListeners: function() {
      this._initPaginationListeners();
      this._initMouseListeners();
      this._initKeyboardListeners();
      this._initMobileListeners();
      this._initWindowListeners();
    },

    _initPaginationListeners: function () {
      var self = this;

      if (!this.params.controlsSelector) {
        return;
      }

      $(this.params.controlsSelector).each(function() {
        $(this).bind("click.destaque", function(e) {
          e.preventDefault();

          var direction = $(e.currentTarget).attr("rel") === "prev" ? "toRight" : "toLeft";
          self.slideSetAndMove(direction);
        });
      });
    },

    _initMobileListeners: function() {
      var self = this;

      this.element.bind("swipeleft.destaque", function(e) {
        self.slideSetAndMove("toLeft");
       });

      this.element.bind("swiperight.destaque", function (e) {
        self.slideSetAndMove("toRight");
      });
    },

    _initMouseListeners: function () {
      var params = this.params;
      var self = this;

      if (params.slideSum > 1) {
        if (params.stopOnMouseOver) {
          this.element.mouseover(function() {
            self.pause();
          });

          this.element.mouseleave(function() {
            self.resume();
          });
        }
      }
    },

    _initKeyboardListeners: function() {
      var self = this;

      $(window).bind("keydown.destaque", function(e) {
        if (e.keyCode === 37) {
          self.slideSetAndMove("toRight");
        } else {
          if (e.keyCode === 39) {
            self.slideSetAndMove("toLeft");
          }
        }
      });
    },

    _initWindowListeners: function() {
      var self = this;

      $(window).bind("resize.destaque", function() {
        self.params.onResize(self);
      });
    },

    _initElements: function () {
      var element = this.element;
      var params = this.params;

      if (params.inited) {
        params.baseSize = element.width();
        params.slideSum = element.find(this.params.itemSelector).length;
        params.elementDirection = params.slideDirection === "toLeft" ? "fromLeft" : "toLeft";

        if (prams.currentSlide < 0 || params.currentSlide >= params.slideSum) {
          params.currentSlide = 0;
        }

        this._initSlide();

        params.inited = false;
      }
    },

    _initSlide: function() {
      var self = this;
      var params = this.params;

      this.element.find(params.itemSelector).each(function() {
        $(this).data("zIndex", params.itemDefaultZIndex).css({zIndex: params.itemDefaultZIndex});
      });

      this.element.find(params.itemForegroundElementSelector).each(function (idx, e) {
        var e = $(this);

        var initialX = parseInt(e.css("left"), 10) + (self.element.width() - params.baseSize) / 2;
        var initialY = parseInt(e.css("top"), 10);

        e.css({top: initialY, left: params.baseSize});
        e.data("xPos", initialX).data("yPos", initialY).data("slidePos", 9999);
      });

      this._loadSlide();
      this._currentSlide().addClass(params.activeItemClass).show();
      this._slideElementsIn();

      params.autoTimeStamp = new Date().getTime();
    },

    pause: function() {
      var params = this.params;

      if (!params.animating) {
        params.mouseOver = true;
        clearTimeout(params.autoId);
        params.onPause(this);
      }
    },

    resume: function() {
      var self = this;
      var params = this.params;

      if (!params.animating) {
        params.mouseOver = false;

        var timeout = 0;

        if (params.autoSlideDelay - (new Date().getTime() - params.autoTimeStamp) < 0) {
          timeout = params.resumeSlideDelay;
        } else {
          timeout = params.autoSlideDelay - (new Date().getTime() - params.autoTimeStamp);
        }

        params.autoId = setTimeout(function() {
          self._slideElementsOut();
        }, timeout);

        params.onResume(this);
      }
    },

    slideSetAndMove: function(direction) {
      var params = this.params;

      if (!params.animating) {
        if (direction === "toLeft") {
          params.slideDirection = "toLeft";
          params.elementDirection = "fromLeft";
          this.element.removeClass("right").addClass("left");
        } else {
          params.slideDirection = "toRight";
          params.elementDirection = "fromRight";
          this.element.removeClass("left").addClass("right");
        }

        this._setElementPositions(true);
        this._slideElementsOut();
      }
    },

    _setElementPositions: function(justCalculate) {
      var params = this.params;
      var element, pos = 0;

      this._currentSlide().find(params.itemForegroundElementSelector).each(function(i) {
        element = $(this);

        if (params.elementDirection === "fromLeft") {
          pos = params.baseSize + (i * params.baseSize) - (i > 0 ? parseInt(element.data("xPos"), 10) : 0);
        } else {
          pos = -params.baseSize - (i * params.baseSize) + (i > 0 ? parseInt(element.data("xPos"), 10) : 0);
        }

        element.data("slidePos", pos);

        if (!justCalculate) {
          element.css({left: pos});
        }
      });
    },

    _moveSlide: function() {
      var self = this;
      var params = this.params;

      var current = this.element.find(params.itemSelector + "." + params.activeItemClass);
      var next = null;
      var outPosition = 0;
      var pos = 0;

      params.animating = true;

      if (params.slideDirection === "toLeft") {
        outPosition = -params.slideMovement;
        next = this.element.find(params.itemSelector).eq(params.currentSlide < this.element.find(params.itemSelector).length - 1 ? params.currentSlide + 1 : 0);

        if (next.length) {
          next.css({left: params.baseSize, zIndex: params.itemDefaultZIndex + 1}).show();
        }
      } else {
        outPosition = params.slideMovement;
        next = this.element.find(params.itemSelector).eq(params.currentSlide < 1 ? this.element.find(params.itemSelector).length - 1 : params.currentSlide - 1);

        if (next.length) {
          next.css({left: -params.baseSize, zIndex: params.itemDefaultZIndex + 1}).show();
        }
      }

      next.stop().animate({left: pos}, params.slideSpeed, params.easingType, function() {
        next.addClass(params.activeItemClass);
      });

      current.css({zIndex: params.itemDefaultZIndex}).stop().animate({left: outPosition}, params.slideSpeed, params.easingType, function() {
        $(this).removeClass(params.activeItemClass).hide();
      });

      this._updateCurrentSlide();
    },

    _updatePagers: function(callback) {
      var params = this.params;
      var data = {
        currentPage: params.currentSlide,
        totalPages:  params.slideSum
      };

      return params[callback](this, data);
    },

    _currentSlide: function() {
      var params = this.params;
      return this.element.find(params.itemSelector).eq(params.currentSlide);
    },

    _previousSlide: function() {
      var params = this.params;

      if (params.slideDirection === "toLeft") {
        if (params.currentSlide > 0) {
          return params.currentSlide - 1;
        } else {
          return this.element.find(params.itemSelector).length - 1;
        }
      } else {
        if (params.currentSlide < this.element.find(params.itemSelector).length - 1) {
          return params.currentSlide + 1;
        } else {
          return 0;
        }
      }
    },

    _nextSlide: function() {
      var params = this.params;

      if (params.slideDirection === "toLeft") {
        if (params.currentSlide < this.element.find(params.itemSelector).length - 1) {
          return params.currentSlide + 1;
        } else {
          return 0;
        }
      } else {
        if (params.currentSlide > 0) {
          return params.currentSlide - 1;
        } else {
          return this.element.find(params.itemSelector).length - 1;
        }
      }
    },

    _updateCurrentSlide: function() {
      this.params.currentSlide = this._nextSlide();

      this._loadSlide();
      this._slideElementsIn();
    },

    _loadSlide: function() {
      var self = this;
      var params = this.params;

      var slidesIdx = [
        params.currentSlide,
        this._previousSlide(),
        this._nextSlide()
      ];

      for (var i = 0; i < slidesIdx.length; i++) {
        this.element.find(params.itemSelector).eq(slidesIdx[i]).each(function(i) {
          if (!$(this).hasClass(params.itemLoadedClass)) {
            var element = $(this);

            element.addClass(params.itemLoadedClass);
            params.onSlideLoad(self, element);
          }
        });
      }
    },

    _slideElementsIn: function() {
      var self = this;
      var params = this.params;

      var current = this.element.find(params.itemSelector).eq(params.currentSlide);
      var elementNum = current.find(params.itemForegroundElementSelector).length;

      this._setElementPositions();

      current.each(function() {
        $(this).find(params.itemForegroundElementSelector).each(function(i) {
          if (params.inited) {
            $(this).css({left: $(this).data("xPos"), top: $(this).data("yPos")});

            if (elementNum - 1 === i && !params.mouseOver && params.slideSum > 1) {
              params.autoId = setTimeout(function() {
                self._slideElementsOut();
              }, params.autoSlideDelay);

              params.animating = false;
            }
          } else {
            params.animating = true;

            $(this).stop().css({left: $(this).data("slidePos"), top: $(this).data("yPos")});
            $(this).animate({left: $(this).data("xPos")}, params.elementSpeed, params.easingType, function() {
              if (elementNum - 1 === i && params.slideSum > 1) {
                if (!params.mouseOver) {
                  params.autoId = setTimeout(function() {
                    self._slideElementsOut();
                  }, params.autoSlideDelay);
                }

                params.autoTimeStamp = new Date().getTime();
                params.animating = false;
              }
            });
          }
        });
      });
    },

    _slideElementsOut: function() {
      var params = this.params;

      if (params.slideSum < 2) {
        return;
      }

      if (this._updatePagers("beforePageUpdate") === false) {
        return;
      };

      params.animating = true;
      clearTimeout(params.autoId);

      this.element.find(params.itemSelector + "." + params.activeItemClass + " " + params.itemForegroundElementSelector).each(function() {
        var pos = params.elementDirection === "fromLeft" ? $(this).data("slidePos") * -1 + params.baseSize - $(this).width() : $(this).data("slidePos") * -1;
        $(this).stop().animate({left: pos}, params.elementSpeed, params.easingType);
      });

      this._moveSlide();
      this._updatePagers("onPageUpdate");
    }
  };
})(jQuery, window);
<|endoftext|>


<commit_before>var DirectorySelectorView = fileBrowser.FileExplorer.extend({

  initialize: function() {
    this.structure = {};
    this.options.closeDirsByDefault = true;
    this.options.hideFiles = true;

    this.render();

    this.options.actionHandlers = []
  }
});

function createDirectoryPicker(baseDirectory, modalCssId, targetCssId) {
  var url = '/filesystem/contents/?path=' + encodeURIComponent(baseDirectory)

  var selector = new DirectorySelectorView({
    el: $('#explorer'),
    levelTemplate: $('#template-dir-level').html(),
    entryTemplate: $('#template-dir-entry').html()
  });

  selector.options.actionHandlers.push({
    name: 'Select',
    description: 'Select',
    iconHtml: 'Add',
    logic: function(result) {
      var $transferPathRowEl = $('

')
        , $transferPathEl = $('')
        , $transferPathDeleteRl = $('');

      $transferPathDeleteRl.click(function() {
        $transferPathRowEl.remove();
      });

      $transferPathEl.html('/' + result.path);
      $transferPathRowEl.append($transferPathEl);
      $transferPathRowEl.append($transferPathDeleteRl);
      $('#' + targetCssId).append($transferPathRowEl);
      $('#' + modalCssId).remove();

      // tiger stripe transfer paths
      $('.transfer_path').each(function() {
        $(this).parent().css('background-color', '');
      });
      $('.transfer_path:odd').each(function() {
        $(this).parent().css('background-color', '#eee');
      });
    }
  });

  selector.busy();

  $.get(url, function(results) {
    selector.structure = results;
    selector.render();

    selector.idle();
  });
}
<commit_msg>Tweak to look of transfer component picker selected paths.<commit_after>var DirectorySelectorView = fileBrowser.FileExplorer.extend({

  initialize: function() {
    this.structure = {};
    this.options.closeDirsByDefault = true;
    this.options.hideFiles = true;

    this.render();

    this.options.actionHandlers = []
  }
});

function createDirectoryPicker(baseDirectory, modalCssId, targetCssId) {
  var url = '/filesystem/contents/?path=' + encodeURIComponent(baseDirectory)

  var selector = new DirectorySelectorView({
    el: $('#explorer'),
    levelTemplate: $('#template-dir-level').html(),
    entryTemplate: $('#template-dir-entry').html()
  });

  selector.options.actionHandlers.push({
    name: 'Select',
    description: 'Select',
    iconHtml: 'Add',
    logic: function(result) {
      var $transferPathRowEl = $('

')
        , $transferPathEl = $('')
        , $transferPathDeleteRl = $('');

      $transferPathDeleteRl.click(function() {
        $transferPathRowEl.remove();
      });

      $transferPathEl.html('/' + result.path);
      $transferPathRowEl.append($transferPathEl);
      $transferPathRowEl.append($transferPathDeleteRl);
      $('#' + targetCssId).append($transferPathRowEl);
      $('#' + modalCssId).remove();

      // tiger stripe transfer paths
      $('.transfer_path').each(function() {
        $(this).parent().css('background-color', '');
      });
      $('.transfer_path:odd').each(function() {
        $(this).parent().css('background-color', '#eee');
      });
    }
  });

  selector.busy();

  $.get(url, function(results) {
    selector.structure = results;
    selector.render();

    selector.idle();
  });
}
<|endoftext|>


<commit_before>// Copyright 2014 Technical Machine, Inc. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

var tm = process.binding('tm');
var http_parser = process.binding('http_parser');

var url = require('url');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var net = require('net');
var dns = require('dns');
var Readable = require('stream').Readable;
var Writable = require('stream').Writable;

/**
 * ServerResponse
 */

function ServerResponse (req, connection) {
  Writable.call(this);
  this.req = req;
  this.connection = connection;
  this.headers = {};
  this._header = false;
  this._closed = false;

  var self = this;
  this.req.on('end', function () {
    self._closed = true;
  })
  this.req.on('close', function () {
    self._closed = true;
  })
}

// TODO stream
util.inherits(ServerResponse, Writable);

ServerResponse.prototype.setHeader = function (name, value) {
  if (this._header) {
    throw "Already wrote HEAD";
  }
  this.headers[String(name).toLowerCase()] = value;
};

ServerResponse.prototype.setHeaders = function (headers) {
  for (var key in headers) {
    this.setHeader(key, headers[key]);
  }
};

ServerResponse.prototype.writeHead = function (status, headers) {
  if (this._header) {
    throw "Already wrote HEAD";
  }

  if (headers) {
    this.setHeaders(headers);
  }
  this.connection.write('HTTP/1.1 ' + status + ' OK\r\n');
  this._usesContentType = false;
  for (var key in this.headers) {
    if (key.toLowerCase() == 'content-length') {
      this._usesContentType = true;
    }
    this.connection.write(key + ': ' + this.headers[key] + '\r\n');
  }
  if (!this._usesContentType) {
    this.connection.write('Transfer-Encoding: chunked\r\n');
  }
  this.connection.write('\r\n');
  this._header = true;
  // console.log('writing headers', headers);
};

ServerResponse.prototype._write = function (chunk, encoding, callback) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (!this._usesContentType) {
    this.connection.write(Number(chunk.length).toString(16));
    this.connection.write('\r\n');
    this.connection.write(chunk);
    this.connection.write('\r\n');
  } else {
    this.connection.write(chunk);
  }
  callback();
}

ServerResponse.prototype.getHeader = function (key) {
  return this.headers[key.toLowerCase()];
};

ServerResponse.prototype.end = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (data != null) {
    this.write(data);
  }
  this._closed = true;
  if (!this._usesContentType) {
    this.connection.write('0\r\n\r\n');
  }
  this.connection.close();
};

/**
 * ServerRequest
 */

function ServerRequest (connection) {
  Readable.call(this);
  var self = this;

  this.headers = {};
  this.connection = connection;
  this.url = null;

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var lastheader;
  var parser = http_parser.new('request', {
    onMessageBegin: js_wrap_function(function () {
    }),
    onUrl: js_wrap_function(function (url) {
      self.url = url;
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      self.headers[lastheader.toLowerCase()] = value;
    }),
    onHeadersComplete: js_wrap_function(function (info) {
      self.method = info.method;
      self.emit('request', self);
    }),
    onBody: js_wrap_function(function (body) {
      self.push(body);
    }),
    onMessageComplete: js_wrap_function(function () {
      self.push(null);
      self._closed = true;
      // TODO close
    }),
    onError: js_wrap_function(function (err) {
      self.connection.emit('error', err)
    })
  })
  this.connection.on('data', function (data) {
    // console.log('received', data.length, data.substr(0, 15));
    parser.execute(data, 0, data.length);
  })
}

util.inherits(ServerRequest, Readable);

function HTTPServer () {
  var self = this;
  this.connection = net.createServer(function (connection) {
    var request = new ServerRequest(connection);
    var response = new ServerResponse(request, connection);
    request.on('request', function () {
      self.emit('request', request, response)
    });
  });
}

util.inherits(HTTPServer, EventEmitter);

HTTPServer.prototype.listen = function (port, ip) {
  this.connection.listen(port, ip);
};

/**
 * HTTPIncomingResponse
 */

function HTTPIncomingResponse (data) {
  Readable.call(this);
  this.headers = {};
}

util.inherits(HTTPIncomingResponse, Readable);

HTTPIncomingResponse.prototype._read = function () {
  // noop
};

HTTPIncomingResponse.prototype.setEncoding = function () {
  // TODO
};

/**
 * HTTPOutgoingRequest
 */

function isIP (host) {
  return host.match(/^[0-9.]+$/);
}

function HTTPOutgoingRequest (port, host, path, method, headers, _secure) {
  var self = this;

  if (path[0] != '/') {
    path = '/' + path;
  }

  self._connected = false;
  self._contentLength = 0;
  self.connection = net.connect(port, host, function () {
    self._connected = true;
    var header = '';
    var usedHost = false, usedUserAgent = false, usedContentLength = false;
    for (var key in headers) {
      if (key.toLowerCase() == 'host') {
        usedHost = true;
      } else if (key.toLowerCase() == 'user-agent') {
        usedUserAgent = true;
      } else if (key.toLowerCase() == 'content-length') {
        usedContentLength = true;
      }
      header = header + key + ': ' + headers[key] + '\r\n';
    }
    if (!usedHost) {
      header = header + 'Host: ' + host + '\r\n'
    }
    if (!usedUserAgent) {
      header = header + 'User-Agent: tessel\r\n';
    }
    if (!usedContentLength && method != 'GET') {
      header = header + 'Content-Length: ' + self._contentLength + '\r\n';
    }
    self.connection.write(method + ' ' + path + ' HTTP/1.1\r\n' + header + '\r\n');
    // self.emit('connect');
  }, _secure)

  self._outgoing = [];
  self.connection.once('connect', function () {
    for (var i = 0; i < self._outgoing.length; i++) {
      self.connection.write(self._outgoing[i]);
    }
    self._outgoing = [];
  })

  self.connection.on('error', function (err) {
    self.emit('error', err);
  })

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var response, lastheader;
  var parser = http_parser.new('response', {
    onMessageBegin: js_wrap_function(function () {
      response = new HTTPIncomingResponse();
      self.connection.on('close', function () {
        response.push(null);
        response = null;
      });
    }),
    onUrl: js_wrap_function(function (url) {
      // nop
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      if (response) {
        response.headers[lastheader.toLowerCase()] = value;
      }
    }),
    onHeadersComplete: js_wrap_function(function (obj) {
      if (response) {
        response.statusCode = obj.status_code
        self.emit('response', response);
      }
    }),
    onBody: js_wrap_function(function (body) {
      if (response) {
        response.push(body);
      }
    }),
    onMessageComplete: js_wrap_function(function () {
      if (response) {
        self.connection.close();
      }
    })
  })
  self.connection.on('data', function (data) {
    var nparsed = parser.execute(data, 0, data.length);
    if (nparsed != data.length) {
      self.connection.emit('error', 'Could not parse tokens at character #' + String(nparsed));
    }
  })

  self.emit('connection', self.connection);
}

util.inherits(HTTPOutgoingRequest, EventEmitter);

HTTPOutgoingRequest.prototype.write = function (data) {
  this._contentLength += data.length;
  if (this._connected) {
    this.connection.write(data);
  } else {
    this._outgoing.push(data)
  }
};

HTTPOutgoingRequest.prototype.end = function () {
  // TODO
  // console.log('end');
};

/**
 * Agent
 */

function Agent () {
  throw new Error('Agent not yet implemented.');
}

/**
 * Public API
 */

function ensureSecure (secure) {
  if (secure) {
    if (this._secure && !process.binding('tm').ssl_context_create) {
      throw new Error("SSL/TLS is not supported in this firmware build.");
    }
  }
}

exports.request = function (opts, onresponse) {
  ensureSecure(this._secure);
  var host = opts.hostname || opts.host || 'localhost';
  var req = new HTTPOutgoingRequest(opts.port || (this._secure ? 443 : 80), host, opts.path || '', opts.method || 'GET', opts.headers || {}, this._secure);
  onresponse && req.on('response', onresponse);
  return req;
};

exports.get = function (opts, onresponse) {
  if (typeof opts == 'string') {
    opts = url.parse(opts);
    if (opts.search) {
      opts.path += opts.search;
    }
  }
  opts.method = 'GET';

  var req = this.request(opts, onresponse);
  // req.end();
  return req;
};

function createServer (onrequest) {
  var server = new HTTPServer();
  onrequest && server.on('request', onrequest);
  return server;
};

exports.Server = exports.createServer = createServer;
exports.Agent = Agent;
exports.ServerResponse = ServerResponse;
exports.ServerRequest = ServerRequest;
exports.IncomingMessage = ServerRequest;
<commit_msg>Adds minor HTTP fixes to make request module usable.<commit_after>// Copyright 2014 Technical Machine, Inc. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

var tm = process.binding('tm');
var http_parser = process.binding('http_parser');

var url = require('url');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var net = require('net');
var dns = require('dns');
var Readable = require('stream').Readable;
var Writable = require('stream').Writable;

/**
 * ServerResponse
 */

function ServerResponse (req, connection) {
  Writable.call(this);
  this.req = req;
  this.connection = connection;
  this.headers = {};
  this._header = false;
  this._closed = false;

  var self = this;
  this.req.on('end', function () {
    self._closed = true;
  })
  this.req.on('close', function () {
    self._closed = true;
  })
}

// TODO stream
util.inherits(ServerResponse, Writable);

ServerResponse.prototype.setHeader = function (name, value) {
  if (this._header) {
    throw "Already wrote HEAD";
  }
  this.headers[String(name).toLowerCase()] = value;
};

ServerResponse.prototype.setHeaders = function (headers) {
  for (var key in headers) {
    this.setHeader(key, headers[key]);
  }
};

ServerResponse.prototype.writeHead = function (status, headers) {
  if (this._header) {
    throw "Already wrote HEAD";
  }

  if (headers) {
    this.setHeaders(headers);
  }
  this.connection.write('HTTP/1.1 ' + status + ' OK\r\n');
  this._usesContentType = false;
  for (var key in this.headers) {
    if (key.toLowerCase() == 'content-length') {
      this._usesContentType = true;
    }
    this.connection.write(key + ': ' + this.headers[key] + '\r\n');
  }
  if (!this._usesContentType) {
    this.connection.write('Transfer-Encoding: chunked\r\n');
  }
  this.connection.write('\r\n');
  this._header = true;
  // console.log('writing headers', headers);
};

ServerResponse.prototype._write = function (chunk, encoding, callback) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (!this._usesContentType) {
    this.connection.write(Number(chunk.length).toString(16));
    this.connection.write('\r\n');
    this.connection.write(chunk);
    this.connection.write('\r\n');
  } else {
    this.connection.write(chunk);
  }
  callback();
}

ServerResponse.prototype.getHeader = function (key) {
  return this.headers[key.toLowerCase()];
};

ServerResponse.prototype.end = function (data) {
  if (!this._header) {
    this.writeHead(200);
  }

  if (data != null) {
    this.write(data);
  }
  this._closed = true;
  if (!this._usesContentType) {
    this.connection.write('0\r\n\r\n');
  }
  this.connection.close();
};

/**
 * ServerRequest
 */

function ServerRequest (connection) {
  Readable.call(this);
  var self = this;

  this.headers = {};
  this.connection = connection;
  this.url = null;

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var lastheader;
  var parser = http_parser.new('request', {
    onMessageBegin: js_wrap_function(function () {
    }),
    onUrl: js_wrap_function(function (url) {
      self.url = url;
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      self.headers[lastheader.toLowerCase()] = value;
    }),
    onHeadersComplete: js_wrap_function(function (info) {
      self.method = info.method;
      self.emit('request', self);
    }),
    onBody: js_wrap_function(function (body) {
      self.push(body);
    }),
    onMessageComplete: js_wrap_function(function () {
      self.push(null);
      self._closed = true;
      // TODO close
    }),
    onError: js_wrap_function(function (err) {
      self.connection.emit('error', err)
    })
  })
  this.connection.on('data', function (data) {
    // console.log('received', data.length, data.substr(0, 15));
    parser.execute(data, 0, data.length);
  })
}

util.inherits(ServerRequest, Readable);

function HTTPServer () {
  var self = this;
  this.connection = net.createServer(function (connection) {
    var request = new ServerRequest(connection);
    var response = new ServerResponse(request, connection);
    request.on('request', function () {
      self.emit('request', request, response)
    });
  });
}

util.inherits(HTTPServer, EventEmitter);

HTTPServer.prototype.listen = function (port, ip) {
  this.connection.listen(port, ip);
};

/**
 * HTTPIncomingResponse
 */

function HTTPIncomingResponse (connection) {
  Readable.call(this);
  this.headers = {};
  this.connection = connection;
}

util.inherits(HTTPIncomingResponse, Readable);

HTTPIncomingResponse.prototype._read = function () {
  // noop
};

HTTPIncomingResponse.prototype.setEncoding = function (encoding) {
  this.encoding = encoding;
};

/**
 * HTTPOutgoingRequest
 */

function isIP (host) {
  return host.match(/^[0-9.]+$/);
}

function HTTPOutgoingRequest (port, host, path, method, headers, _secure) {
  var self = this;

  if (path[0] != '/') {
    path = '/' + path;
  }

  self._connected = false;
  self._contentLength = 0;
  self.connection = net.connect(port, host, function () {
    self._connected = true;
    var header = '';
    var usedHost = false, usedUserAgent = false, usedContentLength = false;
    for (var key in headers) {
      if (key.toLowerCase() == 'host') {
        usedHost = true;
      } else if (key.toLowerCase() == 'user-agent') {
        usedUserAgent = true;
      } else if (key.toLowerCase() == 'content-length') {
        usedContentLength = true;
      }
      header = header + key + ': ' + headers[key] + '\r\n';
    }
    if (!usedHost) {
      header = header + 'Host: ' + host + '\r\n'
    }
    if (!usedUserAgent) {
      header = header + 'User-Agent: tessel\r\n';
    }
    if (!usedContentLength && method != 'GET') {
      header = header + 'Content-Length: ' + self._contentLength + '\r\n';
    }
    self.connection.write(method + ' ' + path + ' HTTP/1.1\r\n' + header + '\r\n');
    // self.emit('connect');
  }, _secure)

  self._outgoing = [];
  self.connection.once('connect', function () {
    for (var i = 0; i < self._outgoing.length; i++) {
      self.connection.write(self._outgoing[i]);
    }
    self._outgoing = [];
  })

  self.connection.on('error', function (err) {
    self.emit('error', err);
  })

  function js_wrap_function (fn) {
    return function () {
      return fn.apply(null, [this].concat(arguments));
    }
  }

  var response, lastheader;
  var parser = http_parser.new('response', {
    onMessageBegin: js_wrap_function(function () {
      response = new HTTPIncomingResponse(self.connection);
      self.connection.on('close', function () {
        response.push(null);
        response = null;
      });
    }),
    onUrl: js_wrap_function(function (url) {
      // nop
    }),
    onHeaderField: js_wrap_function(function (field) {
      lastheader = field;
    }),
    onHeaderValue: js_wrap_function(function (value) {
      if (response) {
        response.headers[lastheader.toLowerCase()] = value;
      }
    }),
    onHeadersComplete: js_wrap_function(function (obj) {
      if (response) {
        response.statusCode = obj.status_code
        self.emit('response', response);
      }
    }),
    onBody: js_wrap_function(function (body) {
      if (response) {
        response.push(body);
      }
    }),
    onMessageComplete: js_wrap_function(function () {
      if (response) {
        self.connection.close();
      }
    })
  })
  self.connection.on('data', function (data) {
    var nparsed = parser.execute(data, 0, data.length);
    if (nparsed != data.length) {
      self.connection.emit('error', 'Could not parse tokens at character #' + String(nparsed));
    }
  })

  self.emit('connection', self.connection);
}

util.inherits(HTTPOutgoingRequest, EventEmitter);

HTTPOutgoingRequest.prototype.write = function (data) {
  this._contentLength += data.length;
  if (this._connected) {
    this.connection.write(data);
  } else {
    this._outgoing.push(data)
  }
};

HTTPOutgoingRequest.prototype.end = function () {
  // TODO
  // console.log('end');
};

/**
 * Agent
 */

function Agent () {
  // NYI
}

/**
 * Public API
 */

function ensureSecure (secure) {
  if (secure) {
    if (this._secure && !process.binding('tm').ssl_context_create) {
      throw new Error("SSL/TLS is not supported in this firmware build.");
    }
  }
}

exports.request = function (opts, onresponse) {
  ensureSecure(this._secure);
  if (opts.agent) {
    throw new Error('Agent not yet implemented.');
  }
  var host = opts.hostname || opts.host || 'localhost';
  var req = new HTTPOutgoingRequest(opts.port || (this._secure ? 443 : 80), host, opts.path || '', opts.method || 'GET', opts.headers || {}, this._secure);
  onresponse && req.on('response', onresponse);
  return req;
};

exports.get = function (opts, onresponse) {
  if (typeof opts == 'string') {
    opts = url.parse(opts);
    if (opts.search) {
      opts.path += opts.search;
    }
  }
  opts.method = 'GET';
  if (opts.agent) {
    throw new Error('Agent not yet implemented.');
  }

  var req = this.request(opts, onresponse);
  // req.end();
  return req;
};

function createServer (onrequest) {
  var server = new HTTPServer();
  onrequest && server.on('request', onrequest);
  return server;
};

exports.Server = exports.createServer = createServer;
exports.Agent = Agent;
exports.ServerResponse = ServerResponse;
exports.ServerRequest = ServerRequest;
exports.IncomingMessage = ServerRequest;
<|endoftext|>


<commit_before>let matrix = {
  years: {
    months: 12,
    days: 365,
    hours: 365 * 24,
    minutes: 365 * 24 * 60,
    seconds: 365 * 24 * 60 * 60,
    milliseconds: 365 * 24 * 60 * 60 * 1000

  },
  months: {
    days: 30,
    hours: 30 * 24,
    minutes: 30 * 24 * 60,
    seconds: 30 * 24 * 60 * 60,
    milliseconds: 30 * 24 * 60 * 60 * 1000
  },
  days: {
    hours: 24,
    minutes: 24 * 60,
    seconds: 24 * 60 * 60,
    milliseconds: 24 * 60 * 60 * 1000
  },
  hours: {
    minutes: 60,
    seconds: 60 * 60,
    milliseconds: 60 * 60 * 1000
  },
  minutes: {
    seconds: 60,
    milliseconds: 60 * 1000
  },
  seconds: {
    milliseconds: 1000
  }
};

let ordered = ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'];

function ensure(unit){
  let normalized = unit.endsWith('s') ? unit : unit + 's';
  if (ordered.indexOf(normalized) === -1){
    throw "Invalid unit " + unit;
  }
  return normalized;
}

function friendlyDuration(durationOrNumber, type){
  return typeof durationOrNumber === 'number' ?
    Duration.fromLength(durationOrNumber, type) :
    durationOrNumber;
}

export class Duration{

  constructor(obj){
    this.values = obj;
  }

  _clone(alts) {
    new Duration(Object.assign(this.values), alts);
  }

  static fromLength(count, unit){
    unit = ensure(unit);
    return Duration.fromObject({[unit]: count});
  }

  static fromObject(obj){
    //todo - ensure() each key
    return new Duration(obj);
  }

  static fromISO(text){}

  static fromString(text, fmt){}

  toFormatString(fmt){}

  toObject(){
    return Object.assign({}, this.values);
  }

  toISO(){}

  toJSON(){}

  plus(countOrDuration, unit='milliseconds'){
    let dur = friendlyDuration(countOrDuration, unit),
        result = {};

    for (let k of ordered){
      let val = dur.get(k) + this.get(k);
      if (val != 0){
        result[k] = val;
      }
    }

    return Duration.fromObject(result);
  }

  minus(countOrDuration, unit='milliseconds'){
    let dur = friendlyDuration(countOrDuration, unit);
    return this.plus(dur.negate());
  }

  get(unit){
    return this[unit]();
  }

  as(unit){
    return this.shiftTo(unit).get(unit);
  }

  shiftTo(...units){
    if (units.length == 0){
      return this;
    }

    let built = {},
        accumulated = {},
        vals = this.toObject(),
        lastUnit;

    for (let k of ordered) {

      if (units.indexOf(k) >= 0){
        built[k] = 0;
        lastUnit = k;

        //anything we haven't boiled down yet should get boiled to this unit
        for (let ak in accumulated){
          built[k] += matrix[ak][k] * accumulated[ak];
          delete(accumulated[ak]);
        }

        //plus anything that's already in this unit
        if (typeof(vals[k]) === 'number'){
          built[k] += vals[k];
        }

        //plus anything further down the chain that should be rolled up in to this
        for(let down in vals){
          if (ordered.indexOf(down) > ordered.indexOf(k)){
            let conv = matrix[k][down],
                added = Math.floor(vals[down] / conv);
            built[k] += added;
            vals[down] -= added * conv;
          }
        }
      }

      //otherwise, keep it in the wings to boil it later
      else if (typeof(vals[k]) === 'number'){
        accumulated[k] = vals[k];
      }
    }

    //anything leftover becomes the decimal for the last unit
    if (lastUnit){
      for (let key in accumulated){
        built[lastUnit] += accumulated[key] / matrix[lastUnit][key];
      }
    }

    return Duration.fromObject(built);
  }

  negate(){
    let negated = {};
    for(let k of Object.keys(this.values)){
      negated[k] = -this.values[k];
    }
    return Duration.fromObject(negated);
  };

  years(){
    return this.values.years || 0;
  }

  months(){
    return this.values.months || 0;
  }

  days(){
    return this.values.days || 0;
  }

  hours(){
    return this.values.hours || 0;
  }

  minutes(){
    return this.values.minutes || 0;
  }

  seconds(){
    return this.values.seconds || 0;
  }

  milliseconds(){
    return this.values.milliseconds || 0;
  }
}
<commit_msg>cleanup<commit_after>let matrix = {
  years: {
    months: 12,
    days: 365,
    hours: 365 * 24,
    minutes: 365 * 24 * 60,
    seconds: 365 * 24 * 60 * 60,
    milliseconds: 365 * 24 * 60 * 60 * 1000

  },
  months: {
    days: 30,
    hours: 30 * 24,
    minutes: 30 * 24 * 60,
    seconds: 30 * 24 * 60 * 60,
    milliseconds: 30 * 24 * 60 * 60 * 1000
  },
  days: {
    hours: 24,
    minutes: 24 * 60,
    seconds: 24 * 60 * 60,
    milliseconds: 24 * 60 * 60 * 1000
  },
  hours: {
    minutes: 60,
    seconds: 60 * 60,
    milliseconds: 60 * 60 * 1000
  },
  minutes: {
    seconds: 60,
    milliseconds: 60 * 1000
  },
  seconds: {
    milliseconds: 1000
  }
};

let ordered = ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'];

function ensure(unit){
  let normalized = unit.endsWith('s') ? unit : unit + 's';
  if (ordered.indexOf(normalized) === -1){
    throw `Invalid unit ${unit}`;
  }
  return normalized;
}

function friendlyDuration(durationOrNumber, type){
  return typeof durationOrNumber === 'number' ?
    Duration.fromLength(durationOrNumber, type) :
    durationOrNumber;
}

export class Duration{

  constructor(obj){
    this.values = obj;
  }

  _clone(alts) {
    new Duration(Object.assign(this.values), alts);
  }

  static fromLength(count, unit){
    unit = ensure(unit);
    return Duration.fromObject({[unit]: count});
  }

  static fromObject(obj){
    //todo - ensure() each key
    return new Duration(obj);
  }

  static fromISO(text){}

  static fromString(text, fmt){}

  toFormatString(fmt){}

  toObject(){
    return Object.assign({}, this.values);
  }

  toISO(){}

  toJSON(){}

  plus(countOrDuration, unit='milliseconds'){
    let dur = friendlyDuration(countOrDuration, unit),
        result = {};

    for (let k of ordered){
      let val = dur.get(k) + this.get(k);
      if (val != 0){
        result[k] = val;
      }
    }

    return Duration.fromObject(result);
  }

  minus(countOrDuration, unit='milliseconds'){
    let dur = friendlyDuration(countOrDuration, unit);
    return this.plus(dur.negate());
  }

  get(unit){
    return this[unit]();
  }

  as(unit){
    return this.shiftTo(unit).get(unit);
  }

  shiftTo(...units){
    if (units.length == 0){
      return this;
    }

    let built = {},
        accumulated = {},
        vals = this.toObject(),
        lastUnit;

    for (let k of ordered) {

      if (units.indexOf(k) >= 0){
        built[k] = 0;
        lastUnit = k;

        //anything we haven't boiled down yet should get boiled to this unit
        for (let ak in accumulated){
          built[k] += matrix[ak][k] * accumulated[ak];
          delete(accumulated[ak]);
        }

        //plus anything that's already in this unit
        if (typeof(vals[k]) === 'number'){
          built[k] += vals[k];
        }

        //plus anything further down the chain that should be rolled up in to this
        for(let down in vals){
          if (ordered.indexOf(down) > ordered.indexOf(k)){
            let conv = matrix[k][down],
                added = Math.floor(vals[down] / conv);
            built[k] += added;
            vals[down] -= added * conv;
          }
        }
      }

      //otherwise, keep it in the wings to boil it later
      else if (typeof(vals[k]) === 'number'){
        accumulated[k] = vals[k];
      }
    }

    //anything leftover becomes the decimal for the last unit
    if (lastUnit){
      for (let key in accumulated){
        built[lastUnit] += accumulated[key] / matrix[lastUnit][key];
      }
    }

    return Duration.fromObject(built);
  }

  negate(){
    let negated = {};
    for(let k of Object.keys(this.values)){
      negated[k] = -this.values[k];
    }
    return Duration.fromObject(negated);
  };

  years(){
    return this.values.years || 0;
  }

  months(){
    return this.values.months || 0;
  }

  days(){
    return this.values.days || 0;
  }

  hours(){
    return this.values.hours || 0;
  }

  minutes(){
    return this.values.minutes || 0;
  }

  seconds(){
    return this.values.seconds || 0;
  }

  milliseconds(){
    return this.values.milliseconds || 0;
  }
}
<|endoftext|>


<commit_before>/**
 * accountHistory.js
 * Get the recent history from a given account.
 */

var { Apis } = require("graphenejs-ws");
var { ChainStore, FetchChain} = require("graphenejs-lib");
var host = require('../config.js').ws.host;

/**
 * @param  {JSON}   	data     	[]
 * @param  {Function} 	callback 	[]
 * @return {JSON}            		[]
 */
module.exports = function(data, callback) {
    this.accountName = data.account;
    this.limit = data.limit;
    this.host = host;
    this.apis = Apis.instance(this.host, true);
    this.chain = ChainStore;
    //
    this.apis.init_promise
        .then(() => {
            this.chain.init()
        })
        .then(() => {
        	return FetchChain("getAccount", this.accountName)
        })
        .then((account)=>{
        	var accountID = (account.toJSON()).id;
        	return  FetchChain("fetchRecentHistory", accountID)
        })
        .then((history)=>{
        	callback(null, history);
        })
        .catch((err) => {
            console.log(err);
            callback(err, null);
        })
};
<commit_msg>Update accountHistory.js<commit_after>/**
 * accountHistory.js
 * Get the recent history from a given account.
 */

var { Apis } = require("graphenejs-ws");
var { ChainStore, FetchChain} = require("graphenejs-lib");
var host = require('../config.js').ws.host;

/**
 * @param  {JSON}   	data     	[]
 * @param  {Function} 	callback 	[]
 * @return {JSON}            		[]
 */
module.exports = function(data, callback) {
    this.accountName = data.account;
    this.limit = data.limit;
    this.host = host;
    this.apis = Apis.instance(this.host, true);
    this.chain = ChainStore;
    //
    this.apis.init_promise
        .then(() => {
            this.chain.init()
        })
        .then(() => {
        	return FetchChain("getAccount", this.accountName)
        })
        .then((account)=>{
        	var accountID = (account.toJSON()).id;
        	return  FetchChain("fetchRecentHistory", accountID)
        })
        .then((history)=>{
        	callback(null, history);
        })
        .catch((err) => {
            callback(err, null);
        })
};
<|endoftext|>