<commit_before>/**
* Copyright (c) 2014, salesforce.com, inc.
* 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.
*
*    Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
*    promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/

'use strict';

var version     = require( '../package.json' ).version;
var helpers     = require( './helpers' );
var request     = require( 'request' );
var _           = require( 'lodash' );
var xml2js      = require( 'xml2js' );
var parseString = require( 'xml2js' ).parseString;
var FuelAuth    = require( 'fuel-auth' );

var FuelSoap = function (options) {
	var authOptions = options && options.auth || {};

	// use fuel auth instance if applicable
	if( authOptions instanceof  FuelAuth ) {
		this.AuthClient = authOptions;
	} else {
		try {
			this.AuthClient = new FuelAuth( authOptions );
		} catch ( err ) {
			throw err;
		}
	}

	// adding version to object
	this.version = version;

	// setting up default headers
	this.defaultHeaders = {
		'User-Agent': 'node-fuel/' + this.version,
		'Content-Type': 'text/xml'
	};

	// configuring soap options
	this.requestOptions = {};
	this.requestOptions.uri = options.soapEndpoint || 'https://webservice.exacttarget.com/Service.asmx';
	this.requestOptions.method = 'POST';
};

FuelSoap.prototype.soapRequest = function (options, callback) {
	// we need a callback
	if( !_.isFunction( callback ) ) {
		throw new TypeError( 'callback argument is required' );
	}

	// we need options
	if( !_.isPlainObject( options ) ) {
		throw new TypeError( 'options argument is required' );
	}

	var requestOptions = this.requestOptions;
	requestOptions.headers = this.defaultHeaders;
	requestOptions.headers.SOAPAction = options.action;

	this.AuthClient.getAccessToken( _.clone( options.auth ), function( err, body ) {
		var localError, retry, authOptions;

		if( err ) {
			helpers.deliverResponse( 'error', err, callback, 'FuelAuth' );
			return;
		}

		// if there's no access token we have a problem
		if ( !body.accessToken ) {
			localError = new Error( 'No access token' );
			localError.res = body;
			helpers.deliverResponse( 'error', localError, callback, 'FuelAuth' );
			return;
		}

		// retry request?
		retry       = options.retry || false;
		authOptions = _.clone( options.auth );

		// clean up
		delete options.retry;
		delete options.auth;

		var env = this._buildEnvelope(options.req, body.accessToken);
		console.log(env);
		requestOptions.body = env;

		request(requestOptions, function (err, res, body) {
			if (err) {
				helpers.deliverResponse( 'error', err, callback, 'Request Module inside soapRequest' );
				return;
			}

			// parse the response
			this._parseResponse( options.key, body, function(err, data) {
				if (err && helpers.checkExpiredToken(err) && retry) {
					options.auth = authOptions;
					this.soapRequest(options, callback);
					return;
				}

				if (err) {
					helpers.deliverResponse( 'error', err, callback );
				} else {
					helpers.deliverResponse( 'response', {body: data, res: res}, callback );
				}
			});
		}.bind( this ) );
	}.bind( this ) );
};

FuelSoap.prototype.create = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'CreateRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.CreateRequest.Objects = props;
	body.CreateRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Create',
		req: body,
		key: 'CreateResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.retrieve = function (type, props, filter, callback) {
	var defaultProps = ['Client', 'ID', 'ObjectID'];

	if (arguments.length < 4) {
		//if props and filter are not included
		if (Object.prototype.toString.call(arguments[1]) === "[object Function]") {
			callback = props;
			filter = null;
			props = defaultProps;
		}

		//if props or filter is included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {

			callback = filter;
			//check if props or filter
			if (Object.prototype.toString.call(arguments[1]) === "[object Object]") {
				filter = props;
				props = defaultProps;
			} else {
				filter = null;
			}
		}
	}

	//TO-DO How to handle casing with properties?

	var body = {
		'RetrieveRequestMsg': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'RetrieveRequest': {
				'ObjectType': type,
				'Properties': props
			}
		}
	};

	// filter can be simple or complex and has three properties leftOperand, rightOperand, and operator
	if (filter) {
		body.RetrieveRequestMsg.RetrieveRequest.Filter = this._parseFilter(filter);
	}

	this.soapRequest({
		action: 'Retrieve',
		req: body,
		key: 'RetrieveResponseMsg',
		retry: true
	}, callback);
};

// TO-DO Handle other simple filter value types like DateValue
FuelSoap.prototype._parseFilter = function(filter) {
	var fType = (_.isObject(filter.leftOperand) && _.isObject(filter.rightOperand)) ? "Complex" : "Simple";
	var obj = {
		'$': {
			'xsi:type': fType + "FilterPart"
		}
	};

	if (fType === "Complex") {
		obj.LeftOperand = this._parseFilter(filter.leftOperand);
		obj.LogicalOperator = filter.operator;
		obj.RightOperand = this._parseFilter(filter.rightOperand);
	} else {
		obj.Property = filter.leftOperand;
		obj.SimpleOperator = filter.operator;
		obj.Value = filter.rightOperand;
	}

	return obj;
};

FuelSoap.prototype.update = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'UpdateRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.UpdateRequest.Objects = props;
	body.UpdateRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Update',
		req: body,
		key: 'UpdateResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.delete = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'DeleteRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.DeleteRequest.Objects = props;
	body.DeleteRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Delete',
		req: body,
		key: 'DeleteResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.query = function () {

};

FuelSoap.prototype.describe = function (type, callback) {

	var body = {
		DefinitionRequestMsg: {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'DescribeRequests': {
				'ObjectDefinitionRequest': {
					'ObjectType': type
				}
			}
		}
	};

	this.soapRequest({
		action: 'Describe',
		req: body,
		key: 'DefinitionResponseMsg',
		retry: true
	}, callback);
};

FuelSoap.prototype.execute = function () {

};

FuelSoap.prototype.perform = function () {

};

FuelSoap.prototype.configure = function () {

};

FuelSoap.prototype.schedule = function () {

};

FuelSoap.prototype.versionInfo = function () {

};

FuelSoap.prototype.extract = function () {

};

FuelSoap.prototype.getSystemStatus = function () {

};

FuelSoap.prototype._buildEnvelope = function (request, token) {

	var envelope = {
		'$': {
			"xmlns": "http://schemas.xmlsoap.org/soap/envelope/",
			"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
		},
		'Header': {
			'fueloauth': {
				'$': {
					"xmlns": "http://exacttarget.com"
				},
				"_": token
			}
		},
		"Body": request
	};

	var buildOptions = {
		rootName: "Envelope",
		headless: true
	};

	var builder = new xml2js.Builder(buildOptions);
	return builder.buildObject(envelope);
};

FuelSoap.prototype._parseResponse = function(key, body, callback) {
	var parseOptions = {
		trim: true,
		normalize: true,
		explicitArray: false,
		ignoreAttrs: true
	};

	parseString(body, parseOptions, function (err, res) {

		if( err ) {
			err.errorPropagatedFrom = 'xml2js.parseString';
			callback( err, null );
			return;
		}

		var soapError;
		var soapBody = res['soap:Envelope']['soap:Body'];

		// Check for SOAP Fault
		if ( soapBody['soap:Fault']) {
			var fault = soapBody['soap:Fault'];
			soapError = new Error(fault.faultstring);
			soapError.faultstring = fault.faultstring;
			soapError.faultCode = fault.faultcode;
			if ( fault.detail ) {
				soapError.detail = fault.detail;
			}
			soapError.errorPropagatedFrom = 'SOAP Fault';
			callback( soapError, null );
			return;
		}

		var parsedRes = soapBody[key];

		if (key === 'DefinitionResponseMsg') {
			// Return empty object if no ObjectDefinition is returned.
			parsedRes.ObjectDefinition = parsedRes.ObjectDefinition || {};
			callback( null, parsedRes );
			return;
		}

		// Results should always be an array
		parsedRes.Results = _.isArray(parsedRes.Results) ? parsedRes.Results : _.isObject(parsedRes.Results) ? [parsedRes.Results] : [];

		if (key === 'RetrieveResponseMsg') {
			if ( parsedRes.OverallStatus === 'OK' || parsedRes.OverallStatus === 'MoreDataAvailable') {
				callback( null, parsedRes );
			} else {
				// This is an error
				console.log(parsedRes.OverallStatus.split(':')[1].trim());
				soapError = new Error(parsedRes.OverallStatus.split(':')[1].trim());
				soapError.requestId = parsedRes.RequestID;
				soapError.errorPropagatedFrom = 'Retrieve Response';
				callback( soapError, null );
			}
			return;
		}

		if ( parsedRes.OverallStatus === 'Error' ||  parsedRes.OverallStatus === 'Has Errors') {
			soapError = new Error('Soap Error');
			soapError.requestId = parsedRes.RequestID;
			soapError.results = parsedRes.Results;
			soapError.errorPropagatedFrom = key;
			callback( soapError, null );
			return;
		}

		callback( null, parsedRes );
	}.bind( this ) );
};

module.exports = FuelSoap;
<commit_msg>adding client ID to retrieve<commit_after>/**
* Copyright (c) 2014?, salesforce.com, inc.
* 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.
*
*    Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
*    promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/

'use strict';

var version     = require( '../package.json' ).version;
var helpers     = require( './helpers' );
var request     = require( 'request' );
var _           = require( 'lodash' );
var xml2js      = require( 'xml2js' );
var parseString = require( 'xml2js' ).parseString;
var FuelAuth    = require( 'fuel-auth' );

var FuelSoap = function (options) {
	var authOptions = options && options.auth || {};

	// use fuel auth instance if applicable
	if( authOptions instanceof  FuelAuth ) {
		this.AuthClient = authOptions;
	} else {
		try {
			this.AuthClient = new FuelAuth( authOptions );
		} catch ( err ) {
			throw err;
		}
	}

	// adding version to object
	this.version = version;

	// setting up default headers
	this.defaultHeaders = {
		'User-Agent': 'node-fuel/' + this.version,
		'Content-Type': 'text/xml'
	};

	// configuring soap options
	this.requestOptions = {};
	this.requestOptions.uri = options.soapEndpoint || 'https://webservice.exacttarget.com/Service.asmx';
	this.requestOptions.method = 'POST';
};

FuelSoap.prototype.soapRequest = function (options, callback) {
	// we need a callback
	if( !_.isFunction( callback ) ) {
		throw new TypeError( 'callback argument is required' );
	}

	// we need options
	if( !_.isPlainObject( options ) ) {
		throw new TypeError( 'options argument is required' );
	}

	var requestOptions = this.requestOptions;
	requestOptions.headers = this.defaultHeaders;
	requestOptions.headers.SOAPAction = options.action;

	this.AuthClient.getAccessToken( _.clone( options.auth ), function( err, body ) {
		var localError, retry, authOptions;

		if( err ) {
			helpers.deliverResponse( 'error', err, callback, 'FuelAuth' );
			return;
		}

		// if there's no access token we have a problem
		if ( !body.accessToken ) {
			localError = new Error( 'No access token' );
			localError.res = body;
			helpers.deliverResponse( 'error', localError, callback, 'FuelAuth' );
			return;
		}

		// retry request?
		retry       = options.retry || false;
		authOptions = _.clone( options.auth );

		// clean up
		delete options.retry;
		delete options.auth;

		var env = this._buildEnvelope(options.req, body.accessToken);
		console.log(env);
		requestOptions.body = env;

		request(requestOptions, function (err, res, body) {
			if (err) {
				helpers.deliverResponse( 'error', err, callback, 'Request Module inside soapRequest' );
				return;
			}

			// parse the response
			this._parseResponse( options.key, body, function(err, data) {
				if (err && helpers.checkExpiredToken(err) && retry) {
					options.auth = authOptions;
					this.soapRequest(options, callback);
					return;
				}

				if (err) {
					helpers.deliverResponse( 'error', err, callback );
				} else {
					helpers.deliverResponse( 'response', {body: data, res: res}, callback );
				}
			});
		}.bind( this ) );
	}.bind( this ) );
};

FuelSoap.prototype.create = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'CreateRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.CreateRequest.Objects = props;
	body.CreateRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Create',
		req: body,
		key: 'CreateResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.retrieve = function (type, props, options, callback) {
	var defaultProps = ['Client', 'ID', 'ObjectID'];
	var filter = null;
	var clientIDs =  null;
	if (arguments.length < 4) {
		//if props and options are not included
		if (Object.prototype.toString.call(arguments[1]) === "[object Function]") {
			callback = props;
			filter = null;
			options = null;
			clientIDs = null;
			props = defaultProps;
		}

		//if props or options is included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {

			callback = options;
			//check if props or filter
			if (Object.prototype.toString.call(arguments[1]) === "[object Object]") {
				filter = options.filter;
				clientIDs = options.clientIDs;
				props = defaultProps;
			} else {
				filter = null;
				options = null;
				clientIDs =  null;
			}
		}
	}
	else {
		filter = options.filter;
		clientIDs = options.clientIDs;
	}

	//TO-DO How to handle casing with properties?
	if(clientIDs) {
		var body = {
			'RetrieveRequestMsg': {
				'$': {
					'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
				},
				'RetrieveRequest': {
					'ObjectType': type,
					'Properties': props,
					'ClientIDs': clientIDs
				}
			}
		};
	} else {
		var body = {
			'RetrieveRequestMsg': {
				'$': {
					'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
				},
				'RetrieveRequest': {
					'ObjectType': type,
					'Properties': props
				}
			}
		};
	}

	// filter can be simple or complex and has three properties leftOperand, rightOperand, and operator
	if (filter) {
		body.RetrieveRequestMsg.RetrieveRequest.Filter = this._parseFilter(filter);
	}

	this.soapRequest({
		action: 'Retrieve',
		req: body,
		key: 'RetrieveResponseMsg',
		retry: true
	}, callback);
};

// TO-DO Handle other simple filter value types like DateValue
FuelSoap.prototype._parseFilter = function(filter) {
	var fType = (_.isObject(filter.leftOperand) && _.isObject(filter.rightOperand)) ? "Complex" : "Simple";
	var obj = {
		'$': {
			'xsi:type': fType + "FilterPart"
		}
	};

	if (fType === "Complex") {
		obj.LeftOperand = this._parseFilter(filter.leftOperand);
		obj.LogicalOperator = filter.operator;
		obj.RightOperand = this._parseFilter(filter.rightOperand);
	} else {
		obj.Property = filter.leftOperand;
		obj.SimpleOperator = filter.operator;
		obj.Value = filter.rightOperand;
	}

	return obj;
};

FuelSoap.prototype.update = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'UpdateRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.UpdateRequest.Objects = props;
	body.UpdateRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Update',
		req: body,
		key: 'UpdateResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.delete = function (type, props, options, callback) {

	if (arguments.length < 4) {
		//if options are not included
		if (Object.prototype.toString.call(arguments[2]) === "[object Function]") {
			callback = options;
			options = null;
		}
	}

	var body = {
		'DeleteRequest': {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'Options': options
		}
	};

	body.DeleteRequest.Objects = props;
	body.DeleteRequest.Objects.$ = {
		'xsi:type': type
	};

	this.soapRequest({
		action: 'Delete',
		req: body,
		key: 'DeleteResponse',
		retry: true
	}, callback);
};

FuelSoap.prototype.query = function () {

};

FuelSoap.prototype.describe = function (type, callback) {

	var body = {
		DefinitionRequestMsg: {
			'$': {
				'xmlns': 'http://exacttarget.com/wsdl/partnerAPI'
			},
			'DescribeRequests': {
				'ObjectDefinitionRequest': {
					'ObjectType': type
				}
			}
		}
	};

	this.soapRequest({
		action: 'Describe',
		req: body,
		key: 'DefinitionResponseMsg',
		retry: true
	}, callback);
};

FuelSoap.prototype.execute = function () {

};

FuelSoap.prototype.perform = function () {

};

FuelSoap.prototype.configure = function () {

};

FuelSoap.prototype.schedule = function () {

};

FuelSoap.prototype.versionInfo = function () {

};

FuelSoap.prototype.extract = function () {

};

FuelSoap.prototype.getSystemStatus = function () {

};

FuelSoap.prototype._buildEnvelope = function (request, token) {

	var envelope = {
		'$': {
			"xmlns": "http://schemas.xmlsoap.org/soap/envelope/",
			"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
		},
		'Header': {
			'fueloauth': {
				'$': {
					"xmlns": "http://exacttarget.com"
				},
				"_": token
			}
		},
		"Body": request
	};

	var buildOptions = {
		rootName: "Envelope",
		headless: true
	};

	var builder = new xml2js.Builder(buildOptions);
	return builder.buildObject(envelope);
};

FuelSoap.prototype._parseResponse = function(key, body, callback) {
	var parseOptions = {
		trim: true,
		normalize: true,
		explicitArray: false,
		ignoreAttrs: true
	};

	parseString(body, parseOptions, function (err, res) {

		if( err ) {
			err.errorPropagatedFrom = 'xml2js.parseString';
			callback( err, null );
			return;
		}

		var soapError;
		var soapBody = res['soap:Envelope']['soap:Body'];

		// Check for SOAP Fault
		if ( soapBody['soap:Fault']) {
			var fault = soapBody['soap:Fault'];
			soapError = new Error(fault.faultstring);
			soapError.faultstring = fault.faultstring;
			soapError.faultCode = fault.faultcode;
			if ( fault.detail ) {
				soapError.detail = fault.detail;
			}
			soapError.errorPropagatedFrom = 'SOAP Fault';
			callback( soapError, null );
			return;
		}

		var parsedRes = soapBody[key];

		if (key === 'DefinitionResponseMsg') {
			// Return empty object if no ObjectDefinition is returned.
			parsedRes.ObjectDefinition = parsedRes.ObjectDefinition || {};
			callback( null, parsedRes );
			return;
		}

		// Results should always be an array
		parsedRes.Results = _.isArray(parsedRes.Results) ? parsedRes.Results : _.isObject(parsedRes.Results) ? [parsedRes.Results] : [];

		if (key === 'RetrieveResponseMsg') {
			if ( parsedRes.OverallStatus === 'OK' || parsedRes.OverallStatus === 'MoreDataAvailable') {
				callback( null, parsedRes );
			} else {
				// This is an error
				console.log(parsedRes.OverallStatus.split(':')[1].trim());
				soapError = new Error(parsedRes.OverallStatus.split(':')[1].trim());
				soapError.requestId = parsedRes.RequestID;
				soapError.errorPropagatedFrom = 'Retrieve Response';
				callback( soapError, null );
			}
			return;
		}

		if ( parsedRes.OverallStatus === 'Error' ||  parsedRes.OverallStatus === 'Has Errors') {
			soapError = new Error('Soap Error');
			soapError.requestId = parsedRes.RequestID;
			soapError.results = parsedRes.Results;
			soapError.errorPropagatedFrom = key;
			callback( soapError, null );
			return;
		}

		callback( null, parsedRes );
	}.bind( this ) );
};

module.exports = FuelSoap;
<|endoftext|>


<commit_before>var functions = exports;

//
// ### function returns (value)
// #### @value Function return value
// Creates a function which alwas returns `value`
//
functions.returns = function (value) {
  return function () {
    return value;
  }
};

//
// ### function throws (error)
// #### @error Error which should be thrown
// Throws `error` when called.
//
functions.throws = function (error) {
  return function () {
    throw error;
  };
};

//
// ### function throwsIfError ()
// #### @error Value to throw when first parameter is truthy. Optional.
// Returns a function throwing `error` or first parameter when first supplied
// parameter is truthy.
//
functions.throwsIfError = function (error) {
  return function (err) {
    if (err) {
      throw (typeof error !== 'undefined') ? error : err;
    }
  };
};

//
// ### function noop ()
// Does nothing.
//
functions.noop = function () {};

//
// ### function eq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 == val1`
//
functions.eq = function (val0, val1) {
  return val0 == val1;
};

//
// ### function neq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 != val1`
//
functions.neq = function (val0, val1) {
  return val0 != val1;
};

//
// ### function strictEq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 === val1`
//
functions.strictEq = function (val0, val1) {
  return val0 === val1;
};

//
// ### function gt (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 > val1`
//
functions.gt = function (val0, val1) {
  return val0 > val1;
};

//
// ### function lt (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 < val1`
//
functions.lt = function (val0, val1) {
  return val0 < val1;
};

//
// ### function ge (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 >= val1`
//
functions.ge = function (val0, val1) {
  return val0 >= val1;
};

//
// ### function le (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 <= val1`
//
functions.le = function (val0, val1) {
  return val0 <= val1;
};

<commit_msg>[functions] `strictNeq` function<commit_after>var functions = exports;

//
// ### function returns (value)
// #### @value Function return value
// Creates a function which alwas returns `value`
//
functions.returns = function (value) {
  return function () {
    return value;
  }
};

//
// ### function throws (error)
// #### @error Error which should be thrown
// Throws `error` when called.
//
functions.throws = function (error) {
  return function () {
    throw error;
  };
};

//
// ### function throwsIfError ()
// #### @error Value to throw when first parameter is truthy. Optional.
// Returns a function throwing `error` or first parameter when first supplied
// parameter is truthy.
//
functions.throwsIfError = function (error) {
  return function (err) {
    if (err) {
      throw (typeof error !== 'undefined') ? error : err;
    }
  };
};

//
// ### function noop ()
// Does nothing.
//
functions.noop = function () {};

//
// ### function eq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 == val1`
//
functions.eq = function (val0, val1) {
  return val0 == val1;
};

//
// ### function neq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 != val1`
//
functions.neq = function (val0, val1) {
  return val0 != val1;
};

//
// ### function strictEq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 === val1`
//
functions.strictEq = function (val0, val1) {
  return val0 === val1;
};

//
// ### function strictNeq (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 !== val1`
//
functions.strictNeq = function (val0, val1) {
  return val0 !== val1;
};

//
// ### function gt (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 > val1`
//
functions.gt = function (val0, val1) {
  return val0 > val1;
};

//
// ### function lt (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 < val1`
//
functions.lt = function (val0, val1) {
  return val0 < val1;
};

//
// ### function ge (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 >= val1`
//
functions.ge = function (val0, val1) {
  return val0 >= val1;
};

//
// ### function le (val0, val1)
// #### val0 First value
// #### val1 Second value
// Returns `val0 <= val1`
//
functions.le = function (val0, val1) {
  return val0 <= val1;
};

<|endoftext|>


<commit_before>/*globals require exports process*/

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
var fs  = require("fs");
var Buffer = require('buffer').Buffer;

/**
 * Reads the client-side files and concatenate them into a single file
 * @returns {String} the concatenated file
 */
var concatenateClientFiles = function () {
    var buffer, code = "", fd, stats, i, size = 0, file,
    files = ["msg.js", "client/AcequiaClient.js", "client/ClientStore.js"];

    for (i in files) {
        file = __dirname + "/" + files[i];
        stats = fs.statSync(file);
        buffer = new Buffer(stats.size);

        fd = fs.openSync(file, "r");
        size += fs.readSync(fd, buffer, 0, stats.size, null);
        fs.close(fd);

        code += buffer.toString("utf8");
    }

    return code;
};

/**
 * Returns the minified version of the client code.
 * @param {String} code The full client codfe.
 * @return {String} The minified client code.
 */
var minifyClientFiles = function (code) {
    var ast = jsp.parse(code);
    ast = pro.ast_mangle(ast);
    ast = pro.ast_squeeze(ast);
    return pro.gen_code(ast);
};

/**
 * Generates the client code for the server to serve up.
 */
exports.generateClientCode = function () {
    var full = concatenateClientFiles(),
    minified = minifyClientFiles(full);

    return {
        full: full,
        minified: minified
    };
};<commit_msg>Works with latest version of uglify-js.<commit_after>/*globals require exports process*/

var UglifyJS = require("uglify-js");
var fs  = require("fs");
var Buffer = require('buffer').Buffer;

/**
 * Reads the client-side files and concatenate them into a single file
 * @returns {String} the concatenated file
 */
var concatenateClientFiles = function () {
    var buffer, code = "", fd, stats, i, size = 0, file,
    files = ["msg.js", "client/AcequiaClient.js", "client/ClientStore.js"];

    for (i in files) {
        file = __dirname + "/" + files[i];
        stats = fs.statSync(file);
        buffer = new Buffer(stats.size);

        fd = fs.openSync(file, "r");
        size += fs.readSync(fd, buffer, 0, stats.size, null);
        fs.close(fd);

        code += buffer.toString("utf8");
    }

    return code;
};

/**
 * Returns the minified version of the client code.
 * @param {String} code The full client codfe.
 * @return {String} The minified client code.
 */
var minifyClientFiles = function (code) {
    return UglifyJS.minify(code, {fromString: true}).code;
};

/**
 * Generates the client code for the server to serve up.
 */
exports.generateClientCode = function () {
    var full = concatenateClientFiles(),
    minified = minifyClientFiles(full);

    return {
        full: full,
        minified: minified
    };
};<|endoftext|>


<commit_before>"use strict";
/*global console: true, ODSA */

(function ($) {
  var settings = ODSA.SETTINGS,
      odsaUtils = ODSA.UTILS;

  var Status = ODSA.MOD.STATUS;

  /**
   * How fast to show and hide subsections
   */
  var speed = 300;

  var chapters = {};
  var assignments = {};
  var ex_mod_map = {};
  /**
   * Utility function to search objects in JSON
  */
  function getObjects(obj, key, val) {
    var objects = [];
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        if (typeof obj[i] === 'object') {
          objects = objects.concat(getObjects(obj[i], key, val));
        } else if ((i === key) && (obj[key] === val)) {
          objects.push(obj);
        }
      }
    }
    return objects;
  }

  /**
   * Generates a table of exercises and the user's score, for the given module
   */
  function generateModule(modName, modExercises) {
    if (settings.DEBUG_MODE) {
      console.group('generateModule(' + modName + ', modExercises)');
      console.debug(JSON.stringify(modExercises));
    }

    var userTotal = 0,
        modTotal = 0,
        html = '';

    if (Object.keys(modExercises).length === 0) { // Module contains no exercises
      html = 'There are no exercises in this section';
    } else {
      // If this value stays at -1 after looping through all exercises,
      // there are no exercises worth points in the module
      var exerPoints = -1;

      // Generate the table of exercises
      html = '
';
      html += '
Exercises	Points
';

      var exercise_;

      for (var exercise in modExercises) {
        if (modExercises.hasOwnProperty(exercise) && typeof modExercises[exercise].points !== "undefined") {
          ex_mod_map[exercise] = modName;
          exercise_ = exercise;
          if (!modExercises[exercise].hasOwnProperty('long_name')) {
            modExercises[exercise].long_name = modExercises[exercise].exercise;
            if (modExercises[exercise].points === 0) {
              var obj_Exe = getObjects(chapters, 'long_name', modExercises[exercise].exercise);
              if (typeof obj_Exe[0] !== "undefined") {
                modExercises[exercise].points = obj_Exe[0].points;
              } else {
                modExercises[exercise].points = 0;
              }
            }
            exercise_ = modExercises[exercise].name;
          }
          // Determine how many points the exercise is worth (to the nearest 2 decimal places and keep a running total for the module
          exerPoints = Math.round(parseFloat(modExercises[exercise].points) * 100) / 100;
          modTotal += exerPoints;

          // Create a row in the table for the exercise
          html += '
' + modExercises[exercise].long_name + '	';
          if (ODSA.MOD.getProficiencyStatus(exercise_) === Status.STORED) {
            // User is proficient with this exercise
            userTotal += exerPoints;
            html += '' + exerPoints.toFixed(2);
          } else {
            html += '0.00';
          }
          html += ' / ' + exerPoints.toFixed(2) + '
';
        }
      }

      // Append the total row
      html += '
Total	' + userTotal.toFixed(2) + ' / ' + modTotal.toFixed(2) + '

';

      // If the module contains only exercises that don't have points, list the module as containing no exercises
      if (exerPoints === -1) {
        html = 'There are no exercises in this section';
      }
    }

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }

    return {'userTotal': userTotal, 'total': modTotal, 'html': html};
  }

  /**
   * Generate the necessary rows and tables for a section of the book (chapter, section, subsection, etc)
   */
  function generateSection(secData, prefix, depth) {
    if (settings.DEBUG_MODE) {
      console.group('generateSection(secData, ' + depth + ')');
      console.debug(JSON.stringify(secData));
    }

    var i = 0,
        userTotal = 0,
        sectionTotal = 0,
        data,
        row,
        html = '
';

    for (var sect in secData) {
      if (secData.hasOwnProperty(sect)) {
        // Calculate the new prefix
        i++;
        var newPrefix = (prefix === '') ? prefix + (i - 1) : prefix + '.' + i;

        var modName = sect.slice(sect.lastIndexOf('/') + 1).replace('.rst', '');

        // Call the appropriate function for processing a module or a subsection
        if (secData[sect].exercises) {
          if (secData[sect].assignment) {
            secData[sect].long_name = secData[sect].assignment + ' (' +  secData[sect].due_date + ')';
            data = generateModule(secData[sect].assignment, secData[sect].exercises);
          } else {
            data = generateModule(modName, secData[sect].exercises);
          }
        } else {
          data = generateSection(secData[sect], newPrefix, depth + 1);

          // If an entire section is not worth any points, don't include it in the gradebook
          if (data.total === 0) {
            continue;
          }
        }

        // Keep a total of how many points a user has earned and how much each section is worth
        userTotal += data.userTotal;
        sectionTotal += data.total;

        // Remove the periods from the prefix and '/' from the section name, so that jQuery can properly use IDs
        var secName = newPrefix.replace(/\./g, '') + '-' + sect.replace(/\//g, '');
        secName = secName.replace(/\s/g, '_');

        // Add a row for the section
        html += '
' + newPrefix + ' ';
        html += ((secData[sect].exercises && secData[sect].long_name) ? secData[sect].long_name : sect) + '	';
        html += (ODSA.MOD.getProficiencyStatus(modName) === Status.STORED) ? '' : '';
        html += data.userTotal.toFixed(2) + ' / ' + data.total.toFixed(2) + '
';
        html += '
';
        html += data.html;
        html += '
';
      }
    }

    html += '
';

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }

    return {'userTotal': userTotal, 'total': sectionTotal, 'html': html};
  }

  /**
   * Get the config file and use it to initialize the gradebook
   * Attach click handlers to section headers to expand their subsections
   */
  function generateGradeTable() {
    // Hide the "loading" message
    $('#loadingMessage').hide();
    $('#gradeHeader').show();

    var data = generateSection(chapters, '', 0);
    var data1 = generateSection(assignments, '', 0);
    var html = data.html;
    html = html.slice(html.indexOf('<tr'), html.lastIndexOf('</table'));
    html = '

Chapter	Score
' + html;
    html += '
Total	' + data.userTotal.toFixed(2) + ' / ' + data.total.toFixed(2) + '

';
    $('#gradeData').append(html);

    var html1 = data1.html;
    html1 = html1.slice(html1.indexOf('<tr'), html1.lastIndexOf('</table'));
    html1 = '

Assignment (Due Date)	Score
' + html1;
    html1 += '
Total	' + data1.userTotal.toFixed(2) + ' / ' + data1.total.toFixed(2) + '

';
    $('#gradeDataA').append(html1);

    // Hide all zero point exercises by default
    $('.zeroPointExer').hide();
    $('#showZeroPointExer').removeAttr('checked');

    // Collapse all the containers
    $('.section-header').addClass('expandImage');

    $('.section-header').each(function (index, item) {
      // Attach a click handler to all section headers that makes the appropriate container appear
      $(this).click(function (event) {
        var secName = $(event.target).data('sec-name');

        if (secName) {
          var container = $('#' + secName);

          if ($(this).hasClass('expandImage')) {
            container.show(speed);
            $(this).removeClass('expandImage');
            $(this).addClass('collapseImage');
          } else {
            container.hide(speed);
            $(this).removeClass('collapseImage');
            $(this).addClass('expandImage');
          }
        }
      });
    });

    // Hide assignment view ny default
    $('#selectGradeView').attr('checked', 'checked');
    $('#gradeDataA').hide();
  }

  /**
   * If the server is enabled and a user is logged in, queries the server for the user's points and updates local proficiency cache
   * Loads the grade book page using the config file
   */
  function loadGradebook() { // Queries the server for the user's points
    if (settings.DEBUG_MODE) {
      console.group('loadGradebook()');
      console.debug('book: ' + settings.BOOK_NAME);
    }

    // Clear previous grade data, show the "loading" message
    $('#gradeData').html('');
    $('#gradeDataA').html('');
    $('#gradeHeader').hide();
    $('#loadingMessage').show();

    // Sync local proficiency cache with server
    if (odsaUtils.serverEnabled() && odsaUtils.userLoggedIn()) {
      ODSA.MOD.syncProficiency();
    } else {
      generateGradeTable();
    }

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }
  }

  function showErrorMsg() {
    // Remove the loading message and display an error message to the user
    $('#loadingMessage').hide();
    $('#gradeData').replaceWith('
The server did not respond.  Please try again later.
');
  }

  function expandAll() {
    $('.gb-section-container').show(speed);
    $('.section-header').removeClass('expandImage');
    $('.section-header').addClass('collapseImage');
  }

  function collapseAll() {
    $('.gb-section-container').hide(speed);
    $('.section-header').addClass('expandImage');
    $('.section-header').removeClass('collapseImage');
  }

  $(document).ready(function () {
    // Get the config file and use it to initialize chapters
    $.getJSON(location.href.substring(0, location.href.lastIndexOf('/')) + '/_static/' + settings.BOOK_NAME + '.json', function (confData) {
      chapters = confData.chapters;
      // Get the assignments list from the localstorage
      assignments = odsaUtils.getJSON(localStorage.assignments);
      loadGradebook();
    });

    // Listen for event that triggers a gradebook load
    $("body").on("gradebook-load", function (e, data) {
      loadGradebook();
    });

    // Listen for event that triggers the gradebook to generate the grade table
    $("body").on("gradebook-gen-table", function (e, data) {
      generateGradeTable();
    });

    // Listen for event that triggers the error message to be shown
    $("body").on("gradebook-error", function (e, data) {
      generateGradeTable();
    });

    // Attach the expandAll function to the 'Expand All' link
    $('#expand').click(function () {
      expandAll();
      return false;
    });

    // Attach the collapseAll function to the 'Collapse All' link
    $('#collapse').click(function () {
      collapseAll();
      return false;
    });

    // Attach a handler to the ""select grade view" checkbox which will show or hide the chapter or the assignment grade table
    $('#selectGradeView').change(function () {
      if (this.checked) {
        $('#gradeData').show();
        odsaUtils.logUserAction('odsa-gradebook-show-chapview', '');
        odsaUtils.logUserAction('odsa-gradebook-hide-assgview', '');
        $('#gradeDataA').hide();
      } else {
        $('#gradeData').hide();
        odsaUtils.logUserAction('odsa-gradebook-hide-chapview', '');
        odsaUtils.logUserAction('odsa-gradebook-show-assgview', '');
        $('#gradeDataA').show();
      }
      return false;
    });

    // Attach a handler to the "Show 0-point execises" checkbox which will show or hide exercises that are worth 0-points
    $('#showZeroPointExer').change(function () {
      if (this.checked) {
        $('.zeroPointExer').show(speed);
        odsaUtils.logUserAction('odsa-gradebook-show-zpe', '');
      } else {
        $('.zeroPointExer').hide(speed);
        odsaUtils.logUserAction('odsa-gradebook-hide-zpe', '');
      }
      return false;
    });
  });
}(jQuery));
<commit_msg>Search assignment exercises by name not by 'long_name'<commit_after>"use strict";
/*global console: true, ODSA */

(function ($) {
  var settings = ODSA.SETTINGS,
      odsaUtils = ODSA.UTILS;

  var Status = ODSA.MOD.STATUS;

  /**
   * How fast to show and hide subsections
   */
  var speed = 300;

  var chapters = {};
  var assignments = {};
  var ex_mod_map = {};
  /**
   * Utility function to search objects in JSON
  */
  function getObjects(obj, key) {
    var objects = [];
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        if (typeof obj[i] === 'object') {
          if (i === key) {
              objects.push(obj[i]);
          }
          objects = objects.concat(getObjects(obj[i], key));
        }
      }
    }
    return objects;
  }

  /**
   * Generates a table of exercises and the user's score, for the given module
   */
  function generateModule(modName, modExercises) {
    if (settings.DEBUG_MODE) {
      console.group('generateModule(' + modName + ', modExercises)');
      console.debug(JSON.stringify(modExercises));
    }

    var userTotal = 0,
        modTotal = 0,
        html = '';

    if (Object.keys(modExercises).length === 0) { // Module contains no exercises
      html = 'There are no exercises in this section';
    } else {
      // If this value stays at -1 after looping through all exercises,
      // there are no exercises worth points in the module
      var exerPoints = -1;

      // Generate the table of exercises
      html = '
';
      html += '
Exercises	Points
';

      var exercise_;

      for (var exercise in modExercises) {
        if (modExercises.hasOwnProperty(exercise) && typeof modExercises[exercise].points !== "undefined") {
          ex_mod_map[exercise] = modName;
          exercise_ = exercise;
          if (!modExercises[exercise].hasOwnProperty('long_name')) {
            modExercises[exercise].long_name = modExercises[exercise].exercise;
            if (modExercises[exercise].points === 0) {
              var obj_Exe = getObjects(chapters, modExercises[exercise].name);
              if (typeof obj_Exe[0] !== "undefined") {
                modExercises[exercise].points = obj_Exe[0].points;
              } else {
                modExercises[exercise].points = 0;
              }
            }
            exercise_ = modExercises[exercise].name;
          }
          // Determine how many points the exercise is worth (to the nearest 2 decimal places and keep a running total for the module
          exerPoints = Math.round(parseFloat(modExercises[exercise].points) * 100) / 100;
          modTotal += exerPoints;

          // Create a row in the table for the exercise
          html += '
' + modExercises[exercise].long_name + '	';
          if (ODSA.MOD.getProficiencyStatus(exercise_) === Status.STORED) {
            // User is proficient with this exercise
            userTotal += exerPoints;
            html += '' + exerPoints.toFixed(2);
          } else {
            html += '0.00';
          }
          html += ' / ' + exerPoints.toFixed(2) + '
';
        }
      }

      // Append the total row
      html += '
Total	' + userTotal.toFixed(2) + ' / ' + modTotal.toFixed(2) + '

';

      // If the module contains only exercises that don't have points, list the module as containing no exercises
      if (exerPoints === -1) {
        html = 'There are no exercises in this section';
      }
    }

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }

    return {'userTotal': userTotal, 'total': modTotal, 'html': html};
  }

  /**
   * Generate the necessary rows and tables for a section of the book (chapter, section, subsection, etc)
   */
  function generateSection(secData, prefix, depth) {
    if (settings.DEBUG_MODE) {
      console.group('generateSection(secData, ' + depth + ')');
      console.debug(JSON.stringify(secData));
    }

    var i = 0,
        userTotal = 0,
        sectionTotal = 0,
        data,
        row,
        html = '
';

    for (var sect in secData) {
      if (secData.hasOwnProperty(sect)) {
        // Calculate the new prefix
        i++;
        var newPrefix = (prefix === '') ? prefix + (i - 1) : prefix + '.' + i;

        var modName = sect.slice(sect.lastIndexOf('/') + 1).replace('.rst', '');

        // Call the appropriate function for processing a module or a subsection
        if (secData[sect].exercises) {
          if (secData[sect].assignment) {
            secData[sect].long_name = secData[sect].assignment + ' (' +  secData[sect].due_date + ')';
            data = generateModule(secData[sect].assignment, secData[sect].exercises);
          } else {
            data = generateModule(modName, secData[sect].exercises);
          }
        } else {
          data = generateSection(secData[sect], newPrefix, depth + 1);

          // If an entire section is not worth any points, don't include it in the gradebook
          if (data.total === 0) {
            continue;
          }
        }

        // Keep a total of how many points a user has earned and how much each section is worth
        userTotal += data.userTotal;
        sectionTotal += data.total;

        // Remove the periods from the prefix and '/' from the section name, so that jQuery can properly use IDs
        var secName = newPrefix.replace(/\./g, '') + '-' + sect.replace(/\//g, '');
        secName = secName.replace(/\s/g, '_');

        // Add a row for the section
        html += '
' + newPrefix + ' ';
        html += ((secData[sect].exercises && secData[sect].long_name) ? secData[sect].long_name : sect) + '	';
        html += (ODSA.MOD.getProficiencyStatus(modName) === Status.STORED) ? '' : '';
        html += data.userTotal.toFixed(2) + ' / ' + data.total.toFixed(2) + '
';
        html += '
';
        html += data.html;
        html += '
';
      }
    }

    html += '
';

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }

    return {'userTotal': userTotal, 'total': sectionTotal, 'html': html};
  }

  /**
   * Get the config file and use it to initialize the gradebook
   * Attach click handlers to section headers to expand their subsections
   */
  function generateGradeTable() {
    // Hide the "loading" message
    $('#loadingMessage').hide();
    $('#gradeHeader').show();

    var data = generateSection(chapters, '', 0);
    var data1 = generateSection(assignments, '', 0);
    var html = data.html;
    html = html.slice(html.indexOf('<tr'), html.lastIndexOf('</table'));
    html = '

Chapter	Score
' + html;
    html += '
Total	' + data.userTotal.toFixed(2) + ' / ' + data.total.toFixed(2) + '

';
    $('#gradeData').append(html);

    var html1 = data1.html;
    html1 = html1.slice(html1.indexOf('<tr'), html1.lastIndexOf('</table'));
    html1 = '

Assignment (Due Date)	Score
' + html1;
    html1 += '
Total	' + data1.userTotal.toFixed(2) + ' / ' + data1.total.toFixed(2) + '

';
    $('#gradeDataA').append(html1);

    // Hide all zero point exercises by default
    $('.zeroPointExer').hide();
    $('#showZeroPointExer').removeAttr('checked');

    // Collapse all the containers
    $('.section-header').addClass('expandImage');

    $('.section-header').each(function (index, item) {
      // Attach a click handler to all section headers that makes the appropriate container appear
      $(this).click(function (event) {
        var secName = $(event.target).data('sec-name');

        if (secName) {
          var container = $('#' + secName);

          if ($(this).hasClass('expandImage')) {
            container.show(speed);
            $(this).removeClass('expandImage');
            $(this).addClass('collapseImage');
          } else {
            container.hide(speed);
            $(this).removeClass('collapseImage');
            $(this).addClass('expandImage');
          }
        }
      });
    });

    // Hide assignment view ny default
    $('#selectGradeView').attr('checked', 'checked');
    $('#gradeDataA').hide();
  }

  /**
   * If the server is enabled and a user is logged in, queries the server for the user's points and updates local proficiency cache
   * Loads the grade book page using the config file
   */
  function loadGradebook() { // Queries the server for the user's points
    if (settings.DEBUG_MODE) {
      console.group('loadGradebook()');
      console.debug('book: ' + settings.BOOK_NAME);
    }

    // Clear previous grade data, show the "loading" message
    $('#gradeData').html('');
    $('#gradeDataA').html('');
    $('#gradeHeader').hide();
    $('#loadingMessage').show();

    // Sync local proficiency cache with server
    if (odsaUtils.serverEnabled() && odsaUtils.userLoggedIn()) {
      ODSA.MOD.syncProficiency();
    } else {
      generateGradeTable();
    }

    if (settings.DEBUG_MODE) {
      console.groupEnd();
    }
  }

  function showErrorMsg() {
    // Remove the loading message and display an error message to the user
    $('#loadingMessage').hide();
    $('#gradeData').replaceWith('
The server did not respond.  Please try again later.
');
  }

  function expandAll() {
    $('.gb-section-container').show(speed);
    $('.section-header').removeClass('expandImage');
    $('.section-header').addClass('collapseImage');
  }

  function collapseAll() {
    $('.gb-section-container').hide(speed);
    $('.section-header').addClass('expandImage');
    $('.section-header').removeClass('collapseImage');
  }

  $(document).ready(function () {
    // Get the config file and use it to initialize chapters
    $.getJSON(location.href.substring(0, location.href.lastIndexOf('/')) + '/_static/' + settings.BOOK_NAME + '.json', function (confData) {
      chapters = confData.chapters;
      // Get the assignments list from the localstorage
      assignments = odsaUtils.getJSON(localStorage.assignments);
      loadGradebook();
    });

    // Listen for event that triggers a gradebook load
    $("body").on("gradebook-load", function (e, data) {
      loadGradebook();
    });

    // Listen for event that triggers the gradebook to generate the grade table
    $("body").on("gradebook-gen-table", function (e, data) {
      generateGradeTable();
    });

    // Listen for event that triggers the error message to be shown
    $("body").on("gradebook-error", function (e, data) {
      generateGradeTable();
    });

    // Attach the expandAll function to the 'Expand All' link
    $('#expand').click(function () {
      expandAll();
      return false;
    });

    // Attach the collapseAll function to the 'Collapse All' link
    $('#collapse').click(function () {
      collapseAll();
      return false;
    });

    // Attach a handler to the ""select grade view" checkbox which will show or hide the chapter or the assignment grade table
    $('#selectGradeView').change(function () {
      if (this.checked) {
        $('#gradeData').show();
        odsaUtils.logUserAction('odsa-gradebook-show-chapview', '');
        odsaUtils.logUserAction('odsa-gradebook-hide-assgview', '');
        $('#gradeDataA').hide();
      } else {
        $('#gradeData').hide();
        odsaUtils.logUserAction('odsa-gradebook-hide-chapview', '');
        odsaUtils.logUserAction('odsa-gradebook-show-assgview', '');
        $('#gradeDataA').show();
      }
      return false;
    });

    // Attach a handler to the "Show 0-point execises" checkbox which will show or hide exercises that are worth 0-points
    $('#showZeroPointExer').change(function () {
      if (this.checked) {
        $('.zeroPointExer').show(speed);
        odsaUtils.logUserAction('odsa-gradebook-show-zpe', '');
      } else {
        $('.zeroPointExer').hide(speed);
        odsaUtils.logUserAction('odsa-gradebook-hide-zpe', '');
      }
      return false;
    });
  });
}(jQuery));
<|endoftext|>


<commit_before>/**
 * Copyright reelyActive 2014-2019
 * We believe in an open Internet of Things
 */

const http = require('http');
const dgram = require('dgram');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const Barnowl = require('barnowl');
const BarnowlReel = require('barnowl-reel');
const Barnacles = require('barnacles');
const BarnaclesSocketIO = require('barnacles-socketio');
const BarnaclesElasticsearch = require('barnacles-elasticsearch');
const Chickadee = require('chickadee');
const JSONSilo = require('json-silo');
const Raddec = require('raddec');
const StatusManager = require('./statusmanager');
const ElasticManager = require('./elasticmanager');

const PORT = process.env.PORT || 3001;
const REEL_PORT = process.env.REEL_PORT || 50000;
const RADDEC_PORT = process.env.RADDEC_PORT || 50001;
const ELASTICSEARCH_NODE = process.env.ELASTICSEARCH_NODE;

/**
 * HLCServer Class
 * Serves up hyperlocal context (HLC) for the Internet of Things.
 */
class HLCServer {

  /**
   * HLCServer constructor
   * @param {Object} options The configuration options.
   * @constructor
   */
  constructor(options) {
    let self = this;
    options = options || {};

    this.app = express();
    this.server = http.createServer(this.app);
    this.router = express.Router();
    this.app.use(bodyParser.json());
    this.app.use(function(req, res, next) {
      req.hlcserver = self;
      next();
    });
    this.app.use('/status', require('./routes/status'));
    this.app.use('/interfaces/elasticsearch',
                 require('./routes/interfaces/elasticsearch'));
    this.app.use('/', express.static(path.resolve(__dirname + '/../web')));
    this.app.use('/', this.router);

    this.udpServer = dgram.createSocket('udp4');
    this.udpServer.bind(RADDEC_PORT, '0.0.0.0');

    this.elastic = new ElasticManager({ node: ELASTICSEARCH_NODE });
    this.status = new StatusManager();

    this.barnowl = new Barnowl(options.barnowl);
    this.barnowl.addListener(BarnowlReel, {}, BarnowlReel.UdpListener,
                             { path: '0.0.0.0:' + REEL_PORT });

    if(!options.hasOwnProperty('barnacles')) {
      options.barnacles = {};
    }
    options.barnacles.barnowl = this.barnowl;
    this.barnacles = new Barnacles(options.barnacles);
    this.barnacles.addInterface(BarnaclesSocketIO, { server: this.server });
    this.elastic.notifyOnElasticsearchRunning(function(client) {
      self.barnacles.addInterface(BarnaclesElasticsearch, { client: client });
      console.log('reelyActive hlc-server detected Elasticsearch node');
    });

    if(!options.hasOwnProperty('chickadee')) {
      options.chickadee = {};
    }
    options.chickadee.app = this.app;
    this.chickadee = new Chickadee(options.chickadee);

    if(!options.hasOwnProperty('jsonsilo')) {
      options.jsonsilo = {};
    }
    options.jsonsilo.app = this.app;
    this.jsonsilo = new JSONSilo(options.jsonsilo);

    this.udpServer.on('message', function(msg) {
      try {
        let raddec = new Raddec(msg);

        if(raddec !== null) {
          self.barnacles.handleRaddec(raddec);
        }
      }
      catch(error) {};
    });

    this.server.listen(PORT, function() {
      console.log('reelyActive hlc-server is listening on port', PORT);
    });
  }

  /**
   * Add the given hardware listener to barnowl.
   * @param {Class} interfaceClass The (uninstantiated) barnowl-x interface.
   * @param {Object} interfaceOptions The interface options as a JSON object.
   * @param {Class} listenerClass The (uninstantiated) listener class.
   * @param {Object} listenerOptions The listener options as a JSON object.
   */
  addListener(interfaceClass, interfaceOptions, listenerClass,
              listenerOptions) {
    this.barnowl.addListener(interfaceClass, interfaceOptions,
                             listenerClass, listenerOptions);
  }

  /**
   * Add the given interface to barnacles.
   * @param {Class} interfaceClass The (uninstantiated) barnacles-x interface.
   * @param {Object} interfaceOptions The interface options as a JSON object.
   */
  addInterface(interfaceClass, interfaceOptions) {
    this.barnacles.addInterface(interfaceClass, interfaceOptions);
  }

}

module.exports = HLCServer;
<commit_msg>Added CORS<commit_after>/**
 * Copyright reelyActive 2014-2019
 * We believe in an open Internet of Things
 */

const http = require('http');
const dgram = require('dgram');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const Barnowl = require('barnowl');
const BarnowlReel = require('barnowl-reel');
const Barnacles = require('barnacles');
const BarnaclesSocketIO = require('barnacles-socketio');
const BarnaclesElasticsearch = require('barnacles-elasticsearch');
const Chickadee = require('chickadee');
const JSONSilo = require('json-silo');
const Raddec = require('raddec');
const StatusManager = require('./statusmanager');
const ElasticManager = require('./elasticmanager');

const PORT = process.env.PORT || 3001;
const REEL_PORT = process.env.REEL_PORT || 50000;
const RADDEC_PORT = process.env.RADDEC_PORT || 50001;
const ELASTICSEARCH_NODE = process.env.ELASTICSEARCH_NODE;

const DEFAULT_USE_CORS = true;

/**
 * HLCServer Class
 * Serves up hyperlocal context (HLC) for the Internet of Things.
 */
class HLCServer {

  /**
   * HLCServer constructor
   * @param {Object} options The configuration options.
   * @constructor
   */
  constructor(options) {
    let self = this;
    options = options || {};

    if(!options.hasOwnProperty('useCors')) {
      options.useCors = DEFAULT_USE_CORS;
    }

    this.app = express();
    this.server = http.createServer(this.app);
    this.router = express.Router();
    this.app.use(bodyParser.json());
    this.app.use(function(req, res, next) {
      req.hlcserver = self;
      next();
    });
    this.app.use('/status', require('./routes/status'));
    this.app.use('/interfaces/elasticsearch',
                 require('./routes/interfaces/elasticsearch'));
    this.app.use('/', express.static(path.resolve(__dirname + '/../web')));
    this.app.use('/', this.router);

    if(options.useCors) {
      self.app.use(cors());
    }

    this.udpServer = dgram.createSocket('udp4');
    this.udpServer.bind(RADDEC_PORT, '0.0.0.0');

    this.elastic = new ElasticManager({ node: ELASTICSEARCH_NODE });
    this.status = new StatusManager();

    this.barnowl = new Barnowl(options.barnowl);
    this.barnowl.addListener(BarnowlReel, {}, BarnowlReel.UdpListener,
                             { path: '0.0.0.0:' + REEL_PORT });

    if(!options.hasOwnProperty('barnacles')) {
      options.barnacles = {};
    }
    options.barnacles.barnowl = this.barnowl;
    this.barnacles = new Barnacles(options.barnacles);
    this.barnacles.addInterface(BarnaclesSocketIO, { server: this.server });
    this.elastic.notifyOnElasticsearchRunning(function(client) {
      self.barnacles.addInterface(BarnaclesElasticsearch, { client: client });
      console.log('reelyActive hlc-server detected Elasticsearch node');
    });

    if(!options.hasOwnProperty('chickadee')) {
      options.chickadee = {};
    }
    options.chickadee.app = this.app;
    this.chickadee = new Chickadee(options.chickadee);

    if(!options.hasOwnProperty('jsonsilo')) {
      options.jsonsilo = {};
    }
    options.jsonsilo.app = this.app;
    this.jsonsilo = new JSONSilo(options.jsonsilo);

    this.udpServer.on('message', function(msg) {
      try {
        let raddec = new Raddec(msg);

        if(raddec !== null) {
          self.barnacles.handleRaddec(raddec);
        }
      }
      catch(error) {};
    });

    this.server.listen(PORT, function() {
      console.log('reelyActive hlc-server is listening on port', PORT);
    });
  }

  /**
   * Add the given hardware listener to barnowl.
   * @param {Class} interfaceClass The (uninstantiated) barnowl-x interface.
   * @param {Object} interfaceOptions The interface options as a JSON object.
   * @param {Class} listenerClass The (uninstantiated) listener class.
   * @param {Object} listenerOptions The listener options as a JSON object.
   */
  addListener(interfaceClass, interfaceOptions, listenerClass,
              listenerOptions) {
    this.barnowl.addListener(interfaceClass, interfaceOptions,
                             listenerClass, listenerOptions);
  }

  /**
   * Add the given interface to barnacles.
   * @param {Class} interfaceClass The (uninstantiated) barnacles-x interface.
   * @param {Object} interfaceOptions The interface options as a JSON object.
   */
  addInterface(interfaceClass, interfaceOptions) {
    this.barnacles.addInterface(interfaceClass, interfaceOptions);
  }

}

module.exports = HLCServer;
<|endoftext|>


<commit_before>// Implemented by window & worker
declare interface IDBEnvironment {
  indexedDB: IDBFactory;
}

type IDBDirection = 'next' | 'nextunique' | 'prev' | 'prevunique';

// Implemented by window.indexedDB & worker.indexedDB
declare interface IDBFactory {
  open(name: string, version?: number): IDBOpenDBRequest;
  deleteDatabase(name: string): IDBOpenDBRequest;
  cmp(a: any, b: any): -1|0|1;
}

declare interface IDBRequest extends EventTarget {
  result: IDBObjectStore;
  error: Error;
  source: ?(IDBIndex | IDBObjectStore | IDBCursor);
  transaction: IDBTransaction;
  readyState: 'pending'|'done';
  onerror: (err: any) => mixed;
  onsuccess: (e: any) => mixed;
}

declare interface IDBOpenDBRequest extends IDBRequest {
  onblocked: (e: any) => mixed;
  onupgradeneeded: (e: any) => mixed;
}

declare interface IDBDatabase extends EventTarget {
  close(): void;
  createObjectStore(name: string, options?: {
    keyPath?: ?(string|string[]),
    autoIncrement?: bool
  }): IDBObjectStore;
  deleteObjectStore(name: string): void;
  transaction(storeNames: string|string[], mode?: 'readonly'|'readwrite'|'versionchange'): IDBTransaction;
  name: string;
  version: number;
  objectStoreNames: string[];
  onabort: (e: any) => mixed;
  onerror: (e: any) => mixed;
  onversionchange: (e: any) => mixed;
}

declare interface IDBTransaction extends EventTarget {
  abort(): void;
  db: IDBDatabase;
  error: Error;
  mode: 'readonly'|'readwrite'|'versionchange';
  name: string;
  objectStore(name: string): IDBObjectStore;
  onabort: (e: any) => mixed;
  oncomplete: (e: any) => mixed;
  onerror: (e: any) => mixed;
}

declare interface IDBObjectStore {
  add(value: any, key?: any): IDBRequest;
  autoIncrement: bool;
  clear(): IDBRequest;
  createIndex(indexName: string, keyPath: string|string[], optionalParameter?: {
    unique?: bool,
    multiEntry?: bool,
  }): IDBIndex;
  count(keyRange?: any|IDBKeyRange): IDBRequest;
  delete(key: any): IDBRequest;
  deleteIndex(indexName: string): void;
  get(key: any): IDBRequest;
  index(indexName: string): IDBIndex;
  indexNames: string[];
  name: string;
  keyPath: any;
  openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  put(value: any, key?: any): IDBRequest;
  transaction : IDBTransaction;
}

declare interface IDBIndex extends EventTarget {
  count(key?: any|IDBKeyRange): IDBRequest;
  get(key: any|IDBKeyRange): IDBRequest;
  getKey(key: any|IDBKeyRange): IDBRequest;
  openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  name: string;
  objectStore: IDBObjectStore;
  keyPath: any;
  multiEntry: bool;
  unique: bool;
}

declare interface IDBKeyRange {
  bound(lower: any, upper: any, lowerOpen?: bool, upperOpen?: bool): IDBKeyRange;
  only(value: any): IDBKeyRange;
  lowerBound(bound: any, open?: bool): IDBKeyRange;
  upperBound(bound: any, open?: bool): IDBKeyRange;
  lower: any;
  upper: any;
  lowerOpen: bool;
  upperOpen: bool;
}

declare interface IDBCursor {
  advance(count: number): void;
  continue(key?: any): void;
  delete(): IDBRequest;
  update(newValue: any): IDBRequest;
  source: IDBObjectStore|IDBIndex;
  direction: IDBDirection;
  key: any;
  primaryKey: any;
}

declare interface IDBCursorWithValue extends IDBCursor {
  value: any;
}
<commit_msg>[PR] add onclose to IDBDatabase lib def<commit_after>// Implemented by window & worker
declare interface IDBEnvironment {
  indexedDB: IDBFactory;
}

type IDBDirection = 'next' | 'nextunique' | 'prev' | 'prevunique';

// Implemented by window.indexedDB & worker.indexedDB
declare interface IDBFactory {
  open(name: string, version?: number): IDBOpenDBRequest;
  deleteDatabase(name: string): IDBOpenDBRequest;
  cmp(a: any, b: any): -1|0|1;
}

declare interface IDBRequest extends EventTarget {
  result: IDBObjectStore;
  error: Error;
  source: ?(IDBIndex | IDBObjectStore | IDBCursor);
  transaction: IDBTransaction;
  readyState: 'pending'|'done';
  onerror: (err: any) => mixed;
  onsuccess: (e: any) => mixed;
}

declare interface IDBOpenDBRequest extends IDBRequest {
  onblocked: (e: any) => mixed;
  onupgradeneeded: (e: any) => mixed;
}

declare interface IDBDatabase extends EventTarget {
  close(): void;
  createObjectStore(name: string, options?: {
    keyPath?: ?(string|string[]),
    autoIncrement?: bool
  }): IDBObjectStore;
  deleteObjectStore(name: string): void;
  transaction(storeNames: string|string[], mode?: 'readonly'|'readwrite'|'versionchange'): IDBTransaction;
  name: string;
  version: number;
  objectStoreNames: string[];
  onabort: (e: any) => mixed;
  onclose: (e: any) => mixed;
  onerror: (e: any) => mixed;
  onversionchange: (e: any) => mixed;
}

declare interface IDBTransaction extends EventTarget {
  abort(): void;
  db: IDBDatabase;
  error: Error;
  mode: 'readonly'|'readwrite'|'versionchange';
  name: string;
  objectStore(name: string): IDBObjectStore;
  onabort: (e: any) => mixed;
  oncomplete: (e: any) => mixed;
  onerror: (e: any) => mixed;
}

declare interface IDBObjectStore {
  add(value: any, key?: any): IDBRequest;
  autoIncrement: bool;
  clear(): IDBRequest;
  createIndex(indexName: string, keyPath: string|string[], optionalParameter?: {
    unique?: bool,
    multiEntry?: bool,
  }): IDBIndex;
  count(keyRange?: any|IDBKeyRange): IDBRequest;
  delete(key: any): IDBRequest;
  deleteIndex(indexName: string): void;
  get(key: any): IDBRequest;
  index(indexName: string): IDBIndex;
  indexNames: string[];
  name: string;
  keyPath: any;
  openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  put(value: any, key?: any): IDBRequest;
  transaction : IDBTransaction;
}

declare interface IDBIndex extends EventTarget {
  count(key?: any|IDBKeyRange): IDBRequest;
  get(key: any|IDBKeyRange): IDBRequest;
  getKey(key: any|IDBKeyRange): IDBRequest;
  openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
  name: string;
  objectStore: IDBObjectStore;
  keyPath: any;
  multiEntry: bool;
  unique: bool;
}

declare interface IDBKeyRange {
  bound(lower: any, upper: any, lowerOpen?: bool, upperOpen?: bool): IDBKeyRange;
  only(value: any): IDBKeyRange;
  lowerBound(bound: any, open?: bool): IDBKeyRange;
  upperBound(bound: any, open?: bool): IDBKeyRange;
  lower: any;
  upper: any;
  lowerOpen: bool;
  upperOpen: bool;
}

declare interface IDBCursor {
  advance(count: number): void;
  continue(key?: any): void;
  delete(): IDBRequest;
  update(newValue: any): IDBRequest;
  source: IDBObjectStore|IDBIndex;
  direction: IDBDirection;
  key: any;
  primaryKey: any;
}

declare interface IDBCursorWithValue extends IDBCursor {
  value: any;
}
<|endoftext|>


<commit_before>function SelectorGadget() {
  this.border_width = 5;
  this.border_padding = 2;
  this.b_top = null;
  this.b_left = null;
  this.b_right = null;
  this.b_bottom = null;
  this.selected = [];
  this.rejected = [];
  this.special_mode = null;
  this.path_output_field = null;
  this.sg_div = null;
  this.unbound = false;
  this.prediction_helper = new DomPredictionHelper();
  this.restricted_elements = jQuery.map(['html', 'body', 'head', 'base'], function(selector) { return jQuery(selector).get(0) });
}
SelectorGadget.prototype = new Object();

SelectorGadget.prototype.makeBorders = function(elem, makeRed) {
  this.removeBorders();
  this.setupBorders();

  if (elem.parentNode)
    var path_to_show = elem.parentNode.tagName.toLowerCase() + ' ' + elem.tagName.toLowerCase();
  else
  var path_to_show = elem.tagName.toLowerCase();

  var elem = jQuery(elem);
  var p = elem.offset();

  var top = p.top;
  var left = p.left;
  var width = elem.outerWidth()
  var height = elem.outerHeight()

  this.b_top.css('width', this.px(width + this.border_padding * 2 + this.border_width * 2)).
             css('top', this.px(top - this.border_width - this.border_padding)).
             css('left', this.px(left - this.border_padding - this.border_width));
  this.b_bottom.css('width', this.px(width + this.border_padding * 2 + this.border_width * 2 - 5)).
                css('top', this.px(top + height + this.border_padding)).
                css('left', this.px(left - this.border_padding - this.border_width)).text(path_to_show);
  this.b_left.css('height', this.px(height + this.border_padding * 2)).
              css('top', this.px(top - this.border_padding)).
              css('left', this.px(left - this.border_padding - this.border_width));
  this.b_right.css('height', this.px(height + this.border_padding * 2)).
               css('top', this.px(top - this.border_padding)).
               css('left', this.px(left + width + this.border_padding));

  if (makeRed || elem.hasClass("sg_suggested") || elem.hasClass("sg_selected")) {
    this.b_top.addClass('sg_border_red');
    this.b_bottom.addClass('sg_border_red');
    this.b_left.addClass('sg_border_red');
    this.b_right.addClass('sg_border_red');
  } else {
    if (this.b_top.hasClass('sg_border_red')) {
      this.b_top.removeClass('sg_border_red');
      this.b_bottom.removeClass('sg_border_red');
      this.b_left.removeClass('sg_border_red');
      this.b_right.removeClass('sg_border_red');
    }
  }

  this.showBorders();
};

SelectorGadget.prototype.px = function(p) {
  return p + 'px';
};

SelectorGadget.prototype.showBorders = function() {
  this.b_top.show();
  this.b_bottom.show();
  this.b_left.show();
  this.b_right.show();
};

SelectorGadget.prototype.removeBorders = function() {
  if (this.b_top) {
    this.b_top.hide();
    this.b_bottom.hide();
    this.b_left.hide();
    this.b_right.hide();
//    jQuery('.sg_highlighted').removeClass('sg_highlighted');
  }
}

SelectorGadget.prototype.setupBorders = function() {
  if (!this.b_top) {
    var width = this.border_width + 'px';
    this.b_top = jQuery('<div>').addClass('sg_border').css('height', width).hide();
    this.b_bottom = jQuery('<div>').addClass('sg_border').addClass('sg_bottom_border').css('height', this.px(this.border_width + 6)).hide();
    this.b_left = jQuery('<div>').addClass('sg_border').css('width', width).hide();
    this.b_right = jQuery('<div>').addClass('sg_border').css('width', width).hide();

    this.addBorderToDom();
  }
};

SelectorGadget.prototype.addBorderToDom = function() {
  document.body.appendChild(this.b_top.get(0));
  document.body.appendChild(this.b_bottom.get(0));
  document.body.appendChild(this.b_left.get(0));
  document.body.appendChild(this.b_right.get(0));
};

SelectorGadget.prototype.removeBorderFromDom = function() {
  if (this.b_top) {
    this.b_top.remove();
    this.b_bottom.remove();
    this.b_left.remove();
    this.b_right.remove();
  }
};

SelectorGadget.prototype.sg_mouseover = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  if (this == document.body || this == document.body.parentNode) return;
  var self = jQuery(this);
  if (gadget.special_mode == 'd') { // Jump to any the first selected parent of this node.
//    self.addClass('sg_highlighted');
    var parent = gadget.firstSelectedOrSuggestedParent(this);
    if (parent != this)
      gadget.makeBorders(parent, true);
    else
      gadget.makeBorders(this);
  } else {
    if (!jQuery('.sg_selected', this).get(0)) {
//      self.addClass('sg_highlighted');
      gadget.makeBorders(this);
    }
  }
  return false;
};

SelectorGadget.prototype.firstSelectedOrSuggestedParent = function(elem) {
  var orig = elem;
  if (jQuery(elem).hasClass('sg_suggested') || jQuery(elem).hasClass('sg_selected')) return elem
  while (elem.parentNode && (elem = elem.parentNode)) {
    if (jQuery(elem).hasClass('sg_suggested') || jQuery(elem).hasClass('sg_selected')) return elem
  }
  return orig;
};

SelectorGadget.prototype.sg_mouseout = function(e) {
  if (e.data.self.unbound) return true;
  if (this == document.body || this == document.body.parentNode) return;
  e.data.self.removeBorders();
  return false;
};

SelectorGadget.prototype.sg_click = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  var elem = this;
  var w_elem = jQuery(elem);
  if (elem == document.body || elem == document.body.parentNode) return;
  //window.getSelection().removeAllRanges();

  if (gadget.special_mode == 'd') {
    var potential_elem = gadget.firstSelectedOrSuggestedParent(elem);
    if (potential_elem != elem) {
      elem = potential_elem;
    }
  } else {
    if (jQuery('.sg_selected', this).get(0)) return; // Don't allow selection of elements that have a selected child.
  }

  if (w_elem.hasClass('sg_selected')) {
    w_elem.removeClass('sg_selected');
    gadget.selected.splice(jQuery.inArray(elem, gadget.selected), 1);
  } else if (w_elem.hasClass("sg_rejected")) {
    w_elem.removeClass('sg_rejected');
    gadget.rejected.splice(jQuery.inArray(elem, gadget.rejected), 1);
  } else if (w_elem.hasClass("sg_suggested")) {
    w_elem.addClass('sg_rejected');
    gadget.rejected.push(elem);
  } else {
    w_elem.addClass('sg_selected');
    gadget.selected.push(elem);
  }

  gadget.clearSuggested()
  var prediction = gadget.prediction_helper.predictCss(gadget.selected, gadget.rejected.concat(gadget.restricted_elements));
  gadget.suggestPredicted(prediction);
  gadget.setPath(prediction);
  gadget.removeBorders();

  gadget.blockClicksOn(elem);

  return false;
};

SelectorGadget.prototype.setupEventHandlers = function() {
  jQuery("*:not(.sg_ignore)").bind("mouseover.sg", { 'self': this }, this.sg_mouseover);
  jQuery("*:not(.sg_ignore)").bind("mouseout.sg", { 'self': this }, this.sg_mouseout);
  jQuery("*:not(.sg_ignore)").bind("mousedown.sg", { 'self': this }, this.sg_click);
  jQuery("html").bind("keydown.sg", { 'self': this }, this.listen_for_action_keys);
  jQuery("html").bind("keyup.sg", { 'self': this }, this.clear_action_keys);
};

// Why doesn't this work?
// SelectorGadget.prototype.removeEventHandlers = function() {
//   // For some reason the jQuery unbind isn't working for me.
//
//   // jQuery("*").unbind("mouseover.sg");//, this.sg_mouseover);
//   // jQuery("*").unbind("mouseout.sg");//, this.sg_mouseout);
//   // jQuery("*").unbind("click.sg");//, this.sg_click);
//   // jQuery("html").unbind("keydown.sg");//, this.listen_for_action_keys);
//   // jQuery("html").unbind("keyup.sg");//, this.clear_action_keys);
// };

// The only action key right now is shift, which snaps to any div that has been selected.
SelectorGadget.prototype.listen_for_action_keys = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  if (e.keyCode == 16 || e.keyCode == 68) { // shift or d
    gadget.special_mode = 'd';
    gadget.removeBorders();
  }
};

SelectorGadget.prototype.clear_action_keys = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  gadget.removeBorders();
  gadget.special_mode = null;
};

// Block clicks for a moment by covering this element with a div.  Eww?
SelectorGadget.prototype.blockClicksOn = function(elem) {
  var elem = jQuery(elem);
  var p = elem.offset();
  var block = jQuery('<div>').css('position', 'absolute').css('z-index', '9999999').css('width', this.px(elem.outerWidth())).
                              css('height', this.px(elem.outerHeight())).css('top', this.px(p.top)).css('left', this.px(p.left)).
                              css('background-color', '');
  document.body.appendChild(block.get(0));
  setTimeout(function() { block.remove(); },250)
};

SelectorGadget.prototype.setMode = function(mode) {
  if (mode == 'browse') {
    this.removeEventHandlers();
  } else if (mode == 'interactive') {
    this.setupEventHandlers();
  }
  this.clearSelected();
};

SelectorGadget.prototype.suggestPredicted = function(prediction) {
  if (prediction && prediction != '') {
    var count = 0;
    jQuery(prediction).each(function() {
      count += 1;
      if (!jQuery(this).hasClass('sg_selected') && !jQuery(this).hasClass('sg_ignore') && !jQuery(this).hasClass('sg_rejected')) jQuery(this).addClass('sg_suggested');
    });

    if (this.clear_button) {
      if (count > 0) {
        this.clear_button.attr('value', 'Clear (' + count + ')');
      } else {
        this.clear_button.attr('value', 'Clear');
      }
    }
  }
};

SelectorGadget.prototype.setPath = function(prediction) {
  if (prediction && prediction.length > 0)
    this.path_output_field.value = prediction;
  else
    this.path_output_field.value = 'No valid path found.';
};

SelectorGadget.prototype.refreshFromPath = function(e) {
  var self = (e && e.data && e.data.self) || this;
  var path = self.path_output_field.value;
  self.clearSelected();
  self.suggestPredicted(path);
  self.setPath(path);
};

SelectorGadget.prototype.clearSelected = function(e) {
  var self = (e && e.data && e.data.self) || this;
  self.selected = [];
  self.rejected = [];
  jQuery('.sg_selected').removeClass('sg_selected');
  jQuery('.sg_rejected').removeClass('sg_rejected');
  self.removeBorders();
  self.resetOutputs();
};

SelectorGadget.prototype.resetOutputs = function() {
  this.setPath();
  this.clearSuggested();
};

SelectorGadget.prototype.clearSuggested = function() {
  jQuery('.sg_suggested').removeClass('sg_suggested');
  if (this.clear_button) this.clear_button.attr('value', 'Clear');
};

SelectorGadget.prototype.makeInterface = function() {
  this.sg_div = jQuery('<div>').attr('id', '_sg_div').addClass('sg_bottom').addClass('sg_ignore');
  var self = this;
  var path = jQuery('<input>').attr('id', '_sg_path_field').attr('class', 'sg_ignore').keydown(function(e) {
    if (e.keyCode == 13) {
      return self.refreshFromPath(e);
    }
  });
  this.sg_div.append(path);
  this.clear_button = jQuery('<input type="button" value="Clear"/>').bind("click", {'self': this}, this.clearSelected).attr('class', 'sg_ignore');
  this.sg_div.append(this.clear_button);
  this.sg_div.append(jQuery('<input type="button" value="Toggle Position"/>').click(function() {
    if (self.sg_div.hasClass('sg_top')) {
      self.sg_div.removeClass('sg_top').addClass('sg_bottom');
    } else {
      self.sg_div.removeClass('sg_bottom').addClass('sg_top');
    }
  }).attr('class', 'sg_ignore'));
  this.sg_div.append(jQuery('<input type="button" value="X"/>').bind("click", {'self': this}, this.unbind).attr('class', 'sg_ignore'));
  jQuery('body').append(this.sg_div);

  this.path_output_field = path.get(0);
};

SelectorGadget.prototype.removeInterface = function(e) {
  this.sg_div.remove();
  this.sg_div = null;

  this.removeBorderFromDom();
};

SelectorGadget.prototype.unbind = function(e) {
  var self = (e && e.data && e.data.self) || this;
  self.unbound = true;
  self.removeInterface();
  self.clearSelected();
};

SelectorGadget.prototype.rebind = function() {
  this.unbound = false;
  this.makeInterface();
  this.resetOutputs();
  this.setupBorders();
  this.addBorderToDom();
};

// And go!
if (typeof(selector_gadget) == 'undefined' || selector_gadget == null) {
  (function() {
    selector_gadget = new SelectorGadget();
    selector_gadget.makeInterface();
    selector_gadget.resetOutputs();
    selector_gadget.setMode('interactive');
  })();
} else if (selector_gadget.unbound) {
  selector_gadget.rebind();
} else {
  selector_gadget.unbind();
}

jQuery('.selector_gadget_loading').remove();<commit_msg>autoselection of text box, bug fix with flickering text<commit_after>function SelectorGadget() {
  this.border_width = 5;
  this.border_padding = 2;
  this.b_top = null;
  this.b_left = null;
  this.b_right = null;
  this.b_bottom = null;
  this.selected = [];
  this.rejected = [];
  this.special_mode = null;
  this.path_output_field = null;
  this.sg_div = null;
  this.unbound = false;
  this.prediction_helper = new DomPredictionHelper();
  this.restricted_elements = jQuery.map(['html', 'body', 'head', 'base'], function(selector) { return jQuery(selector).get(0) });
}
SelectorGadget.prototype = new Object();

SelectorGadget.prototype.makeBorders = function(elem, makeRed) {
  this.removeBorders();
  this.setupBorders();

  if (elem.parentNode)
    var path_to_show = elem.parentNode.tagName.toLowerCase() + ' ' + elem.tagName.toLowerCase();
  else
  var path_to_show = elem.tagName.toLowerCase();

  var elem = jQuery(elem);
  var p = elem.offset();

  var top = p.top;
  var left = p.left;
  var width = elem.outerWidth()
  var height = elem.outerHeight()

  this.b_top.css('width', this.px(width + this.border_padding * 2 + this.border_width * 2)).
             css('top', this.px(top - this.border_width - this.border_padding)).
             css('left', this.px(left - this.border_padding - this.border_width));
  this.b_bottom.css('width', this.px(width + this.border_padding * 2 + this.border_width * 2 - 5)).
                css('top', this.px(top + height + this.border_padding)).
                css('left', this.px(left - this.border_padding - this.border_width)).text(path_to_show);
  this.b_left.css('height', this.px(height + this.border_padding * 2)).
              css('top', this.px(top - this.border_padding)).
              css('left', this.px(left - this.border_padding - this.border_width));
  this.b_right.css('height', this.px(height + this.border_padding * 2)).
               css('top', this.px(top - this.border_padding)).
               css('left', this.px(left + width + this.border_padding));

  if (makeRed || elem.hasClass("sg_suggested") || elem.hasClass("sg_selected")) {
    this.b_top.addClass('sg_border_red');
    this.b_bottom.addClass('sg_border_red');
    this.b_left.addClass('sg_border_red');
    this.b_right.addClass('sg_border_red');
  } else {
    if (this.b_top.hasClass('sg_border_red')) {
      this.b_top.removeClass('sg_border_red');
      this.b_bottom.removeClass('sg_border_red');
      this.b_left.removeClass('sg_border_red');
      this.b_right.removeClass('sg_border_red');
    }
  }

  this.showBorders();
};

SelectorGadget.prototype.px = function(p) {
  return p + 'px';
};

SelectorGadget.prototype.showBorders = function() {
  this.b_top.show();
  this.b_bottom.show();
  this.b_left.show();
  this.b_right.show();
};

SelectorGadget.prototype.removeBorders = function() {
  if (this.b_top) {
    this.b_top.hide();
    this.b_bottom.hide();
    this.b_left.hide();
    this.b_right.hide();
//    jQuery('.sg_highlighted').removeClass('sg_highlighted');
  }
}

SelectorGadget.prototype.setupBorders = function() {
  if (!this.b_top) {
    var width = this.border_width + 'px';
    this.b_top = jQuery('<div>').addClass('sg_border').css('height', width).hide();
    this.b_bottom = jQuery('<div>').addClass('sg_border').addClass('sg_bottom_border').css('height', this.px(this.border_width + 6)).hide();
    this.b_left = jQuery('<div>').addClass('sg_border').css('width', width).hide();
    this.b_right = jQuery('<div>').addClass('sg_border').css('width', width).hide();

    this.addBorderToDom();
  }
};

SelectorGadget.prototype.addBorderToDom = function() {
  document.body.appendChild(this.b_top.get(0));
  document.body.appendChild(this.b_bottom.get(0));
  document.body.appendChild(this.b_left.get(0));
  document.body.appendChild(this.b_right.get(0));
};

SelectorGadget.prototype.removeBorderFromDom = function() {
  if (this.b_top) {
    this.b_top.remove();
    this.b_bottom.remove();
    this.b_left.remove();
    this.b_right.remove();
  }
};

SelectorGadget.prototype.sg_mouseover = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  if (this == document.body || this == document.body.parentNode) return;
  var self = jQuery(this);
  if (gadget.special_mode == 'd') { // Jump to any the first selected parent of this node.
//    self.addClass('sg_highlighted');
    var parent = gadget.firstSelectedOrSuggestedParent(this);
    if (parent != this)
      gadget.makeBorders(parent, true);
    else
      gadget.makeBorders(this);
  } else {
    if (!jQuery('.sg_selected', this).get(0)) {
//      self.addClass('sg_highlighted');
      gadget.makeBorders(this);
    }
  }
  return false;
};

SelectorGadget.prototype.firstSelectedOrSuggestedParent = function(elem) {
  var orig = elem;
  if (jQuery(elem).hasClass('sg_suggested') || jQuery(elem).hasClass('sg_selected')) return elem
  while (elem.parentNode && (elem = elem.parentNode)) {
    if (jQuery(elem).hasClass('sg_suggested') || jQuery(elem).hasClass('sg_selected')) return elem
  }
  return orig;
};

SelectorGadget.prototype.sg_mouseout = function(e) {
  if (e.data.self.unbound) return true;
  if (this == document.body || this == document.body.parentNode) return;
  e.data.self.removeBorders();
  return false;
};

SelectorGadget.prototype.sg_click = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  var elem = this;
  var w_elem = jQuery(elem);
  if (elem == document.body || elem == document.body.parentNode) return;
  //window.getSelection().removeAllRanges();

  if (gadget.special_mode == 'd') {
    var potential_elem = gadget.firstSelectedOrSuggestedParent(elem);
    if (potential_elem != elem) {
      elem = potential_elem;
    }
  } else {
    if (jQuery('.sg_selected', this).get(0)) return; // Don't allow selection of elements that have a selected child.
  }

  if (w_elem.hasClass('sg_selected')) {
    w_elem.removeClass('sg_selected');
    gadget.selected.splice(jQuery.inArray(elem, gadget.selected), 1);
  } else if (w_elem.hasClass("sg_rejected")) {
    w_elem.removeClass('sg_rejected');
    gadget.rejected.splice(jQuery.inArray(elem, gadget.rejected), 1);
  } else if (w_elem.hasClass("sg_suggested")) {
    w_elem.addClass('sg_rejected');
    gadget.rejected.push(elem);
  } else {
    w_elem.addClass('sg_selected');
    gadget.selected.push(elem);
  }

  gadget.clearSuggested()
  var prediction = gadget.prediction_helper.predictCss(gadget.selected, gadget.rejected.concat(gadget.restricted_elements));
  gadget.suggestPredicted(prediction);
  gadget.setPath(prediction);
  gadget.removeBorders();

  gadget.blockClicksOn(elem);

  return false;
};

SelectorGadget.prototype.setupEventHandlers = function() {
  jQuery("*:not(.sg_ignore)").bind("mouseover.sg", { 'self': this }, this.sg_mouseover);
  jQuery("*:not(.sg_ignore)").bind("mouseout.sg", { 'self': this }, this.sg_mouseout);
  jQuery("*:not(.sg_ignore)").bind("mousedown.sg", { 'self': this }, this.sg_click);
  jQuery("html").bind("keydown.sg", { 'self': this }, this.listen_for_action_keys);
  jQuery("html").bind("keyup.sg", { 'self': this }, this.clear_action_keys);
};

// Why doesn't this work?
// SelectorGadget.prototype.removeEventHandlers = function() {
//   // For some reason the jQuery unbind isn't working for me.
//
//   // jQuery("*").unbind("mouseover.sg");//, this.sg_mouseover);
//   // jQuery("*").unbind("mouseout.sg");//, this.sg_mouseout);
//   // jQuery("*").unbind("click.sg");//, this.sg_click);
//   // jQuery("html").unbind("keydown.sg");//, this.listen_for_action_keys);
//   // jQuery("html").unbind("keyup.sg");//, this.clear_action_keys);
// };

// The only action key right now is shift, which snaps to any div that has been selected.
SelectorGadget.prototype.listen_for_action_keys = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  if (e.keyCode == 16 || e.keyCode == 68) { // shift or d
    gadget.special_mode = 'd';
    gadget.removeBorders();
  }
};

SelectorGadget.prototype.clear_action_keys = function(e) {
  var gadget = e.data.self;
  if (gadget.unbound) return true;
  gadget.removeBorders();
  gadget.special_mode = null;
};

// Block clicks for a moment by covering this element with a div.  Eww?
SelectorGadget.prototype.blockClicksOn = function(elem) {
  var elem = jQuery(elem);
  var p = elem.offset();
  var block = jQuery('<div>').css('position', 'absolute').css('z-index', '9999999').css('width', this.px(elem.outerWidth())).
                              css('height', this.px(elem.outerHeight())).css('top', this.px(p.top)).css('left', this.px(p.left)).
                              css('background-color', '');
  document.body.appendChild(block.get(0));
  setTimeout(function() { block.remove(); },250)
};

SelectorGadget.prototype.setMode = function(mode) {
  if (mode == 'browse') {
    this.removeEventHandlers();
  } else if (mode == 'interactive') {
    this.setupEventHandlers();
  }
  this.clearSelected();
};

SelectorGadget.prototype.suggestPredicted = function(prediction) {
  if (prediction && prediction != '') {
    var count = 0;
    jQuery(prediction).each(function() {
      count += 1;
      if (!jQuery(this).hasClass('sg_selected') && !jQuery(this).hasClass('sg_ignore') && !jQuery(this).hasClass('sg_rejected')) jQuery(this).addClass('sg_suggested');
    });

    if (this.clear_button) {
      if (count > 0) {
        this.clear_button.attr('value', 'Clear (' + count + ')');
      } else {
        this.clear_button.attr('value', 'Clear');
      }
    }
  }
};

SelectorGadget.prototype.setPath = function(prediction) {
  if (prediction && prediction.length > 0)
    this.path_output_field.value = prediction;
  else
    this.path_output_field.value = 'No valid path found.';
};

SelectorGadget.prototype.refreshFromPath = function(e) {
  var self = (e && e.data && e.data.self) || this;
  var path = self.path_output_field.value;
  self.clearSelected();
  self.suggestPredicted(path);
  self.setPath(path);
};

SelectorGadget.prototype.clearSelected = function(e) {
  var self = (e && e.data && e.data.self) || this;
  self.selected = [];
  self.rejected = [];
  jQuery('.sg_selected').removeClass('sg_selected');
  jQuery('.sg_rejected').removeClass('sg_rejected');
  self.removeBorders();
  self.clearSuggested();
};

SelectorGadget.prototype.clearEverything = function(e) {
  var self = (e && e.data && e.data.self) || this;
  self.clearSelected()
  self.resetOutputs()
};

SelectorGadget.prototype.resetOutputs = function() {
  this.setPath();
};

SelectorGadget.prototype.clearSuggested = function() {
  jQuery('.sg_suggested').removeClass('sg_suggested');
  if (this.clear_button) this.clear_button.attr('value', 'Clear');
};

SelectorGadget.prototype.makeInterface = function() {
  this.sg_div = jQuery('<div>').attr('id', '_sg_div').addClass('sg_bottom').addClass('sg_ignore');
  var self = this;
  var path = jQuery('<input>').attr('id', '_sg_path_field').attr('class', 'sg_ignore').keydown(function(e) {
    if (e.keyCode == 13) {
      return self.refreshFromPath(e);
    }
  }).focus(function() { jQuery(this).select(); });
  this.sg_div.append(path);
  this.clear_button = jQuery('<input type="button" value="Clear"/>').bind("click", {'self': this}, this.clearEverything).attr('class', 'sg_ignore');
  this.sg_div.append(this.clear_button);
  this.sg_div.append(jQuery('<input type="button" value="Toggle Position"/>').click(function() {
    if (self.sg_div.hasClass('sg_top')) {
      self.sg_div.removeClass('sg_top').addClass('sg_bottom');
    } else {
      self.sg_div.removeClass('sg_bottom').addClass('sg_top');
    }
  }).attr('class', 'sg_ignore'));
  this.sg_div.append(jQuery('<input type="button" value="X"/>').bind("click", {'self': this}, this.unbind).attr('class', 'sg_ignore'));
  jQuery('body').append(this.sg_div);

  this.path_output_field = path.get(0);
};

SelectorGadget.prototype.removeInterface = function(e) {
  this.sg_div.remove();
  this.sg_div = null;

  this.removeBorderFromDom();
};

SelectorGadget.prototype.unbind = function(e) {
  var self = (e && e.data && e.data.self) || this;
  self.unbound = true;
  self.removeInterface();
  self.clearSelected();
};

SelectorGadget.prototype.rebind = function() {
  this.unbound = false;
  this.makeInterface();
  this.clearEverything();
  this.setupBorders();
  this.addBorderToDom();
};

// And go!
if (typeof(selector_gadget) == 'undefined' || selector_gadget == null) {
  (function() {
    selector_gadget = new SelectorGadget();
    selector_gadget.makeInterface();
    selector_gadget.clearEverything();
    selector_gadget.setMode('interactive');
  })();
} else if (selector_gadget.unbound) {
  selector_gadget.rebind();
} else {
  selector_gadget.unbind();
}

jQuery('.selector_gadget_loading').remove();<|endoftext|>