<commit_before>var Utils = require('./utils')

module.exports = (function() {
  var QueryInterface = function(sequelize) {
    this.sequelize = sequelize
    this.QueryGenerator = require('./connectors/' + this.sequelize.options.connector + '/query-generator')
  }
  Utils.addEventEmitter(QueryInterface)

  QueryInterface.prototype.createTable = function(tableName, attributes, options) {
    var _query = query.call(this, this.QueryGenerator.createTableQuery(tableName, attributes, options))
      , self   = this

    _query
      .success(function(){ self.emit('createTable', null) })
      .error(function(err){ self.emit('createTable', err)})

    return _query
  }

  QueryInterface.prototype.dropTable = function(tableName) {
    var _query = query.call(this, this.QueryGenerator.dropTableQuery(tableName))
      , self   = this

    _query
      .success(function(){ self.emit('dropTable', null) })
      .error(function(err){ self.emit('dropTable', err)})

    return _query
  }

  QueryInterface.prototype.dropAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer()

      self.showAllTables().success(function(tableNames) {
        tableNames.forEach(function(tableName) {
          chainer.add(self.sequelize.getQueryInterface().dropTable(tableName))
        })
        chainer
          .run()
          .success(function() {
            self.emit('dropAllTables', null)
            emitter.emit('success', null)
          })
          .error(function(err) {
            self.emit('dropAllTables', err)
            emitter.emit('failure', err)
          })
      }).error(function(err) {
        self.emit('dropAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.renameTable = function(before, after) {
    var _query = query.call(this, this.QueryGenerator.renameTableQuery(before, after))
      , self   = this

    _query
      .success(function(){ self.emit('renameTable', null) })
      .error(function(err){ self.emit('renameTable', err)})

    return _query

  }

  QueryInterface.prototype.showAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('SHOW TABLES;', null, { raw: true }).success(function(data) {
        var tableNames = Sequelize.Utils._.map(data, function(hash) {
          return Sequelize.Utils._.values(hash)
        })

        self.emit('showAllTables', null)
        emitter.emit('success', Sequelize.Utils._.flatten(tableNames))
      }).error(function(err) {
        self.emit('showAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.addColumn = function() {

  }

  QueryInterface.prototype.removeColumn = function() {

  }

  QueryInterface.prototype.changeColumn = function() {

  }

  QueryInterface.prototype.renameColumn = function() {

  }

  QueryInterface.prototype.addIndex = function() {

  }

  QueryInterface.prototype.removeIndex = function() {

  }

  QueryInterface.prototype.insert = function(model, tableName, values) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      query
        .call(self, self.QueryGenerator.insertQuery(tableName, values), model)
        .success(function(obj) {
          self.emit('insert', null)

          obj.isNewRecord = false
          emitter.emit('success', obj)
        })
        .error(function(err) {
          self.emit('insert', err)
          emitter.emit('failure', err)
        })
    }).run()
  }

  QueryInterface.prototype.update = function(model, tableName, values, identifier) {
    var sql    = this.QueryGenerator.updateQuery(tableName, values, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('update', null) })
      .error(function(err){ self.emit('update', err)})

    return _query
  }

  QueryInterface.prototype.delete = function(model, tableName, identifier) {
    var sql    = this.QueryGenerator.deleteQuery(tableName, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('delete', null) })
      .error(function(err){ self.emit('delete', err)})

    return _query
  }

  QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
    queryOptions = queryOptions || {}

    var sql    = this.QueryGenerator.selectQuery(tableName, options)
      , _query = query.call(this, sql, factory, queryOptions)
      , self   = this

    _query
      .success(function(){ self.emit('select', null) })
      .error(function(err){ self.emit('select', err)})

    return _query
  }

  QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
    var self = this

    if(attributeSelector == undefined)
      throw new Error('Please pass an attribute selector!')

    return new Utils.CustomEventEmitter(function(emitter) {
      var sql    = self.QueryGenerator.selectQuery(tableName, options)
        , _query = query.call(self, sql, null, { plain: true, raw: true })

      _query.success(function(data) {
        self.emit('rawSelect', null)
        emitter.emit('success', data[attributeSelector])
      }).error(function(err) {
        self.emit('rawSelect', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  // private

  var query = function() {
    var args = Utils._.map(arguments, function(arg, _) { return arg })

    // add this as callee via the second argument
    if(arguments.length == 1) args.push(this)

    return this.sequelize.query.apply(this.sequelize, args)
  }

  return QueryInterface
})()
<commit_msg>describeTable + addColumn<commit_after>var Utils     = require('./utils')
  , DataTypes = require('./data-types')

module.exports = (function() {
  var QueryInterface = function(sequelize) {
    this.sequelize = sequelize
    this.QueryGenerator = require('./connectors/' + this.sequelize.options.connector + '/query-generator')
  }
  Utils.addEventEmitter(QueryInterface)

  QueryInterface.prototype.createTable = function(tableName, attributes, options) {
    var _query = query.call(this, this.QueryGenerator.createTableQuery(tableName, attributes, options))
      , self   = this

    _query
      .success(function(){ self.emit('createTable', null) })
      .error(function(err){ self.emit('createTable', err)})

    return _query
  }

  QueryInterface.prototype.dropTable = function(tableName) {
    var _query = query.call(this, this.QueryGenerator.dropTableQuery(tableName))
      , self   = this

    _query
      .success(function(){ self.emit('dropTable', null) })
      .error(function(err){ self.emit('dropTable', err)})

    return _query
  }

  QueryInterface.prototype.dropAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer()

      self.showAllTables().success(function(tableNames) {
        tableNames.forEach(function(tableName) {
          chainer.add(self.sequelize.getQueryInterface().dropTable(tableName))
        })
        chainer
          .run()
          .success(function() {
            self.emit('dropAllTables', null)
            emitter.emit('success', null)
          })
          .error(function(err) {
            self.emit('dropAllTables', err)
            emitter.emit('failure', err)
          })
      }).error(function(err) {
        self.emit('dropAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.renameTable = function(before, after) {
    var _query = query.call(this, this.QueryGenerator.renameTableQuery(before, after))
      , self   = this

    _query
      .success(function(){ self.emit('renameTable', null) })
      .error(function(err){ self.emit('renameTable', err)})

    return _query

  }

  QueryInterface.prototype.showAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('SHOW TABLES;', null, { raw: true }).success(function(data) {
        var tableNames = Sequelize.Utils._.map(data, function(hash) {
          return Sequelize.Utils._.values(hash)
        })

        self.emit('showAllTables', null)
        emitter.emit('success', Sequelize.Utils._.flatten(tableNames))
      }).error(function(err) {
        self.emit('showAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.describeTable = function(tableName) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('DESCRIBE `' + tableName + '`;', null, { raw: true }).success(function(data) {
        emitter.emit('success', data)
      }).error(function(err) {
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) {
    var attributes = {}

    if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
      attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
    else
      attributes[attributeName] = dataTypeOrOptions

    var options = Utils.simplifyAttributes(attributes)
      , query   = this.QueryGenerator.addColumnQuery(tableName, options)
      , self    = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query(query).success(function() {
        self.emit('addColumn', null)
        emitter.emit('success', null)
      }).error(function(err) {
        self.emit('addColumn', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.removeColumn = function() {

  }

  QueryInterface.prototype.changeColumn = function() {

  }

  QueryInterface.prototype.renameColumn = function() {

  }

  QueryInterface.prototype.addIndex = function() {

  }

  QueryInterface.prototype.removeIndex = function() {

  }

  QueryInterface.prototype.insert = function(model, tableName, values) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      query
        .call(self, self.QueryGenerator.insertQuery(tableName, values), model)
        .success(function(obj) {
          self.emit('insert', null)

          obj.isNewRecord = false
          emitter.emit('success', obj)
        })
        .error(function(err) {
          self.emit('insert', err)
          emitter.emit('failure', err)
        })
    }).run()
  }

  QueryInterface.prototype.update = function(model, tableName, values, identifier) {
    var sql    = this.QueryGenerator.updateQuery(tableName, values, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('update', null) })
      .error(function(err){ self.emit('update', err)})

    return _query
  }

  QueryInterface.prototype.delete = function(model, tableName, identifier) {
    var sql    = this.QueryGenerator.deleteQuery(tableName, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('delete', null) })
      .error(function(err){ self.emit('delete', err)})

    return _query
  }

  QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
    queryOptions = queryOptions || {}

    var sql    = this.QueryGenerator.selectQuery(tableName, options)
      , _query = query.call(this, sql, factory, queryOptions)
      , self   = this

    _query
      .success(function(){ self.emit('select', null) })
      .error(function(err){ self.emit('select', err)})

    return _query
  }

  QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
    var self = this

    if(attributeSelector == undefined)
      throw new Error('Please pass an attribute selector!')

    return new Utils.CustomEventEmitter(function(emitter) {
      var sql    = self.QueryGenerator.selectQuery(tableName, options)
        , _query = query.call(self, sql, null, { plain: true, raw: true })

      _query.success(function(data) {
        self.emit('rawSelect', null)
        emitter.emit('success', data[attributeSelector])
      }).error(function(err) {
        self.emit('rawSelect', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  // private

  var query = function() {
    var args = Utils._.map(arguments, function(arg, _) { return arg })

    // add this as callee via the second argument
    if(arguments.length == 1) args.push(this)

    return this.sequelize.query.apply(this.sequelize, args)
  }

  return QueryInterface
})()
<|endoftext|>


<commit_before>(function ( proto ) {

	var set, resetWrapped;

	proto.set = function ( keypath, value, complete ) {
		var map, changes, upstreamChanges, previousTransitionManager, transitionManager, i, changeHash;

		upstreamChanges = [ '' ]; // empty string will always be an upstream keypath
		changes = [];

		if ( isObject( keypath ) ) {
			map = keypath;
			complete = value;
		}

		// manage transitions
		previousTransitionManager = this._transitionManager;
		this._transitionManager = transitionManager = makeTransitionManager( this, complete );

		// setting multiple values in one go
		if ( map ) {
			for ( keypath in map ) {
				if ( hasOwn.call( map, keypath) ) {
					value = map[ keypath ];
					keypath = normaliseKeypath( keypath );

					set( this, keypath, value, changes, upstreamChanges );
				}
			}
		}

		// setting a single value
		else {
			keypath = normaliseKeypath( keypath );
			set( this, keypath, value, changes, upstreamChanges );
		}

		// if anything has changed, attempt to resolve any unresolved keypaths...
		if ( changes.length && this._pendingResolution.length ) {
			attemptKeypathResolution( this );
		}

		// ...and notify dependants
		if ( upstreamChanges.length ) {
			notifyMultipleDependants( this, upstreamChanges, true );
		}

		if ( changes.length ) {
			notifyMultipleDependants( this, changes );
		}

		// Attributes don't reflect changes automatically if there is a possibility
		// that they will need to change again before the .set() cycle is complete
		// - they defer their updates until all values have been set
		processDeferredUpdates( this );

		// transition manager has finished its work
		this._transitionManager = previousTransitionManager;
		transitionManager.ready();

		// Fire a change event
		if ( ( i = changes.length ) && !this.firingChangeEvent ) {
			this.firingChangeEvent = true; // short-circuit any potential infinite loops

			changeHash = {};

			i = changes.length;
			while ( i-- ) {
				changeHash[ changes[i] ] = this.get( changes[i] );
			}

			this.fire( 'change', changeHash );

			this.firingChangeEvent = false;
		}

		return this;
	};

	set = function ( ractive, keypath, value, changes, upstreamChanges ) {
		var cached, keys, previous, key, obj, accumulated, currentKeypath, keypathToClear, wrapped;

		if ( ( wrapped = ractive._wrapped[ keypath ] ) && wrapped.reset ) {
			if ( resetWrapped( ractive, keypath, value, wrapped, changes, upstreamChanges ) !== false ) {
				return;
			}
		}

		cached = ractive._cache[ keypath ];
		previous = ractive.get( keypath );

		keys = keypath.split( '.' );
		accumulated = [];

		// update the model, if necessary
		if ( previous !== value ) {

			// update data
			obj = ractive.data;
			while ( keys.length > 1 ) {
				key = accumulated[ accumulated.length ] = keys.shift();
				currentKeypath = accumulated.join( '.' );

				if ( wrapped = ractive._wrapped[ currentKeypath ] ) {
					if ( wrapped.set ) {
						wrapped.set( keys.join( '.' ), value );
					}

					obj = wrapped.get();
				}

				else {
					// If this branch doesn't exist yet, create a new one - if the next
					// key matches /^\s*[0-9]+\s*$/, assume we want an array branch rather
					// than an object
					if ( !obj[ key ] ) {

						// if we're creating a new branch, we may need to clear the upstream
						// keypath
						if ( !keypathToClear ) {
							keypathToClear = currentKeypath;
						}

						obj[ key ] = ( /^\s*[0-9]+\s*$/.test( keys[0] ) ? [] : {} );
					}

					obj = obj[ key ];
				}
			}

			key = keys[0];
			obj[ key ] = value;
		}

		else {
			// if the value is the same as the cached value AND the value is a primitive,
			// we don't need to do anything else
			if ( value === cached && typeof value !== 'object' ) {
				return;
			}
		}

		// Clear cache
		clearCache( ractive, keypathToClear || keypath );

		// add this keypath to the list of changes
		changes[ changes.length ] = keypath;

		// add upstream keypaths to the list of upstream changes
		keys = keypath.split( '.' );
		while ( keys.length > 1 ) {
			keys.pop();
			keypath = keys.join( '.' );

			if ( !upstreamChanges[ keypath ] ) {
				upstreamChanges[ upstreamChanges.length ] = keypath;
				upstreamChanges[ keypath ] = true;
			}
		}
	};

	resetWrapped = function ( ractive, keypath, value, wrapped, changes, upstreamChanges ) {
		var previous, cached, cacheMap, keys, i;

		previous = wrapped.get();

		if ( !isEqual( previous, value ) ) {
			if ( wrapped.reset( value ) === false ) {
				return false;
			}
		}

		value = wrapped.get();
		cached = ractive._cache[ keypath ];

		if ( !isEqual( cached, value ) ) {
			ractive._cache[ keypath ] = value;

			// Clear downstream keypaths only. Otherwise this wrapper will be torn down!
			// TODO is there a way to intelligently detect whether a wrapper should be
			// torn down?
			cacheMap = ractive._cacheMap[ keypath ];

			if ( cacheMap ) {
				i = cacheMap.length;
				while ( i-- ) {
					clearCache( ractive, cacheMap[i] );
				}
			}

			changes[ changes.length ] = keypath;

			// add upstream keypaths to the list of upstream changes
			keys = keypath.split( '.' );
			while ( keys.length > 1 ) {
				keys.pop();
				keypath = keys.join( '.' );

				if ( !upstreamChanges[ keypath ] ) {
					upstreamChanges[ upstreamChanges.length ] = keypath;
					upstreamChanges[ keypath ] = true;
				}
			}
		}
	};

}( proto ));<commit_msg>tidied up set() method, made it slightly more efficient<commit_after>(function ( proto ) {

	var set, getUpstreamChanges, resetWrapped;

	proto.set = function ( keypath, value, complete ) {
		var map, changes, upstreamChanges, previousTransitionManager, transitionManager, i, changeHash;

		changes = [];

		if ( isObject( keypath ) ) {
			map = keypath;
			complete = value;
		}

		// Set multiple keypaths in one go
		if ( map ) {
			for ( keypath in map ) {
				if ( hasOwn.call( map, keypath) ) {
					value = map[ keypath ];
					keypath = normaliseKeypath( keypath );

					set( this, keypath, value, changes );
				}
			}
		}

		// Set a single keypath
		else {
			keypath = normaliseKeypath( keypath );
			set( this, keypath, value, changes );
		}

		if ( !changes.length ) {
			return;
		}

		// Manage transitions
		previousTransitionManager = this._transitionManager;
		this._transitionManager = transitionManager = makeTransitionManager( this, complete );

		// Attempt to resolve any unresolved keypaths...
		if ( this._pendingResolution.length ) {
			attemptKeypathResolution( this );
		}

		// ...and notify dependants
		upstreamChanges = getUpstreamChanges( changes );
		if ( upstreamChanges.length ) {
			notifyMultipleDependants( this, upstreamChanges, true );
		}

		notifyMultipleDependants( this, changes );

		// Attributes don't reflect changes automatically if there is a possibility
		// that they will need to change again before the .set() cycle is complete
		// - they defer their updates until all values have been set
		processDeferredUpdates( this );

		// transition manager has finished its work
		this._transitionManager = previousTransitionManager;
		transitionManager.ready();

		// Fire a change event
		if ( !this.firingChangeEvent ) {
			this.firingChangeEvent = true; // short-circuit any potential infinite loops

			changeHash = {};

			i = changes.length;
			while ( i-- ) {
				changeHash[ changes[i] ] = this.get( changes[i] );
			}

			this.fire( 'change', changeHash );

			this.firingChangeEvent = false;
		}

		return this;
	};

	set = function ( ractive, keypath, value, changes ) {
		var cached, keys, previous, key, obj, accumulated, currentKeypath, keypathToClear, wrapped;

		if ( ( wrapped = ractive._wrapped[ keypath ] ) && wrapped.reset ) {
			if ( resetWrapped( ractive, keypath, value, wrapped, changes ) !== false ) {
				return;
			}
		}

		cached = ractive._cache[ keypath ];
		previous = ractive.get( keypath );

		keys = keypath.split( '.' );
		accumulated = [];

		// update the model, if necessary
		if ( previous !== value ) {

			// update data
			obj = ractive.data;
			while ( keys.length > 1 ) {
				key = accumulated[ accumulated.length ] = keys.shift();
				currentKeypath = accumulated.join( '.' );

				if ( wrapped = ractive._wrapped[ currentKeypath ] ) {
					if ( wrapped.set ) {
						wrapped.set( keys.join( '.' ), value );
					}

					obj = wrapped.get();
				}

				else {
					// If this branch doesn't exist yet, create a new one - if the next
					// key matches /^\s*[0-9]+\s*$/, assume we want an array branch rather
					// than an object
					if ( !obj[ key ] ) {

						// if we're creating a new branch, we may need to clear the upstream
						// keypath
						if ( !keypathToClear ) {
							keypathToClear = currentKeypath;
						}

						obj[ key ] = ( /^\s*[0-9]+\s*$/.test( keys[0] ) ? [] : {} );
					}

					obj = obj[ key ];
				}
			}

			key = keys[0];
			obj[ key ] = value;
		}

		else {
			// if the value is the same as the cached value AND the value is a primitive,
			// we don't need to do anything else
			if ( value === cached && typeof value !== 'object' ) {
				return;
			}
		}

		// Clear cache
		clearCache( ractive, keypathToClear || keypath );

		// add this keypath to the list of changes
		changes[ changes.length ] = keypath;
	};

	getUpstreamChanges = function ( changes ) {
		var upstreamChanges = [ '' ], i, keypath, keys, upstreamKeypath;

		i = changes.length;
		while ( i-- ) {
			keypath = changes[i];
			keys = keypath.split( '.' );

			while ( keys.length > 1 ) {
				keys.pop();
				upstreamKeypath = keys.join( '.' );

				if ( !upstreamChanges[ upstreamKeypath ] ) {
					upstreamChanges[ upstreamChanges.length ] = upstreamKeypath;
					upstreamChanges[ upstreamKeypath ] = true;
				}
			}
		}

		return upstreamChanges;
	};

	resetWrapped = function ( ractive, keypath, value, wrapped, changes ) {
		var previous, cached, cacheMap, i;

		previous = wrapped.get();

		if ( !isEqual( previous, value ) ) {
			if ( wrapped.reset( value ) === false ) {
				return false;
			}
		}

		value = wrapped.get();
		cached = ractive._cache[ keypath ];

		if ( !isEqual( cached, value ) ) {
			ractive._cache[ keypath ] = value;

			// Clear downstream keypaths only. Otherwise this wrapper will be torn down!
			// TODO is there a way to intelligently detect whether a wrapper should be
			// torn down?
			cacheMap = ractive._cacheMap[ keypath ];

			if ( cacheMap ) {
				i = cacheMap.length;
				while ( i-- ) {
					clearCache( ractive, cacheMap[i] );
				}
			}

			changes[ changes.length ] = keypath;
		}
	};

}( proto ));<|endoftext|>


<commit_before>/*
This file is part of the GhostDriver by Ivan De Marino <http://ivandemarino.me>.

Copyright (c) 2012, Ivan De Marino <http://ivandemarino.me>
All rights reserved.

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

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

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

/* generate node configuration for this node */
var nodeconf = function(ip, port, hub) {
    var ref$, hubHost, hubPort;

    ref$ = hub.match(/([\w\d\.]+):(\d+)/);
    hubHost = ref$[1];
    hubPort = +ref$[2]; //< ensure it's of type "number"

    return {
        capabilities: [{
            browserName: "phantomjs",
            maxInstances: 1,
            seleniumProtocol: "WebDriver"
        }],
        configuration: {
            hub: hub,
            hubHost: hubHost,
            hubPort: hubPort,
            port: port,
            proxy: "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
            // Note that multiple webdriver sessions or instances within a single
            // Ghostdriver process will interact in unexpected and undesirable ways.
            maxSession: 1,
            register: true,
            registerCycle: 5000,
            role: "wd",
            url: "http://" + ip + ":" + port,
            remoteHost: "http://" + ip + ":" + port
        }
    };
};

module.exports = {
    register: function(ip, port, hub) {
        try {
            var page = require('webpage').create();
            port = +port; //< ensure it's of type "number"
            if(!hub.match(/\/$/)) {
                hub += '/';
            }

            /* Register with selenium grid server */
            page.open(hub + 'grid/register', {
                operation: 'post',
                data: JSON.stringify(nodeconf(ip, port, hub)),
                headers: {
                    'Content-Type': 'application/json'
                }
            }, function(status) {
                if(status !== 'success') {
                    console.error("Unable to contact grid " + hub + ": " + status);
                    phantom.exit(1);
                }
                if(page.framePlainText !== "ok") {
                    console.error("Problem registering with grid " + hub + ": " + page.content);
                    phantom.exit(1);
                }
                console.log("Registered with grid hub: " + hub + " (" + page.framePlainText + ")");
            });
        } catch (e) {
            throw new Error("Could not register to Selenium Grid Hub: " + hub);
        }
    }
};
<commit_msg>Update hub_register.js<commit_after>/*
This file is part of the GhostDriver by Ivan De Marino <http://ivandemarino.me>.

Copyright (c) 2012, Ivan De Marino <http://ivandemarino.me>
All rights reserved.

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

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

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

/* generate node configuration for this node */
var nodeconf = function(ip, port, hub) {
    var ref$, hubHost, hubPort;

    ref$ = hub.match(/([\w\d\.]+):(\d+)/);
    hubHost = ref$[1];
    hubPort = +ref$[2]; //< ensure it's of type "number"

    return {
        capabilities: [{
            browserName: "phantomjs",
            maxInstances: 1,
            seleniumProtocol: "WebDriver"
        }],
        configuration: {
            hub: hub,
            hubHost: hubHost,
            hubPort: hubPort,
            port: port,
            proxy: "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
            // Note that multiple webdriver sessions or instances within a single
            // Ghostdriver process will interact in unexpected and undesirable ways.
            maxSession: 1,
            register: true,
            registerCycle: 5000,
            role: "wd",
            url: "http://" + ip + ":" + port,
            remoteHost: "http://" + ip + ":" + port
        }
    };
};

var keepAlive = function(ip, port, hub) {
   setInterval(function() {
      var page = require('webpage').create();
      page.open(hub + 'grid/api/proxy?id=' + ip + ':' + port, {
                operation: 'get',
                headers: {
                    'Content-Type': 'application/json'
                }
            }, function(status) {
               console.log(page.framePlainText);
            });
   }, 5000);
};

module.exports = {
    register: function(ip, port, hub) {
        try {
            var page = require('webpage').create();
            port = +port; //< ensure it's of type "number"
            if(!hub.match(/\/$/)) {
                hub += '/';
            }

            /* Register with selenium grid server */
            page.open(hub + 'grid/register', {
                operation: 'post',
                data: JSON.stringify(nodeconf(ip, port, hub)),
                headers: {
                    'Content-Type': 'application/json'
                }
            }, function(status) {
                if(status !== 'success') {
                    console.error("Unable to contact grid " + hub + ": " + status);
                    phantom.exit(1);
                }
                if(page.framePlainText.toLowerCase().indexOf('ok') === -1) {
                    console.error("Problem registering with grid " + hub + ": " + page.content);
                    phantom.exit(1);
                }
                console.log("Registered with grid hub: " + hub + " (" + page.framePlainText + ")");
                keepAlive(ip, port, hub);
            });
        } catch (e) {
            throw new Error("Could not register to Selenium Grid Hub: " + hub);
        }
    }
};
<|endoftext|>


<commit_before>(function(root) {

    var hyve = (typeof require == 'function' && !(typeof define == 'function' && define.amd)) ? require('../src/hyve.core.js') : root.hyve

    hyve.feeds.twitter = {
        methods : ['search', 'friends', 'popular'],
        interval : 2000,
        interval_friends : 60000,
        result_type : 'mixed', // mixed, recent, popular
        since_ids : {},
        oauth_consumer_key : '',
        oauth_nonce  : '',
        oauth_signature : '',
        oauth_signature_method : 'HMAC-SHA1',
        oauth_timestamp : '',
        oauth_token : '',
        oauth_version : '1.0',
        feed_urls : {
            search: 'http://search.twitter.com/search.json?q={{query}}&lang=en&include_entities=True{{#&result_type=#result_type}}{{since}}{{#&callback=#callback}}',
            friends: 'https://api.twitter.com/1/statuses/home_timeline.json',
            popular: 'http://search.twitter.com/search.json?q={{query}}&lang=en&rpp=25&include_entities=True{{#&result_type=#result_type}}{{since}}{{#&callback=#callback}}'
        },
        format_url : function(query){
            var since_arg
            if (this.since_ids[query]){
                since_arg = '&since_id='+this.since_ids[query]
            }
            return {
                      query: query
                    , result_type: this.result_type
                    , since: since_arg
                    , key: 'oauth_consumer_key='  + this.oauth_consumer_key
                    , nonce: '&oauth_nonce=' + this.oauth_nonce
                    , signature: '&oauth_signature=' + this.oauth_signature
                    , signature_method: '&oauth_signature_method=' + this.oauth_signature_method
                    , timestamp: '&oauth_timestamp=' + this.oauth_timestamp
                    , token: '&oauth_token=' + this.oauth_token
                    , version: '&oauth_version=' + this.oauth_version
            }
        },
        parsers : {
            search : function(data, query, callback){
                if (data.refresh_url){
                    hyve.feeds.twitter.since_ids[query] = data.refresh_url.replace(/\?since_id=([0-9]+).*/ig, "$1")
                }
                if (!this.items_seen){
                    this.items_seen = {}
                }
                if (data.results){
                    data.results.forEach(function(item){
                        if (!this.items_seen[item.id_str.toString()]){
                            this.items_seen[item.id_str.toString()] = true
                            var links = []
                            if (item.entities.urls) {
                                item.entities.urls.forEach(function(url){
                                    if(url.expanded_url){
                                        links.push(url.expanded_url)
                                    } else {
                                        links.push("http://"+url.url)
                                    }
                                })
                            }
                            var weight = 1
                            var likes = ''
                            if (item.metadata.result_type == 'popular'){
                                likes = item.metadata.recent_retweets
                                weight = likes
                            }

                            hyve.process({
                                'service' : 'twitter',
                                'type' : 'text',
                                'query' : query,
                                'user' : {
                                    'id' : item.from_user_id_str,
                                    'real_name': item.from_user_name,
                                    'name': item.from_user,
                                    'avatar' : item.profile_image_url,
                                    'profile' : "http://twitter.com/"+item.from_user
                                },
                                'likes' : likes,
                                'id' : item.id_str,
                                'date' : item.created_at,
                                'text' : item.text,
                                'links' : links,
                                'source' : 'http://twitter.com/'+
                                           item.from_user+
                                           '/status/'+item.id,
                                'weight' : weight
                            },callback)
                        }
                    },this)
                }
            },

            friends : function(data, query, callback) {
                if (data) {
                    if (!this.items_seen) this.items_seen = {}

                    data.forEach(function(item)  {

                        id = item.id_str

                        var weight = 1
                        if (item.retweet_count) {
                            weight = item.retweet_count
                        }

                        if (!this.items_seen[id]) {
                           this.items_seen[id] = true

                            console.log('TWITTA',item)

                            hyve.process({
                                'service': 'twitter',
                                'type': 'text',
                                'query': query,
                                'user' : {
                                    'id': item.user.id_str,
                                    'name': item.user.screen_name,
                                    'real_name': item.user.name,
                                    'avatar': item.user.profile_image_url,
                                    'profile':  "http://twitter.com/" + item.user.screen_name
                                },
                                'id': id,
                                'date': item.created_at,
                                'text': item.text,
                                'source': "http://twitter.com/" + item.user.screen_name + "/status/" + id,
                                'weight': weight
                            }, callback)
                        }
                    }, this);
                }
            },

            popular: function(data, query, callback) {

                var sorted_items = []

                if (data.results) {
                    data.results.forEach(function(item) {
                        var weight = 1
                        var recent_retweets = item.metadata.recent_retweets
                        if (recent_retweets > 1) {
                            weight = recent_retweets
                        }
                        item.weight = weight
                        sorted_items.push(item)

                        item.links = []
                        if (item.entities.urls) {
                            item.entities.urls.forEach(function(url){
                                if(url.expanded_url){
                                    item.links.push(url.expanded_url)
                                } else {
                                    item.links.push("http://"+url.url)
                                }
                            })
                        }

                    })

                    sorted_items.sort(function(a, b) {
                        return b.weight - a.weight
                    })

                    sorted_items.forEach(function(item) {
                        hyve.process({
                            'service' : 'twitter',
                            'type' : 'text',
                            'query' : query,
                            'user' : {
                                'id' : item.from_user_id_str,
                                'avatar' : item.profile_image_url,
                                'profile' : "http://twitter.com/"+item.from_user
                            },
                            'id' : item.id_str,
                            'date' : item.created_at,
                            'text' : item.text,
                            'links' : item.links,
                            'source' : 'http://twitter.com/'+
                                       item.from_user+
                                       '/status/'+item.id,
                            'weight' : item.weight
                        }, callback)
                    }, this)

                    //popular is called once, clear interval
                    hyve.stop(['twitter'])
                }
            }
        }
    }
})(this)
<commit_msg>remove log line<commit_after>(function(root) {

    var hyve = (typeof require == 'function' && !(typeof define == 'function' && define.amd)) ? require('../src/hyve.core.js') : root.hyve

    hyve.feeds.twitter = {
        methods : ['search', 'friends', 'popular'],
        interval : 2000,
        interval_friends : 60000,
        result_type : 'mixed', // mixed, recent, popular
        since_ids : {},
        oauth_consumer_key : '',
        oauth_nonce  : '',
        oauth_signature : '',
        oauth_signature_method : 'HMAC-SHA1',
        oauth_timestamp : '',
        oauth_token : '',
        oauth_version : '1.0',
        feed_urls : {
            search: 'http://search.twitter.com/search.json?q={{query}}&lang=en&include_entities=True{{#&result_type=#result_type}}{{since}}{{#&callback=#callback}}',
            friends: 'https://api.twitter.com/1/statuses/home_timeline.json',
            popular: 'http://search.twitter.com/search.json?q={{query}}&lang=en&rpp=25&include_entities=True{{#&result_type=#result_type}}{{since}}{{#&callback=#callback}}'
        },
        format_url : function(query){
            var since_arg
            if (this.since_ids[query]){
                since_arg = '&since_id='+this.since_ids[query]
            }
            return {
                      query: query
                    , result_type: this.result_type
                    , since: since_arg
                    , key: 'oauth_consumer_key='  + this.oauth_consumer_key
                    , nonce: '&oauth_nonce=' + this.oauth_nonce
                    , signature: '&oauth_signature=' + this.oauth_signature
                    , signature_method: '&oauth_signature_method=' + this.oauth_signature_method
                    , timestamp: '&oauth_timestamp=' + this.oauth_timestamp
                    , token: '&oauth_token=' + this.oauth_token
                    , version: '&oauth_version=' + this.oauth_version
            }
        },
        parsers : {
            search : function(data, query, callback){
                if (data.refresh_url){
                    hyve.feeds.twitter.since_ids[query] = data.refresh_url.replace(/\?since_id=([0-9]+).*/ig, "$1")
                }
                if (!this.items_seen){
                    this.items_seen = {}
                }
                if (data.results){
                    data.results.forEach(function(item){
                        if (!this.items_seen[item.id_str.toString()]){
                            this.items_seen[item.id_str.toString()] = true
                            var links = []
                            if (item.entities.urls) {
                                item.entities.urls.forEach(function(url){
                                    if(url.expanded_url){
                                        links.push(url.expanded_url)
                                    } else {
                                        links.push("http://"+url.url)
                                    }
                                })
                            }
                            var weight = 1
                            var likes = ''
                            if (item.metadata.result_type == 'popular'){
                                likes = item.metadata.recent_retweets
                                weight = likes
                            }

                            hyve.process({
                                'service' : 'twitter',
                                'type' : 'text',
                                'query' : query,
                                'user' : {
                                    'id' : item.from_user_id_str,
                                    'real_name': item.from_user_name,
                                    'name': item.from_user,
                                    'avatar' : item.profile_image_url,
                                    'profile' : "http://twitter.com/"+item.from_user
                                },
                                'likes' : likes,
                                'id' : item.id_str,
                                'date' : item.created_at,
                                'text' : item.text,
                                'links' : links,
                                'source' : 'http://twitter.com/'+
                                           item.from_user+
                                           '/status/'+item.id,
                                'weight' : weight
                            },callback)
                        }
                    },this)
                }
            },

            friends : function(data, query, callback) {
                if (data) {
                    if (!this.items_seen) this.items_seen = {}

                    data.forEach(function(item)  {

                        id = item.id_str

                        var weight = 1
                        if (item.retweet_count) {
                            weight = item.retweet_count
                        }

                        if (!this.items_seen[id]) {
                           this.items_seen[id] = true

                            hyve.process({
                                'service': 'twitter',
                                'type': 'text',
                                'query': query,
                                'user' : {
                                    'id': item.user.id_str,
                                    'name': item.user.screen_name,
                                    'real_name': item.user.name,
                                    'avatar': item.user.profile_image_url,
                                    'profile':  "http://twitter.com/" + item.user.screen_name
                                },
                                'id': id,
                                'date': item.created_at,
                                'text': item.text,
                                'source': "http://twitter.com/" + item.user.screen_name + "/status/" + id,
                                'weight': weight
                            }, callback)
                        }
                    }, this);
                }
            },

            popular: function(data, query, callback) {

                var sorted_items = []

                if (data.results) {
                    data.results.forEach(function(item) {
                        var weight = 1
                        var recent_retweets = item.metadata.recent_retweets
                        if (recent_retweets > 1) {
                            weight = recent_retweets
                        }
                        item.weight = weight
                        sorted_items.push(item)

                        item.links = []
                        if (item.entities.urls) {
                            item.entities.urls.forEach(function(url){
                                if(url.expanded_url){
                                    item.links.push(url.expanded_url)
                                } else {
                                    item.links.push("http://"+url.url)
                                }
                            })
                        }

                    })

                    sorted_items.sort(function(a, b) {
                        return b.weight - a.weight
                    })

                    sorted_items.forEach(function(item) {
                        hyve.process({
                            'service' : 'twitter',
                            'type' : 'text',
                            'query' : query,
                            'user' : {
                                'id' : item.from_user_id_str,
                                'avatar' : item.profile_image_url,
                                'profile' : "http://twitter.com/"+item.from_user
                            },
                            'id' : item.id_str,
                            'date' : item.created_at,
                            'text' : item.text,
                            'links' : item.links,
                            'source' : 'http://twitter.com/'+
                                       item.from_user+
                                       '/status/'+item.id,
                            'weight' : item.weight
                        }, callback)
                    }, this)

                    //popular is called once, clear interval
                    hyve.stop(['twitter'])
                }
            }
        }
    }
})(this)
<|endoftext|>


<commit_before>"use strict";

var Halley               = require('halley/backbone');
var log                  = require('loglevel');
var _                    = require('underscore');
var Backbone             = require('backbone');
var TemplateSubscription = require('./template-subscription');
var debug                = require('debug-proxy')('grc:client');

Halley.Promise.config({
  warnings: false,
  longStackTraces: false,
  cancellation: true
});

/* @const */
var FAYE_PREFIX = '/api';
var FAYE_PREFIX_RE = /^\/api/;
var DEFAULT_FAYE_URL = 'https://ws.gitter.im/faye';
var PING_RESPONSE_TIMEOUT = 30000;

var ErrorLogger = function() {};
ErrorLogger.prototype.incoming = function(message, callback) {
  if(message.error) {
    debug('Bayeux error: %j', message);
  }

  callback(message);
};

var ClientAuth = function(client, options) {
  this.client = client;
  if(options.authProvider) {
    this.authProvider = options.authProvider;
  } else {
    this.authProvider = function(callback) {
      return callback({ token: options.token });
    };
  }
};

ClientAuth.prototype.outgoing = function(message, callback) {
  if(message.channel !== '/meta/handshake') return callback(message);

  var uniqueClientId = this.client.uniqueClientId;

  this.client.clientId = null;
  debug("Rehandshaking realtime connection");

  this.authProvider(function(authInfo) {
      if(!message.ext) message.ext = {};
      _.extend(message.ext, authInfo);
      message.ext.uniqueClientId = uniqueClientId;
      message.ext.realtimeLibrary = 'halley';
      callback(message);
   });
};

ClientAuth.prototype.incoming = function(message, callback) {
  if(message.channel !== '/meta/handshake') return callback(message);

  if(message.successful) {
    // New clientId?
    if(this.client.clientId !== message.clientId) {
      this.client.clientId = message.clientId;
      debug("Realtime reestablished. New id is %s", this.client.clientId);
      this.client.trigger('newConnectionEstablished');
    }

    if (message.ext && message.ext.context) {
      if (message.ext.context.user) {
        this.client.user.set(message.ext.context.user);
      } else if(message.ext.context.userId) {
        this.client.user.set({ id: message.ext.context.userId });
      }
    }

    // Clear any transport problem indicators
    this.client._transportUp();
  }

  callback(message);
};

var SequenceGapDetectorExtension = function(client) {
  var self = this;
  this.client = client;
  this._seq = 0;

  client.on('newConnectionEstablished', function() {
    self._seq = 0;
  });
};

/**
 * Only perform a sequence reset at most once every 5 minutes
 */
var MIN_PERIOD_BETWEEN_RESETS = 300 * 1000;

SequenceGapDetectorExtension.prototype = {
  incoming: function(message, callback) {
    var c = message.ext && message.ext.c;
    var channel = message.channel;

    if (this.lastResetTime) {
      var timeSinceLastReset = Date.now() - this.lastResetTime;
      if (timeSinceLastReset < MIN_PERIOD_BETWEEN_RESETS) {
        return callback(message);
      }
    }

    if(c && channel && channel.indexOf('/meta') !== 0) {
      if (c === 1) {
        this._seq = 1;
        this._seqStarted = true;
        return callback(message);
      }

      if (!this._seqStarted) return callback(message);

      var current = this._seq;
      this._seq = c;

      if (c !== current + 1) {
        // Stop listening to sequence messages until we get a `1` again...
        delete this._seqStarted;
        delete this._seq;
        this.lastResetTime = Date.now();

        // Reset the connection
        log.warn('rtc: Message on channel ' + channel + ' out of sequence. Expected ' + (current + 1) + ' got ' + c + '. Resetting ' + this.client.clientId);
        this.client.trigger('sequence.error');
        this.client.reset(this.client.clientId);
      }

    }
    callback(message);
  }
};

var SnapshotExtension = function(client) {
  this.client = client;
  this._listeners = {};
  this._stateProvider = {};
  this._subscribeTimers = {};
};

SnapshotExtension.prototype = {
  outgoing: function(message, callback) {
    if (message.channel !== '/meta/subscribe') return callback(message);
    var subscribeChannel = message.subscription.replace(FAYE_PREFIX_RE, '');
    this._subscribeTimers[subscribeChannel] = Date.now(); // Record start time

    function first(array, iterator) {
      if (!array || !array.length) return;
      for (var i = 0; i < array.length; i++) {
        var value = iterator(array[i], i);
        if (value !== undefined) return value;
      }
    }

    // Generic listeners register with channel `null` and receive all snapshot requests
    var genericListeners = this._listeners[null];

    /* NB: snapshot state can be 'false' so don't compare with falsy values */
    var snapshotState = first(genericListeners, function(listener) {
      if(!listener.getSnapshotStateForChannel) return;

      return listener.getSnapshotStateForChannel(subscribeChannel);
    });

    // Only try the non-generic listeners if the generic ones did not return results
    if (snapshotState === undefined) {
      var listeners = this._listeners[subscribeChannel];
      snapshotState = first(listeners, function(listener) {
        if(!listener.getSnapshotState) return;
        return listener.getSnapshotState();
      });
    }

    if (snapshotState !== undefined) {
      if (!message.ext) message.ext = {};
      message.ext.snapshot = snapshotState;
    }

    // Add generic subscribe options
    var subscribeOptions = first(genericListeners, function(listener) {
      if(!listener.getSubscribeOptions) return;

      return listener.getSubscribeOptions(subscribeChannel);
    });

    // Subscribe options must be a hash. Graft the values
    // onto the ext object
    if (subscribeOptions) {
      if (!message.ext) message.ext = {};
      _.extend(message.ext, subscribeOptions);
    }

    callback(message);
  },

  incoming: function(message, callback) {
    if (message.channel !== '/meta/subscribe' || !message.ext || !message.ext.snapshot) return callback(message);
    // Add some statistics into the mix
    var startTime = this._subscribeTimers[message.subscription];
    if (startTime) {
      delete this._subscribeTimers[message.subscription];
      var totalTime = Date.now() - startTime;

      if (totalTime > 400) {
        var lastPart = message.subscription.split(/\//).pop();
        this.client.trigger('stats', 'time', 'faye.subscribe.time.' + lastPart, totalTime);

        debug('Subscription to %s took %sms', message.subscription, totalTime);
      }
    }

    var channelListeners = this._listeners;
    var subscriptionChannel = message.subscription.replace(FAYE_PREFIX_RE, '');

    function invokeHandleSnapshot(channel) {
      var listeners = channelListeners[channel];
      var snapshot = message.ext.snapshot;

      if (!listeners) return;

      listeners.forEach(function(listener) {
        if(listener.handleSnapshot) {
          listener.handleSnapshot(snapshot, subscriptionChannel);
        }
      });
    }

    invokeHandleSnapshot(null);
    invokeHandleSnapshot(subscriptionChannel);

    callback(message);
  },

  registerSnapshotHandler: function(channel, snapshotHandler) {
    var list = this._listeners[channel];
    if (list) {
      list.push(snapshotHandler);
    } else {
      list = [snapshotHandler];
      this._listeners[channel] = list;
    }
  },

  deregisterSnapshotHandler: function(channel, snapshotHandler) {
    var list = this._listeners[channel];
    if (!list) return;

    // Remove the handler
    list = list.filter(function(handler) { return handler !== snapshotHandler; });

    if (!list.length) {
      delete this._listeners[channel];
    } else {
      this._listeners[channel] = list;
    }
  }
};

function RealtimeClient(options) {
  var self = this;

  this.uniqueClientId = Math.floor(Math.random() * 100000);

  this.user = new Backbone.Model();
  var halleyOptions;
  if (options.fayeOptions) {
    halleyOptions = options.fayeOptions;

    /* Backwards compatibility for Faye */
    if (halleyOptions.timeout) {
      halleyOptions.timeout = halleyOptions.timeout * 1000;
    }

    if (halleyOptions.interval) {
      halleyOptions.interval = halleyOptions.interval * 1000;
    }

    if (halleyOptions.retry) {
      halleyOptions.retry = halleyOptions.retry * 1000;
    }
  }

  if (options.halleyOptions) {
    halleyOptions = options.halleyOptions;
  }

  if (options.websocketsDisabled) {
    halleyOptions = halleyOptions || {};
    if (!halleyOptions.disabled) halleyOptions.disabled = [];

    client.push('websocket');
  }

  var client = new Halley.Client(options.fayeUrl || DEFAULT_FAYE_URL, halleyOptions);

  client.addExtension(new ClientAuth(this, options));
  client.addExtension(new SequenceGapDetectorExtension(this));
  client.addExtension(new ErrorLogger(this));

  this.snapshots = new SnapshotExtension(this);
  client.addExtension(this.snapshots);

  if(options.extensions) {
    options.extensions.forEach(function(extension) {
      client.addExtension(extension);
    });
  }

  // Connect early in order to obtain the userId
  client.connect();

  this.listenTo(this.user, 'change:id', function() {
    this.trigger('change:userId', this.user.id);
  });

  // Initially, the transport is down
  this._transportDown(10 /* seconds */);

  // Deprecated in favour of connection:down
  this.listenTo(client, 'transport:down', function() {
    debug('Transport down');
    this._transportDown();
  });

  // Deprecated in favour of connection:up
  this.listenTo(client, 'transport:up', function() {
    debug('Transport up');
    this._transportUp();
  });

  this.listenTo(client, 'connection:down', function() {
    this.trigger('connection:down');
  });

  this.listenTo(client, 'connection:up', function() {
    this.trigger('connection:up');
  });

  this.client = client;
}

_.extend(RealtimeClient.prototype, Backbone.Events, {

  reset: function(clientIdOnPing) {
    if(clientIdOnPing !== this.clientId) {
      debug("Ignoring reset request as clientId has changed.");
      return;
    }

    debug("Client reset requested");

    this.trigger('stats', 'event', 'faye.ping.reset');
    this.clientId = null;
    this.client.reset();
  },

  subscribe: function(channel, callback, context) {
    var fayeChannel = FAYE_PREFIX + channel;
    debug('Subscribing to %s', channel);

    return this.client.subscribe(fayeChannel, callback, context);
  },

  subscribeTemplate: function(options) {
    return new TemplateSubscription(this, options);
  },

  publish: function(channel, message) {
    return this.client.publish(FAYE_PREFIX + channel, message);
  },

  disconnect: function () {
    this.client.disconnect();
  },

  registerSnapshotHandler: function(channel, snapshotHandler) {
    return this.snapshots.registerSnapshotHandler(channel, snapshotHandler);
  },

  deregisterSnapshotHandler: function(channel, snapshotHandler) {
    return this.snapshots.deregisterSnapshotHandler(channel, snapshotHandler);
  },

  testConnection: function(reason, callback) {
    /* Wait until the connection is established before attempting the test */
    var originalClientId = this.clientId;

    if (!originalClientId || this._pingOutstanding) {
      debug('Ignoring test connection request');
      return callback && callback();
    }

    debug('Testing connection: reason=%s, clientId=%s', reason, originalClientId);

    if (reason !== 'ping') {
      this.trigger('testConnection', reason);
      debug('Testing connection due to %s', reason);
    }

    this._pingOutstanding = true;

    return this.client.publish(FAYE_PREFIX + '/v1/ping2', { reason: reason }, { deadline: PING_RESPONSE_TIMEOUT })
      .bind(this)
      .timeout(PING_RESPONSE_TIMEOUT + 1000, 'Ping timeout')
      .then(function() {
        debug('Server ping succeeded');
        return true;
      })
      .catch(function(error) {
        debug('Server ping error %j', error);
        this.reset(originalClientId);
        return false;
      })
      .finally(function() {
        this._pingOutstanding = false;
      })
      .asCallback(callback);
  },

  getClientId: function() {
    return this.clientId;
  },

  getUserId: function() {
    return this.user.id;
  },

  _transportDown: function(persistentOutageTimeout) {
    var self = this;
    var timeout = persistentOutageTimeout || 60;

    if(!this._connectionFailureTimeout) {
      this._connectionFailureTimeout = setTimeout(function() {
        if(!self._persistentOutage) {
          self._persistentOutageStartTime = Date.now();
          self._persistentOutage = true;
          debug('Persistent outage');
          self.trigger('connectionFailure');
        }
      }, timeout * 1000);
    }
  },

  _transportUp: function () {
    if(this._connectionFailureTimeout) {
      clearTimeout(this._connectionFailureTimeout);
      this._connectionFailureTimeout = null;
    }

    if(this._persistentOutage) {
      this.trigger('stats', 'event', 'faye.outage.restored');
      this.trigger('stats', 'time', 'faye.outage.restored.time', Date.now() - this._persistentOutageStartTime);
      delete this._persistentOutage;
      delete this._persistentOutageStartTime;

      debug('Persistent outage restored');
      this.trigger('connectionRestored');
    }
  }
});

module.exports = RealtimeClient;
<commit_msg>Fixed bug in web sockets disable<commit_after>"use strict";

var Halley               = require('halley/backbone');
var log                  = require('loglevel');
var _                    = require('underscore');
var Backbone             = require('backbone');
var TemplateSubscription = require('./template-subscription');
var debug                = require('debug-proxy')('grc:client');

Halley.Promise.config({
  warnings: false,
  longStackTraces: false,
  cancellation: true
});

/* @const */
var FAYE_PREFIX = '/api';
var FAYE_PREFIX_RE = /^\/api/;
var DEFAULT_FAYE_URL = 'https://ws.gitter.im/faye';
var PING_RESPONSE_TIMEOUT = 30000;

var ErrorLogger = function() {};
ErrorLogger.prototype.incoming = function(message, callback) {
  if(message.error) {
    debug('Bayeux error: %j', message);
  }

  callback(message);
};

var ClientAuth = function(client, options) {
  this.client = client;
  if(options.authProvider) {
    this.authProvider = options.authProvider;
  } else {
    this.authProvider = function(callback) {
      return callback({ token: options.token });
    };
  }
};

ClientAuth.prototype.outgoing = function(message, callback) {
  if(message.channel !== '/meta/handshake') return callback(message);

  var uniqueClientId = this.client.uniqueClientId;

  this.client.clientId = null;
  debug("Rehandshaking realtime connection");

  this.authProvider(function(authInfo) {
      if(!message.ext) message.ext = {};
      _.extend(message.ext, authInfo);
      message.ext.uniqueClientId = uniqueClientId;
      message.ext.realtimeLibrary = 'halley';
      callback(message);
   });
};

ClientAuth.prototype.incoming = function(message, callback) {
  if(message.channel !== '/meta/handshake') return callback(message);

  if(message.successful) {
    // New clientId?
    if(this.client.clientId !== message.clientId) {
      this.client.clientId = message.clientId;
      debug("Realtime reestablished. New id is %s", this.client.clientId);
      this.client.trigger('newConnectionEstablished');
    }

    if (message.ext && message.ext.context) {
      if (message.ext.context.user) {
        this.client.user.set(message.ext.context.user);
      } else if(message.ext.context.userId) {
        this.client.user.set({ id: message.ext.context.userId });
      }
    }

    // Clear any transport problem indicators
    this.client._transportUp();
  }

  callback(message);
};

var SequenceGapDetectorExtension = function(client) {
  var self = this;
  this.client = client;
  this._seq = 0;

  client.on('newConnectionEstablished', function() {
    self._seq = 0;
  });
};

/**
 * Only perform a sequence reset at most once every 5 minutes
 */
var MIN_PERIOD_BETWEEN_RESETS = 300 * 1000;

SequenceGapDetectorExtension.prototype = {
  incoming: function(message, callback) {
    var c = message.ext && message.ext.c;
    var channel = message.channel;

    if (this.lastResetTime) {
      var timeSinceLastReset = Date.now() - this.lastResetTime;
      if (timeSinceLastReset < MIN_PERIOD_BETWEEN_RESETS) {
        return callback(message);
      }
    }

    if(c && channel && channel.indexOf('/meta') !== 0) {
      if (c === 1) {
        this._seq = 1;
        this._seqStarted = true;
        return callback(message);
      }

      if (!this._seqStarted) return callback(message);

      var current = this._seq;
      this._seq = c;

      if (c !== current + 1) {
        // Stop listening to sequence messages until we get a `1` again...
        delete this._seqStarted;
        delete this._seq;
        this.lastResetTime = Date.now();

        // Reset the connection
        log.warn('rtc: Message on channel ' + channel + ' out of sequence. Expected ' + (current + 1) + ' got ' + c + '. Resetting ' + this.client.clientId);
        this.client.trigger('sequence.error');
        this.client.reset(this.client.clientId);
      }

    }
    callback(message);
  }
};

var SnapshotExtension = function(client) {
  this.client = client;
  this._listeners = {};
  this._stateProvider = {};
  this._subscribeTimers = {};
};

SnapshotExtension.prototype = {
  outgoing: function(message, callback) {
    if (message.channel !== '/meta/subscribe') return callback(message);
    var subscribeChannel = message.subscription.replace(FAYE_PREFIX_RE, '');
    this._subscribeTimers[subscribeChannel] = Date.now(); // Record start time

    function first(array, iterator) {
      if (!array || !array.length) return;
      for (var i = 0; i < array.length; i++) {
        var value = iterator(array[i], i);
        if (value !== undefined) return value;
      }
    }

    // Generic listeners register with channel `null` and receive all snapshot requests
    var genericListeners = this._listeners[null];

    /* NB: snapshot state can be 'false' so don't compare with falsy values */
    var snapshotState = first(genericListeners, function(listener) {
      if(!listener.getSnapshotStateForChannel) return;

      return listener.getSnapshotStateForChannel(subscribeChannel);
    });

    // Only try the non-generic listeners if the generic ones did not return results
    if (snapshotState === undefined) {
      var listeners = this._listeners[subscribeChannel];
      snapshotState = first(listeners, function(listener) {
        if(!listener.getSnapshotState) return;
        return listener.getSnapshotState();
      });
    }

    if (snapshotState !== undefined) {
      if (!message.ext) message.ext = {};
      message.ext.snapshot = snapshotState;
    }

    // Add generic subscribe options
    var subscribeOptions = first(genericListeners, function(listener) {
      if(!listener.getSubscribeOptions) return;

      return listener.getSubscribeOptions(subscribeChannel);
    });

    // Subscribe options must be a hash. Graft the values
    // onto the ext object
    if (subscribeOptions) {
      if (!message.ext) message.ext = {};
      _.extend(message.ext, subscribeOptions);
    }

    callback(message);
  },

  incoming: function(message, callback) {
    if (message.channel !== '/meta/subscribe' || !message.ext || !message.ext.snapshot) return callback(message);
    // Add some statistics into the mix
    var startTime = this._subscribeTimers[message.subscription];
    if (startTime) {
      delete this._subscribeTimers[message.subscription];
      var totalTime = Date.now() - startTime;

      if (totalTime > 400) {
        var lastPart = message.subscription.split(/\//).pop();
        this.client.trigger('stats', 'time', 'faye.subscribe.time.' + lastPart, totalTime);

        debug('Subscription to %s took %sms', message.subscription, totalTime);
      }
    }

    var channelListeners = this._listeners;
    var subscriptionChannel = message.subscription.replace(FAYE_PREFIX_RE, '');

    function invokeHandleSnapshot(channel) {
      var listeners = channelListeners[channel];
      var snapshot = message.ext.snapshot;

      if (!listeners) return;

      listeners.forEach(function(listener) {
        if(listener.handleSnapshot) {
          listener.handleSnapshot(snapshot, subscriptionChannel);
        }
      });
    }

    invokeHandleSnapshot(null);
    invokeHandleSnapshot(subscriptionChannel);

    callback(message);
  },

  registerSnapshotHandler: function(channel, snapshotHandler) {
    var list = this._listeners[channel];
    if (list) {
      list.push(snapshotHandler);
    } else {
      list = [snapshotHandler];
      this._listeners[channel] = list;
    }
  },

  deregisterSnapshotHandler: function(channel, snapshotHandler) {
    var list = this._listeners[channel];
    if (!list) return;

    // Remove the handler
    list = list.filter(function(handler) { return handler !== snapshotHandler; });

    if (!list.length) {
      delete this._listeners[channel];
    } else {
      this._listeners[channel] = list;
    }
  }
};

function RealtimeClient(options) {
  var self = this;

  this.uniqueClientId = Math.floor(Math.random() * 100000);

  this.user = new Backbone.Model();
  var halleyOptions;
  if (options.fayeOptions) {
    halleyOptions = options.fayeOptions;

    /* Backwards compatibility for Faye */
    if (halleyOptions.timeout) {
      halleyOptions.timeout = halleyOptions.timeout * 1000;
    }

    if (halleyOptions.interval) {
      halleyOptions.interval = halleyOptions.interval * 1000;
    }

    if (halleyOptions.retry) {
      halleyOptions.retry = halleyOptions.retry * 1000;
    }
  }

  if (options.halleyOptions) {
    halleyOptions = options.halleyOptions;
  }

  if (options.websocketsDisabled) {
    halleyOptions = halleyOptions || {};
    if (!halleyOptions.disabled) halleyOptions.disabled = [];

    halleyOptions.disabled.push('websocket');
  }

  var client = new Halley.Client(options.fayeUrl || DEFAULT_FAYE_URL, halleyOptions);

  client.addExtension(new ClientAuth(this, options));
  client.addExtension(new SequenceGapDetectorExtension(this));
  client.addExtension(new ErrorLogger(this));

  this.snapshots = new SnapshotExtension(this);
  client.addExtension(this.snapshots);

  if(options.extensions) {
    options.extensions.forEach(function(extension) {
      client.addExtension(extension);
    });
  }

  // Connect early in order to obtain the userId
  client.connect();

  this.listenTo(this.user, 'change:id', function() {
    this.trigger('change:userId', this.user.id);
  });

  // Initially, the transport is down
  this._transportDown(10 /* seconds */);

  // Deprecated in favour of connection:down
  this.listenTo(client, 'transport:down', function() {
    debug('Transport down');
    this._transportDown();
  });

  // Deprecated in favour of connection:up
  this.listenTo(client, 'transport:up', function() {
    debug('Transport up');
    this._transportUp();
  });

  this.listenTo(client, 'connection:down', function() {
    this.trigger('connection:down');
  });

  this.listenTo(client, 'connection:up', function() {
    this.trigger('connection:up');
  });

  this.client = client;
}

_.extend(RealtimeClient.prototype, Backbone.Events, {

  reset: function(clientIdOnPing) {
    if(clientIdOnPing !== this.clientId) {
      debug("Ignoring reset request as clientId has changed.");
      return;
    }

    debug("Client reset requested");

    this.trigger('stats', 'event', 'faye.ping.reset');
    this.clientId = null;
    this.client.reset();
  },

  subscribe: function(channel, callback, context) {
    var fayeChannel = FAYE_PREFIX + channel;
    debug('Subscribing to %s', channel);

    return this.client.subscribe(fayeChannel, callback, context);
  },

  subscribeTemplate: function(options) {
    return new TemplateSubscription(this, options);
  },

  publish: function(channel, message) {
    return this.client.publish(FAYE_PREFIX + channel, message);
  },

  disconnect: function () {
    this.client.disconnect();
  },

  registerSnapshotHandler: function(channel, snapshotHandler) {
    return this.snapshots.registerSnapshotHandler(channel, snapshotHandler);
  },

  deregisterSnapshotHandler: function(channel, snapshotHandler) {
    return this.snapshots.deregisterSnapshotHandler(channel, snapshotHandler);
  },

  testConnection: function(reason, callback) {
    /* Wait until the connection is established before attempting the test */
    var originalClientId = this.clientId;

    if (!originalClientId || this._pingOutstanding) {
      debug('Ignoring test connection request');
      return callback && callback();
    }

    debug('Testing connection: reason=%s, clientId=%s', reason, originalClientId);

    if (reason !== 'ping') {
      this.trigger('testConnection', reason);
      debug('Testing connection due to %s', reason);
    }

    this._pingOutstanding = true;

    return this.client.publish(FAYE_PREFIX + '/v1/ping2', { reason: reason }, { deadline: PING_RESPONSE_TIMEOUT })
      .bind(this)
      .timeout(PING_RESPONSE_TIMEOUT + 1000, 'Ping timeout')
      .then(function() {
        debug('Server ping succeeded');
        return true;
      })
      .catch(function(error) {
        debug('Server ping error %j', error);
        this.reset(originalClientId);
        return false;
      })
      .finally(function() {
        this._pingOutstanding = false;
      })
      .asCallback(callback);
  },

  getClientId: function() {
    return this.clientId;
  },

  getUserId: function() {
    return this.user.id;
  },

  _transportDown: function(persistentOutageTimeout) {
    var self = this;
    var timeout = persistentOutageTimeout || 60;

    if(!this._connectionFailureTimeout) {
      this._connectionFailureTimeout = setTimeout(function() {
        if(!self._persistentOutage) {
          self._persistentOutageStartTime = Date.now();
          self._persistentOutage = true;
          debug('Persistent outage');
          self.trigger('connectionFailure');
        }
      }, timeout * 1000);
    }
  },

  _transportUp: function () {
    if(this._connectionFailureTimeout) {
      clearTimeout(this._connectionFailureTimeout);
      this._connectionFailureTimeout = null;
    }

    if(this._persistentOutage) {
      this.trigger('stats', 'event', 'faye.outage.restored');
      this.trigger('stats', 'time', 'faye.outage.restored.time', Date.now() - this._persistentOutageStartTime);
      delete this._persistentOutage;
      delete this._persistentOutageStartTime;

      debug('Persistent outage restored');
      this.trigger('connectionRestored');
    }
  }
});

module.exports = RealtimeClient;
<|endoftext|>


<commit_before>define(function (require) {
  var backgroundCanvas,
      foregroundCanvas,
      hiddenCanvas,

      backgroundContext,
      foregroundContext,
      hiddenContext,

      widthRatio = 0,
      heightRatio = 0,

      renderers = {};

  function draw(engine) {
    //TODO: game draw logic goes here

    foregroundContext.fillStyle = '#f00';
    foregroundContext.fillRect(300, 300, 200, 200);
  }

  function render(scene) {
    hiddenContext.clearRect(0, 0, hiddenCanvas.width, hiddenCanvas.height);

    hiddenContext.save();

    heightRatio = hiddenCanvas.height / scene.renderHeight;
    widthRatio = hiddenCanvas.width / scene.renderWidth;

    hiddenContext.scale(widthRatio, heightRatio);

    foregroundContext.clearRect(0, 0, foregroundCanvas.width, foregroundCanvas.height);
    foregroundContext.drawImage(hiddenCanvas, 0, 0);

    hiddenContext.restore();
  }

  function createCanvas(id) {
    return $('<canvas>', { 'id': id })[0];
  }

  function contextOf(canvas) {
    var context = canvas.getContext('2d');
    $(canvas).attr({ 'width': window.innerWidth, 'height': window.innerHeight });
    return context;
  }

  function resize() { reset(); }

  function reset() {
    backgroundContext = contextOf(backgroundCanvas);
    foregroundContext = contextOf(foregroundCanvas);
    hiddenContext = contextOf(hiddenCanvas);

    backgroundContext.fillStyle = '#000';
    backgroundContext.fillRect(0, 0,  backgroundCanvas.width, backgroundCanvas.height);

    hiddenContext.lineWidth = 3;
    hiddenContext.capStyle = 'round';
    hiddenContext.joinStyle = 'round';
  }

  function init() {
    backgroundCanvas = createCanvas('background');
    foregroundCanvas = createCanvas('foreground');
    hiddenCanvas = createCanvas('hidden');

    $('body').append(backgroundCanvas).append(foregroundCanvas);

    resize();

    $(window).on('resize', resize);
  }

  return { init: init, callbacks: { draw: draw } };
});
<commit_msg>random squares<commit_after>define(function (require) {
  var backgroundCanvas,
      foregroundCanvas,
      hiddenCanvas,

      backgroundContext,
      foregroundContext,
      hiddenContext,

      widthRatio = 0,
      heightRatio = 0,

      renderers = {};

  function draw(engine) {
    //TODO: game draw logic goes here

    function randomColour() {
      return [
        'rgba(',
        Math.floor(255 * Math.random()), ', ',
        Math.floor(255 * Math.random()), ', ',
        Math.floor(255 * Math.random()), ', ',
        0.5,
        ')'
      ].join('');
    }

    if (Math.random() > 0.95) {
      foregroundContext.clearRect(0, 0, foregroundCanvas.width, foregroundCanvas.height);
    }
    foregroundContext.fillStyle = randomColour();
    foregroundContext.strokeStyle = randomColour();
    foregroundContext.globalCompositeOperation = Math.random() > 0.5 ? 'lighter' : 'darker';
    foregroundContext.lineWidth = 20;
    foregroundContext.save();
    foregroundContext.translate(Math.random() * window.innerWidth, Math.random() * window.innerHeight);
    foregroundContext.rotate(Math.random() * Math.PI * 2);
    foregroundContext.fillRect(0, 0, 200, 200);
    foregroundContext.restore();
    foregroundContext.save();
    foregroundContext.translate(Math.random() * window.innerWidth, Math.random() * window.innerHeight);
    foregroundContext.rotate(Math.random() * Math.PI * 2);
    foregroundContext.strokeRect(0, 0, 200, 200);
    foregroundContext.restore();
  }

  function render(scene) {
    hiddenContext.clearRect(0, 0, hiddenCanvas.width, hiddenCanvas.height);

    hiddenContext.save();

    heightRatio = hiddenCanvas.height / scene.renderHeight;
    widthRatio = hiddenCanvas.width / scene.renderWidth;

    hiddenContext.scale(widthRatio, heightRatio);

    foregroundContext.clearRect(0, 0, foregroundCanvas.width, foregroundCanvas.height);
    foregroundContext.drawImage(hiddenCanvas, 0, 0);

    hiddenContext.restore();
  }

  function createCanvas(id) {
    return $('<canvas>', { 'id': id })[0];
  }

  function contextOf(canvas) {
    var context = canvas.getContext('2d');
    $(canvas).attr({ 'width': window.innerWidth, 'height': window.innerHeight });
    return context;
  }

  function resize() { reset(); }

  function reset() {
    backgroundContext = contextOf(backgroundCanvas);
    foregroundContext = contextOf(foregroundCanvas);
    hiddenContext = contextOf(hiddenCanvas);

    backgroundContext.fillStyle = '#000';
    backgroundContext.fillRect(0, 0,  backgroundCanvas.width, backgroundCanvas.height);

    hiddenContext.lineWidth = 3;
    hiddenContext.capStyle = 'round';
    hiddenContext.joinStyle = 'round';
  }

  function init() {
    backgroundCanvas = createCanvas('background');
    foregroundCanvas = createCanvas('foreground');
    hiddenCanvas = createCanvas('hidden');

    $('body').append(backgroundCanvas).append(foregroundCanvas);

    resize();

    $(window).on('resize', resize);
  }

  return { init: init, callbacks: { draw: draw } };
});
<|endoftext|>


<commit_before>var http = require('http'),
    https = require('https'),
    crypto = require('crypto'),
    querystring = require('querystring'),
    url = require('url'),
    requestDefaults = {
      request: {

      },
      caching: {
        ttl: 60 * 60,
        prefix: 'requestCaching'
      }
    };

function mergeOptions(obj1, obj2) {
  for (var prop in obj2) {
    try {
      if (typeof obj2[prop] === 'object') {
        obj1[prop] = mergeOptions(obj1[prop], obj2[prop]);
      } else if (typeof obj1[prop] === 'undefined') {
        obj1[prop] = obj2[prop];
      }
    } catch (err) {
      obj1[prop] = obj2[prop];
    }
  }

  return obj1;
}

function initRequestOptions(options) {
  if (!options)
    throw new Error('Missing [options] argument');
  if (typeof options !== 'object')
    throw new Error('[options] must be an object');

  mergeOptions(options, requestDefaults);

  if (!(typeof options.caching.ttl === 'number' && options.caching.ttl >= 0))
    throw new Error('[options.caching.ttl] must be a number >= 0');

  if (typeof options.caching.prefix !== 'string')
    throw new Error('[options.caching.prefix] must be a string');

  if (options.caching.key && typeof options.caching.key !== 'string')
    throw new Error('[options.caching.key] must be a string');

  if (typeof options.uri === 'string') {
    var parts = url.parse(options.uri);
    options.request.host = parts.hostname ||  '';
    options.request.port = parts.port;
    options.request.protocol = parts.protocol ||  'http:';
    options.request.path = parts.path ||  '';
  }

  if (options.params && typeof options.params === 'object') {
    if (options.request.method === 'GET' && typeof options.request.path === 'string') {
      var parts = url.parse(options.request.path, true);
      mergeOptions(options.params, parts.query);
      options.paramsStringified = querystring.stringify(options.params);
      options.request.path = parts.pathname + '?' + options.paramsStringified;
    }
    else if (options.request.method === 'POST') {
      options.paramsStringified = querystring.stringify(options.params);
      if (options.paramsStringified.length > 0) {
        if (!options.request.header)
          options.request.header = {};

        mergeOptions(options.request.header, {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': options.paramsStringified.length
        });
      }
    }
  }

  if (!options.caching.key) {
    options.caching.key = crypto.createHash('md5').update(JSON.stringify(options.request)).digest('hex');
  }

  return options;
}

var RequestCaching = function(options) {
  if (!options)
    options = {};
  if (typeof options !== 'object')
    throw new Error('[options] must be an object');
  if (!options.store)
    options.store = 'memory';
  if (typeof options.store !== 'string')
    throw new Error('[options.store] must be a string');

  try {
    var Store = require('./store/' + options.store);
    this._store = new Store();
  } catch (err) {
    throw new Error('Cannot find an implementation for [options.store] "' + options.store + '"');
  }
};

RequestCaching.prototype.request = function(options, callback) {
  if (callback && typeof callback !== 'function')
    throw new Error('[callback] must be a function');

  var options = initRequestOptions(options);

  (function(self, options, callback) {
    var key = (options.caching.prefix ? options.caching.prefix + ':' : '') + options.caching.key;
    self._store.get(key, function(err, data) {
      if(!err && data) {
        callback.apply(self, [null, data.res, data.body, {key: key, hit: true}]);
      }
      else {
        var client = options.request.protocol === 'https:' ? https : http;
        var req = client.request(options.request, function(res) {
          if (!callback) return;

          var body = '';

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

          res.on('end', function() {
            self._store.set(key, {res: res, body: body}, options.caching.ttl, function(err, data) {
              if(err) {
                callback.apply(self, [err, data.res, data.body, {key: key, hit: false}]);
              }
              else {
                callback.apply(self, [null, data.res, data.body, {key: key, hit: false}]);
              }
            });
          });
        });

        req.on('error', function(err) {
          if (!callback) return;
          callback.apply(this, [err, null, null]);
        });

        if (typeof options.paramsStringified === 'string' &&
            options.paramsStringified.length > 0 &&
            options.request.method === 'POST') {
          req.write(options.paramsStringified);
        }

        req.end();
      }
    });
  })(this, options, callback);
};

RequestCaching.prototype.get = function(uri, queryString, ttl, callback) {
  return this.request({
    uri: uri,
    params: queryString,
    request: {
      method: 'GET'
    },
    caching: {
      ttl: ttl
    }
  }, callback);
};

RequestCaching.prototype.post = function(uri, postData, ttl, callback) {
  return this.request({
    uri: uri,
    params: postData,
    request: {
      method: 'POST'
    },
    caching: {
      ttl: ttl
    }
  }, callback);
};

module.exports = RequestCaching;
<commit_msg>Removing closure as not needed<commit_after>var http = require('http'),
    https = require('https'),
    crypto = require('crypto'),
    querystring = require('querystring'),
    url = require('url'),
    requestDefaults = {
      request: {

      },
      caching: {
        ttl: 60 * 60,
        prefix: 'requestCaching'
      }
    };

function mergeOptions(obj1, obj2) {
  for (var prop in obj2) {
    try {
      if (typeof obj2[prop] === 'object') {
        obj1[prop] = mergeOptions(obj1[prop], obj2[prop]);
      } else if (typeof obj1[prop] === 'undefined') {
        obj1[prop] = obj2[prop];
      }
    } catch (err) {
      obj1[prop] = obj2[prop];
    }
  }

  return obj1;
}

function initRequestOptions(options) {
  if (!options)
    throw new Error('Missing [options] argument');
  if (typeof options !== 'object')
    throw new Error('[options] must be an object');

  mergeOptions(options, requestDefaults);

  if (!(typeof options.caching.ttl === 'number' && options.caching.ttl >= 0))
    throw new Error('[options.caching.ttl] must be a number >= 0');

  if (typeof options.caching.prefix !== 'string')
    throw new Error('[options.caching.prefix] must be a string');

  if (options.caching.key && typeof options.caching.key !== 'string')
    throw new Error('[options.caching.key] must be a string');

  if (typeof options.uri === 'string') {
    var parts = url.parse(options.uri);
    options.request.host = parts.hostname ||  '';
    options.request.port = parts.port;
    options.request.protocol = parts.protocol ||  'http:';
    options.request.path = parts.path ||  '';
  }

  if (options.params && typeof options.params === 'object') {
    if (options.request.method === 'GET' && typeof options.request.path === 'string') {
      var parts = url.parse(options.request.path, true);
      mergeOptions(options.params, parts.query);
      options.paramsStringified = querystring.stringify(options.params);
      options.request.path = parts.pathname + '?' + options.paramsStringified;
    }
    else if (options.request.method === 'POST') {
      options.paramsStringified = querystring.stringify(options.params);
      if (options.paramsStringified.length > 0) {
        if (!options.request.header)
          options.request.header = {};

        mergeOptions(options.request.header, {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': options.paramsStringified.length
        });
      }
    }
  }

  if (!options.caching.key) {
    options.caching.key = crypto.createHash('md5').update(JSON.stringify(options.request)).digest('hex');
  }

  return options;
}

var RequestCaching = function(options) {
  if (!options)
    options = {};
  if (typeof options !== 'object')
    throw new Error('[options] must be an object');
  if (!options.store)
    options.store = 'memory';
  if (typeof options.store !== 'string')
    throw new Error('[options.store] must be a string');

  try {
    var Store = require('./store/' + options.store);
    this._store = new Store();
  } catch (err) {
    throw new Error('Cannot find an implementation for [options.store] "' + options.store + '"');
  }
};

RequestCaching.prototype.request = function(options, callback) {
  if (callback && typeof callback !== 'function')
    throw new Error('[callback] must be a function');

  var options = initRequestOptions(options),
      self = this,
      key = (options.caching.prefix ? options.caching.prefix + ':' : '') + options.caching.key;

  self._store.get(key, function(err, data) {
    if (!err &&  data) {
      callback.apply(self, [null, data.res, data.body, {key: key, hit: true}]);
    }
    else {
      var client = options.request.protocol === 'https:' ? https : http;
      var req = client.request(options.request, function(res) {
        if (!callback) return;

        var body = '';

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

        res.on('end', function() {
          self._store.set(key, {res: res, body: body}, options.caching.ttl, function(err, data) {
            if (err) {
              callback.apply(self, [err, data.res, data.body, {key: key, hit: false}]);
            }
            else {
              callback.apply(self, [null, data.res, data.body, {key: key, hit: false}]);
            }
          });
        });
      });

      req.on('error', function(err) {
        if (!callback) return;
        callback.apply(this, [err, null, null]);
      });

      if (typeof options.paramsStringified === 'string' &&
          options.paramsStringified.length > 0 &&
          options.request.method === 'POST') {
        req.write(options.paramsStringified);
      }

      req.end();
    }
  });
};

RequestCaching.prototype.get = function(uri, queryString, ttl, callback) {
  return this.request({
    uri: uri,
    params: queryString,
    request: {
      method: 'GET'
    },
    caching: {
      ttl: ttl
    }
  }, callback);
};

RequestCaching.prototype.post = function(uri, postData, ttl, callback) {
  return this.request({
    uri: uri,
    params: postData,
    request: {
      method: 'POST'
    },
    caching: {
      ttl: ttl
    }
  }, callback);
};

module.exports = RequestCaching;
<|endoftext|>