# Source: liveview.js

    /**
     * @namespace LiveView
     */
    ;(function(window, $, undefined){
    	'use strict';
    	var tmpPrototype;

    	/** @alias LiveView.prototype */
    	window.LiveView = {
    		Table: Table,
    		Query: Query,
    		QueryProperties: QueryProperties,
    		Schema: Schema,
    		Field: Field,
    		Tuple: Tuple,
    		Error: Error,
    		_internal: {}
    	};

    	/**
    	 * Defines the properties of a LiveView Table. Properties are read-only.
    	 * @class Table
    	 * @memberOf LiveView
    	 * @param {String} name The name of the table.
    	 * @param {String} group The group the table belongs to.
    	 * @param {String} shortDescription A short description of the table. Ideal for length-limited fields.
    	 * @param {String} description A full description of the table.
    	 * @param {boolean} isEnabled A flag indicating whether or not the table is enabled.
    	 * @param {String} queryLanguages A CSV string of supported query languages for this table.
    	 * @param {Array} keys Field names of key fields
    	 * @param {LiveView.Schema} schema The Schema that defines the table fields.
    	 */
    	function Table(name, group, shortDescription, description, isEnabled, queryLanguages, keys, schema){
    		if(!(this instanceof Table)){
    			return new Table(name, group, shortDescription, description, isEnabled, queryLanguages, keys, schema);
    		}
    		/**
    		 * The name of the table.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {String} name
    		 */
    		this.name = name;
    		/**
    		 * The group the table belongs to.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {String} group
    		 */
    		this.group = group;
    		/**
    		 * A short description of the table. Ideal for length-limited fields.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {String} shortDescription
    		 */
    		this.shortDescription = shortDescription;
    		/**
    		 * A full description of the table.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {String} description
    		 */
    		this.description = description;
    		/**
    		 * A flag indicating whether or not the table is enabled.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {boolean} isEnabled
    		 */
    		this.isEnabled = isEnabled;
    		/**
    		 * A CSV string of supported query languages for this table.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {String} queryLanguages
    		 */
    		this.queryLanguages = queryLanguages;
    		/**
    		 * An array of string field names identifying the table's key fields.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {Array} keys
    		 */
    		this.keys = keys;
    		/**
    		 * The {@link LiveView.Schema|Schema} for the table.
    		 * @memberOf LiveView.Table
    		 * @instance
    		 * @member {LiveView.Schema} schema
    		 */
    		this.schema = schema;
    	}
    	/** @alias LiveView.Table.prototype */
    	Table.prototype = {
    		constructor: Table
    	};

    	/**
    	 * Object that represents a LiveView query. This object is used as a parameter when calling
    	 * {@link LiveView.Connection.execute|execute} or {@link LiveView.Connection.subscribe|subscribe}. It stores the
    	 * query string (in parameterized format) as well as the parameters to apply when invoking execute or subscribe.
    	 * @class Query
    	 * @memberOf LiveView
    	 * @param {String} query The optionally-parametrized query string
    	 * @param {Object} [parameters] A key-value map of parameters to use to compute the query string, if query is a parametrized string. Users are essentially free to define the key value used to identify a parameter as they wish. The query string is generated using regular expression substitution, so avoidance of regular expression special characters is recommended as it will likely cause unexpected behavior. The '@' character as a parameter prefix works well (e.g. <span style="lvCode">{'@priceMin':100, '@lastUpdated':1415230518223}</span>).
    	 * @param {boolean} [includeInternal] A flag indicating whether or not to include internal fields in (non-aggregate) query results.
    	 */
    	function Query(query, parameters, includeInternal){
    		if(!(this instanceof Query)){
    			return new Query(query, parameters, includeInternal);
    		}
    		/**
    		 * The query string in parametrized form. For example: <span class="lvCode">"Select * From ItemsSales Where lastSoldPrice > @minPrice"</span>
    		 * @memberOf LiveView.Query
    		 * @instance
    		 * @member {String} query
    		 */
    		this.query = query;
    		/**
    		 * A key-value map of parameters to use to compute the query string, if query is a parametrized string. Users are essentially free to define the key value used to identify a parameter as they wish. The query string is generated using regular expression substitution, so avoidance of regular expression special characters is recommended as it will likely cause unexpected behavior. The '@' character as a parameter prefix works well (e.g. <span style="lvCode">{'@priceMin':100, '@lastUpdated':1415230518223}</span>).
    		 * @memberOf LiveView.Query
    		 * @instance
    		 * @member {Object} parameters
    		 */
    		this.parameters = parameters;
    		/**
    		 * A flag indicating whether or not to include internal fields in (non-aggregate) query results.
    		 * @memberOf LiveView.Query
    		 * @instance
    		 * @member {boolean} includeInternal
    		 */
    		this.includeInternal = includeInternal;
    	}
    	/** @alias LiveView.Query.prototype */
    	Query.prototype = {
    		constructor: Query,
    		/**
    		 * Applies parameters to the query and returns the resulting query string. If this is not a parametrized query,
    		 * this will be equivalent to the query field.
    		 * @function
    		 * @memberOf LiveView.Query.prototype
    		 * @returns {String} -- The parameter-applied query string
    		 */
    		getQueryString: function(){
    			var key,
    				escapedKey,
    				queryString = this.query;
    			for(key in this.parameters){
    				if(this.parameters.hasOwnProperty(key)){
    					//we need to escape the key which could contain special characters, we need to treat them as literals
    					escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    					queryString = queryString.replace(new RegExp(escapedKey + '\\b', 'g'), this.parameters[key]);
    				}
    			}
    			return queryString;
    		},
    		/**
    		 * Formats the query string as required by the LiveView server.
    		 * @function
    		 * @memberOf LiveView.Query.prototype
    		 * @returns {String} -- The parameter-applied, server-formatted query string
    		 */
    		serialize: function(){
    			return this.getQueryString.call(this).replace(/^delete\sfrom\s/i ,'SELECT * FROM ').replace(/^delete/i,'SELECT * FROM ');
    		}
    	};

    	/**
    	 * Stores detailed information about a LiveView query.
    	 * @class QueryProperties
    	 * @memberOf LiveView
    	 * @param {Object} [initialValues] An object containing initial values for the QueryProperties object's properties.
    	 * The property names defined in the initialValues object should match those of the corresponding QueryProperties
    	 * member properties.
    	 */
    	function QueryProperties(initialValues){
    		initialValues = initialValues || {};
    		/**
    		 * Flag indicating whether or not the query was configured to include LiveView-internal field values.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {boolean} includeInternal
    		 * @default false
    		 */
    		this.includeInternal = initialValues.includeInternal === true;
    		/**
    		 * Flag indicating whether or not the query is an aggregate query.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {boolean} isAggregate
    		 * @default false
    		 */
    		this.isAggregate = initialValues.isAggregate === true;
    		/**
    		 * If a limit was specified in the query, this is the numerical value of that limit. For example, if the query
    		 * was 'SELECT * FROM ItemsSales LIMIT 30', then the limit value would be 30. If no limit was specified, then
    		 * the value will be -1;
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {Number} limit
    		 * @default -1
    		 */
    		this.limit = isNaN(initialValues.limit) ? -1:initialValues.limit;
    		/**
    		 * An array of objects that define the ORDER BY clause of the query. The order of elements in the array
    		 * determines the priority of ordering (i.e. orderBy[0] is of the highest priority). The elements in the array
    		 * contain two properties: <strong>fieldName</strong> and <strong>direction</strong>. The
    		 * <strong>fieldName</strong> property is the string name that identifies the ORDER BY field. The
    		 * <strong>direction</strong> property indicates is a string that indicates what direction to order the values
    		 * ('ASC' for ascending order and 'DESC' for descending order).
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {Array} orderBy
    		 * @default []
    		 */
    		this.orderBy = initialValues.orderBy || [];
    		/**
    		 * The predicate or set of conditions appearing in the query's WHERE clause.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {String} predicate
    		 * @default null
    		 */
    		this.predicate = initialValues.predicate || null;
    		/**
    		 * If a time-delay modifier was added to the WHEN clause, predicateDelay is the value of the delay in
    		 * milliseconds. For example, if the query was 'SELECT * FROM ItemsSales WHERE price > 100 FOR 1000', then
    		 * predicateDelay would be 1000). If no time-delay modifier was specified, predicateDelay will be -1.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {Number} predicateDelay
    		 * @default 0
    		 */
    		this.predicateDelay = isNaN(initialValues.predicateDelay) ? 0:initialValues.predicateDelay;
    		/**
    		 * The projection of the query (i.e. those fields appearing in the query's FROM clause). Stored in CSV format.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {String} projection
    		 * @default null
    		 */
    		this.projection = initialValues.projection || null;
    		/**
    		 * One of ['SNAPSHOT', 'CONTINUOUS', 'SNAPSHOT_AND_CONTINUOUS', 'DELETE']
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {String} queryType
    		 * @default null
    		 */
    		this.queryType = initialValues.queryType || null;
    		/**
    		 * The {@link LiveView.Schema|Schema} of the parsed query.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {Object} schema
    		 * @default null
    		 */
    		this.schema = initialValues.schema || null;
    		/**
    		 * The name of the table against which the query will be performed.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {String} table
    		 * @default null
    		 */
    		this.table = initialValues.table || null;
    		/**
    		 * If a time-window is specified in the query, this object will contain three properties: <strong>field</strong>,
    		 * <strong>begin</strong>, and <strong>end</strong>. The <strong>field</strong> property is the string name of
    		 * the field specified in the WHEN clause. The <strong>begin</strong> property defines the beginning of the
    		 * time-window. The <strong>end</strong> property defines the end of the time-window. For example, if the query
    		 * is 'SELECT * FROM ItemsSales WHEN transactionTime BETWEEN now()-seconds(30) AND now()', the field would be
    		 * 'transactionTime', the begin would be 'now()-seconds(30)' and the end would be 'now()'.
    		 * @memberOf LiveView.QueryProperties
    		 * @instance
    		 * @member {Object} when
    		 * @default null
    		 */
    		this.when = initialValues.when || null;
    	}
    	//QueryProperties.fromServerQueryModel - used for internal parsing
    	QueryProperties.fromDescribeQueryResult = function(describeQueryResult){
    		var queryProps = buildPropsFromServerModel(describeQueryResult.alert_query_config);
    		queryProps.schema = parseSchemaString(describeQueryResult.schema_xml.schema);
    		return queryProps;

    		function buildPropsFromServerModel(serverQueryModel){
    			serverQueryModel = serverQueryModel || {};
    			return new QueryProperties({
    				includeInternal: serverQueryModel.include_internal || false,
    				isAggregate: serverQueryModel.is_aggregate || false,
    				limit: isNaN(serverQueryModel.limit) ? 0:serverQueryModel.limit,
    				orderBy: (serverQueryModel.order_by && serverQueryModel.order_by.directions instanceof Array) ? serverQueryModel.order_by.directions:[],
    				predicate: serverQueryModel.predicate || '',
    				predicateDelay: isNaN(serverQueryModel.predicate_delay_in_millis) ? 0:serverQueryModel.predicate_delay_in_millis,
    				projection: serverQueryModel.projection || '',
    				queryType: serverQueryModel.query_type || '',
    				table: serverQueryModel.table,
    				when: (!serverQueryModel.time_window ? {}: {
    					field: serverQueryModel.time_window.timstamp_field,
    					begin: serverQueryModel.time_window.window_start_expr,
    					end: serverQueryModel.time_window.window_end_expr
    				})
    			});
    		}

    		function parseSchemaString(schemaStr){
    			var schemaObj, fields = [], i;
    			schemaObj = JSON.parse(schemaStr);
    			for(i = 0; i < schemaObj.fields.length; i++){
    				fields.push(new Field(
    					schemaObj.fields[i].name,
    					schemaObj.fields[i].description || '',
    					schemaObj.fields[i].type.type,
    					schemaObj.fields[i].schema || null
    				));
    			}
    			return new Schema(schemaObj.name || '', fields);
    		}
    	};

    	/**
    	 * A LiveView Schema is used for Queries and Tables to define their respective schemas.
    	 * @class Schema
    	 * @memberOf LiveView
    	 * @param {String} name The name of the schema
    	 * @param {Array} fields Array of {@link LiveView.Field|fields} that compose the schema.
    	 */
    	function Schema(name, fields){
    		var i;
    		if(!(this instanceof Schema)){
    			return new Schema(name, fields);
    		}
    		/**
    		 * Name of the Schema.
    		 * @memberOf LiveView.Schema
    		 * @instance
    		 * @member {String} name
    		 */
    		this.name = name;
    		/**
    		 * The fields that define this schema as an array. The array preserves field order as received from the
    		 * LiveView server.
    		 * @memberOf LiveView.Schema
    		 * @instance
    		 * @member {Array} fields
    		 */
    		this.fields = fields;
    		/**
    		 * The fields that define this schema mapped by field name. The order of fields in the field map may not be
    		 * consistent with the actual schema ordering. The fields array maintains fields in the correct order.
    		 * @memberOf LiveView.Schema
    		 * @instance
    		 * @member {Object<String, LiveView.Field>} fieldsMap
    		 */
    		this.fieldsMap = {};

    		for(i = 0; i < fields.length; i++){
    			this.fieldsMap[fields[i].name] = fields[i];
    		}
    	}
    	/** @alias LiveView.Schema.prototype */
    	Schema.prototype = {
    		constructor: Schema
    	};

    	/**
    	 * Defines the properties of a LiveView Field.
    	 * @class Field
    	 * @memberOf LiveView
    	 * @param {String} name The field name. This is used as the unique identifier for this field.
    	 * @param {String} description A description of the field.
    	 * @param {String} type The type of the field (e.g "string", "int", etc).
    	 * @param {LiveView.Schema} [schema] The schema of this field if its type is "tuple".
    	 */
    	function Field(name, description, type, schema){
    		if(!(this instanceof Field)){
    			return new Field(name, description, type, schema);
    		}
    		/**
    		 * The field name. This is used as the unique identifier for this field.
    		 * @memberOf LiveView.Field
    		 * @instance
    		 * @member {String} name
    		 */
    		this.name = name;
    		/**
    		 * A full length description of the field.
    		 * @memberOf LiveView.Field
    		 * @instance
    		 * @member {String} description
    		 */
    		this.description = description;
    		/**
    		 * The type of the field (e.g "string", "int", etc).
    		 * @memberOf LiveView.Field
    		 * @instance
    		 * @member {String} type
    		 */
    		this.type = type;
    		/**
    		 * The schema of this field if its type is "tuple".
    		 * @memberOf LiveView.Field
    		 * @instance
    		 * @member {LiveView.Schema} schema
    		 */
    		this.schema = schema;
    	}
    	/** @alias LiveView.Field.prototype */
    	Field.prototype = {
    		constructor: Field
    	};

    	/**
    	 *
    	 * @class Tuple
    	 * @memberOf LiveView
    	 * @param {Number} id The tuple's unique ID
    	 * @param {Object} fieldMap Map of fieldName -> fieldValue
    	 */
    	function Tuple(id, fieldMap){
    		if(!(this instanceof Tuple)){
    			return new Tuple(id, fieldMap);
    		}
    		/**
    		 * The tuple's unique ID
    		 * @memberOf LiveView.Tuple
    		 * @instance
    		 * @member {Number} id
    		 */
    		this.id = id;
    		/**
    		 * Map of fieldName -> fieldValue for all fields in the tuple. Note that the order of fields in the map will not
    		 * necessarily correspond to the order of fields in the corresponding schema. One should refer to the
    		 * {@link LiveView.Schema#fields|fields} array of the corresponding schema to obtain the correct ordering of
    		 * fields.
    		 * @memberOf LiveView.Tuple
    		 * @instance
    		 * @member {Object} fieldMap
    		 */
    		this.fieldMap = fieldMap;
    	}
    	Tuple.prototype = {
    		constructor: Tuple
    	};

    	/**
    	 * LiveView error object that will be passed as an argument to error handling callbacks and promise rejection
    	 * handlers. LiveView.Error inherits from window.Error if available.
    	 * @class Error
    	 * @memberOf LiveView
    	 * @param {Number} code The error code that identifies the error.
    	 * @param {String} detail A detailed message about what happened.
    	 * @param {String} message A brief description summarizing the error.
    	 * @constructor
    	 */
    	function Error(code, detail, message){
    		var envErr;
    		if(!(this instanceof Error)){
    			return new Error(code, detail, message);
    		}

    		if(typeof(code) === 'object'){
    			detail = code.detail || '';
    			message = code.message || '';
    			code = code.code || null;
    		}

    		//Add useful error info to our LiveView.Error from the native Error object (some values won't be set)
    		if(typeof(window.Error) === 'function'){
    			envErr = new window.Error(message);
    			/**
    			 * The error stack trace if available in the current browser.
    			 * @memberOf LiveView.Error
    			 * @instance
    			 * @member {String} stack
    			 */
    			this.stack = envErr.stack;
    		}

    		//TODO: Enumerate error codes and update Error creations to use appropriate code
    		/**
    		 * The error code that identifies the error.
    		 * @memberOf LiveView.Error
    		 * @instance
    		 * @member {Number} code
    		 */
    		this.code = code;
    		/**
    		 * A detailed message about what happened.
    		 * @memberOf LiveView.Error
    		 * @instance
    		 * @member {String} detail
    		 */
    		this.detail = detail;

    		/**
    		 * A brief description summarizing the error.
    		 * @memberOf LiveView.Error
    		 * @instance
    		 * @member {String} message
    		 */
    		this.message = message;
    	}

    	tmpPrototype = {
    		constructor: Error,
    		toString: function(){
    			return JSON.stringify(this);
    		}
    	};

    	if(window.Error && typeof(window.Error.prototype) === 'object'){
    		Error.prototype = jQuery.extend(Object.create(window.Error.prototype), tmpPrototype); //so LiveView.Error instance of Error is true
    	}
    	else{
    		Error.prototype = tmpPrototype;
    	}

    }(window, jQuery));