diff --git a/Gruntfile.js b/Gruntfile.js index ec1027d..29655db 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,7 +6,7 @@ module.exports = function(grunt) { pkg: grunt.file.readJSON('package.json'), jsdoc: { dist: { - src: ['lib/*.js', 'lib/drivers/*.js', 'README.md'], + src: ['lib/*.js', 'README.md'], options: { template: 'node_modules/grunt-jsdoc/node_modules/ink-docstrap/template', configure: 'node_modules/grunt-jsdoc/node_modules/ink-docstrap/template/jsdoc.conf.json', @@ -15,7 +15,10 @@ module.exports = function(grunt) { } }, nodeunit: { - all: ['tests/**/*_test.js'] + all: ['tests/**/*_test.js'], + options: { + reporter: 'verbose' + } } }); diff --git a/README.md b/README.md index 61988c3..adbb120 100755 --- a/README.md +++ b/README.md @@ -2,16 +2,22 @@ A node query builder for various SQL databases, based on CodeIgniter's query builder. -### Probable use +### Basic use - var query = require('node-query'); + var nodeQuery = require('node-query'); + + var mysql = ... // Database module connection + + // Three arguments: database type, database connection, database connection library + var query = nodeQuery('mysql', mysql, 'mysql2'); - var res = query - .select('foo') + query.select('foo') .from('bar') .where('x', 3) .orWhere('y', 2) .orderBy('x') .limit(2, 3) - .get(); + .get(function(err, result) { + // Database module result handling + }); diff --git a/docs/adapter.js.html b/docs/adapter.js.html new file mode 100644 index 0000000..2ba1829 --- /dev/null +++ b/docs/adapter.js.html @@ -0,0 +1,189 @@ + + + + + + DocStrap Source: adapter.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: adapter.js

+ +
+
+
'use strict';
+
+/** @module adapter */
+module.exports = {
+
+	/**
+	 * Run the sql query as a prepared statement
+	 *
+	 * @param {String} sql - The sql with placeholders
+	 * @param {Array} params - The values to insert into the query
+	 * @param {Function} callback - Callback to run when a response is recieved
+	 * @return void
+	 */
+	execute: function(sql, params, callback) {
+		throw new Error("Correct adapter not defined for query execution");
+	}
+};
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/adapters_mysql.js.html b/docs/adapters_mysql.js.html new file mode 100644 index 0000000..a177740 --- /dev/null +++ b/docs/adapters_mysql.js.html @@ -0,0 +1,190 @@ + + + + + + DocStrap Source: adapters/mysql.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: adapters/mysql.js

+ +
+
+
/** @module adapters/mysql */
+
+'use strict';
+
+var adapter = require('../lib/adapter.js');
+
+module.exports = function(instance) {
+
+	adapter.execute = function(sql, params, callback) {
+		instance.query(sql, params, callback);
+	};
+
+	return adapter;
+}
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/adapters_mysql2.js.html b/docs/adapters_mysql2.js.html new file mode 100644 index 0000000..b064cd0 --- /dev/null +++ b/docs/adapters_mysql2.js.html @@ -0,0 +1,190 @@ + + + + + + DocStrap Source: adapters/mysql2.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: adapters/mysql2.js

+ +
+
+
/** @module adapters/mysql2 */
+
+'use strict';
+
+var adapter = require('../lib/adapter.js');
+
+module.exports = function(instance) {
+
+	adapter.execute = function(sql, params, callback) {
+		instance.execute(sql, params, callback);
+	};
+
+	return adapter;
+}
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/driver.js.html b/docs/driver.js.html index e4e3df2..e97621b 100644 --- a/docs/driver.js.html +++ b/docs/driver.js.html @@ -1,42 +1,84 @@ + - - JSDoc: Source: driver.js - - - - - - + + DocStrap Source: driver.js + + + + + + +
+ - -
-
-
/** @module driver */
+	
-"use strict"; + +
+ +
+ + + +

Source: driver.js

+ +
+
+
'use strict';
 
 var helpers = require('./helpers');
 
 /**
  * Base Database Driver
  *
- * @type {{identifierChar: string, quoteIdentifiers: quoteIdentifiers}}
+ * @module driver
  */
-module.exports = {
+var d = {
 	identifierChar: '"',
 	tablePrefix: null,
 
@@ -48,8 +90,8 @@ module.exports = {
 	 * @private
 	 */
 	_quote: function(str) {
-		return (helpers.isString(str) && ! (str.startsWith(this.identifierChar) || str.endsWith(this.identifierChar)))
-			? this.identifierChar + str + this.identifierChar
+		return (helpers.isString(str) && ! (str.startsWith(d.identifierChar) || str.endsWith(d.identifierChar)))
+			? d.identifierChar + str + d.identifierChar
 			: str;
 	},
 
@@ -61,12 +103,12 @@ module.exports = {
 	 * @private
 	 */
 	_prefix: function(str) {
-		if (str.startsWith(this.prefix))
+		if (str.startsWith(d.prefix))
 		{
 			return str;
 		}
 
-		return this.prefix + str;
+		return d.prefix + str;
 	},
 
 	/**
@@ -95,7 +137,7 @@ module.exports = {
 	 * @return {String}
 	 */
 	prefixTable: function(table) {
-		if (this.tablePrefix)
+		if (d.tablePrefix)
 		{
 			// Split identifier by period, will split into:
 			// database.schema.table OR
@@ -106,7 +148,7 @@ module.exports = {
 			var segments = idents.length;
 
 			// Add the database prefix
-			idents[segments - 1] = this._prefix(idents[segments - 1]);
+			idents[segments - 1] = d._prefix(idents[segments - 1]);
 
 			table = idents.join('.');
 		}
@@ -121,10 +163,10 @@ module.exports = {
 	 * @return {String}
 	 */
 	quoteTable: function(table) {
-		table = this.prefixTable(table);
+		table = d.prefixTable(table);
 
 		// Quote after prefix
-		return this.quoteIdentifiers(table);
+		return d.quoteIdentifiers(table);
 	},
 
 	/**
@@ -139,40 +181,121 @@ module.exports = {
 		// Recurse for arrays of identifiiers
 		if (Array.isArray(str))
 		{
-			return str.map(this.quoteIdentifiers);
+			return str.map(d.quoteIdentifiers);
 		}
 
 		// Handle commas
 		str = helpers.splitTrim(',', str);
 
 		// Split identifiers by period
-		hiers = str.split('.').map(String.trim).map(this._quote);
+		hiers = str.split('.').map(d._quote);
 		raw = hiers.join('.');
 
 		// TODO: fix functions
 
 		return raw;
 	}
-};
-
-
+}; + +module.exports = d;
+
+
+ + + + + +
+ +
+ + + + +
+ + + + + + + + + + + - - + + -
- - - - - diff --git a/docs/drivers_mysql.js.html b/docs/drivers_mysql.js.html new file mode 100644 index 0000000..7b12565 --- /dev/null +++ b/docs/drivers_mysql.js.html @@ -0,0 +1,203 @@ + + + + + + DocStrap Source: drivers/mysql.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: drivers/mysql.js

+ +
+
+
"use strict";
+
+/**
+ * Driver for MySQL databases
+ *
+ * @returns {driver}
+ */
+module.exports = function() {
+	var driver = require('../driver'),
+		helpers = require('../helpers');
+
+	driver.identifierChar = '`';
+
+	/**
+	 * Override default limit method because mysql likes to be different
+	 */
+	driver.limit = function(sql, limit, offset) {
+		if ( ! helpers.isNumber(offset))
+		{
+			return sql += " LIMIT " + limit;
+		}
+
+		return sql += " LIMIT " + offset + "," + limit;
+	};
+
+	return driver;
+};
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/drivers_pg.js.html b/docs/drivers_pg.js.html new file mode 100644 index 0000000..18f084d --- /dev/null +++ b/docs/drivers_pg.js.html @@ -0,0 +1,188 @@ + + + + + + DocStrap Source: drivers/pg.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: drivers/pg.js

+ +
+
+
"use strict";
+
+/**
+ * Driver for PostgreSQL databases
+ *
+ * @returns {driver}
+ */
+module.exports = function() {
+	var driver = require('../driver');
+
+	return driver;
+};
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/es6-polyfill.js.html b/docs/es6-polyfill.js.html new file mode 100644 index 0000000..e757bce --- /dev/null +++ b/docs/es6-polyfill.js.html @@ -0,0 +1,250 @@ + + + + + + DocStrap Source: es6-polyfill.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: es6-polyfill.js

+ +
+
+
/**
+ * Polyfills for very handy methods that are standardized, but not fully implemented in Javascript engines
+ */
+module.exports = (function() {
+	if (!Array.prototype.fill) {
+		Array.prototype.fill = function(value) {
+			// Steps 1-2.
+			if (this == null) {
+				throw new TypeError('this is null or not defined');
+			}
+
+			var O = Object(this);
+
+			// Steps 3-5.
+			var len = O.length >>> 0;
+
+			// Steps 6-7.
+			var start = arguments[1];
+			var relativeStart = start >> 0;
+
+			// Step 8.
+			var k = relativeStart < 0 ?
+				Math.max(len + relativeStart, 0) :
+				Math.min(relativeStart, len);
+
+			// Steps 9-10.
+			var end = arguments[2];
+			var relativeEnd = end === undefined ?
+				len : end >> 0;
+
+			// Step 11.
+			var final = relativeEnd < 0 ?
+				Math.max(len + relativeEnd, 0) :
+				Math.min(relativeEnd, len);
+
+			// Step 12.
+			while (k < final) {
+				O[k] = value;
+				k++;
+			}
+
+			// Step 13.
+			return O;
+		};
+	}
+
+	if ( !String.prototype.contains ) {
+		String.prototype.contains = function() {
+			return String.prototype.indexOf.apply( this, arguments ) !== -1;
+		};
+	}
+
+	if (!String.prototype.startsWith) {
+		Object.defineProperty(String.prototype, 'startsWith', {
+			enumerable: false,
+			configurable: false,
+			writable: false,
+			value: function (searchString, position) {
+				position = position || 0;
+				return this.lastIndexOf(searchString, position) === position;
+			}
+		});
+	}
+
+	if (!String.prototype.endsWith) {
+		Object.defineProperty(String.prototype, 'endsWith', {
+			value: function (searchString, position) {
+				var subjectString = this.toString();
+				if (position === undefined || position > subjectString.length) {
+					position = subjectString.length;
+				}
+				position -= searchString.length;
+				var lastIndex = subjectString.indexOf(searchString, position);
+				return lastIndex !== -1 && lastIndex === position;
+			}
+		});
+	}
+}());
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/global.html b/docs/global.html index d120339..0050eee 100644 --- a/docs/global.html +++ b/docs/global.html @@ -1,183 +1,445 @@ + - - JSDoc: Global - - - - - - + + DocStrap Global + + + + + + +
+ + +
+ + +
+ +
+ + + +

Global

+
+ +
+

+ +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Methods

+ +
+ +
+

init(drivername, connObject, connLib) → {queryBuilder}

+ + +
+
+ + +
+

Create a query builder object

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
drivername + + +String + + + +

The name of the database type, eg. mysql or pg

connObject + + +Object + + + +

A connection object from the database library you are connecting with

connLib + + +String + + + +

The name of the db connection library you are using, eg. mysql or mysql2

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +queryBuilder + + +
+
+ + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + -
- -
-

- -

- -
- -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - - - - - - - -

Members

- -
- -
-

module:driver :Object

- - -
-
- -
- Base Database Driver -
- - - -
Type:
-
    -
  • - -Object + + -
  • -
- + + - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - -
- -
- - - - - - - -
- -
- - - - - - - - -
- - - - - \ No newline at end of file diff --git a/docs/helpers.js.html b/docs/helpers.js.html index cdc439b..d659812 100644 --- a/docs/helpers.js.html +++ b/docs/helpers.js.html @@ -1,61 +1,90 @@ + - - JSDoc: Source: helpers.js - - - - - - + + DocStrap Source: helpers.js + + + + + + +
+ - -
-
-
/** @module helpers */
+	
+ + +
+ +
+ + + +

Source: helpers.js

+ +
+
+
/** @module helpers */
 
 "use strict";
 
-if (!String.prototype.startsWith) {
-	Object.defineProperty(String.prototype, 'startsWith', {
-		enumerable: false,
-		configurable: false,
-		writable: false,
-		value: function (searchString, position) {
-			position = position || 0;
-			return this.lastIndexOf(searchString, position) === position;
-		}
-	});
-}
+require('./es6-polyfill');
 
-if (!String.prototype.endsWith) {
-	Object.defineProperty(String.prototype, 'endsWith', {
-		value: function (searchString, position) {
-			var subjectString = this.toString();
-			if (position === undefined || position > subjectString.length) {
-				position = subjectString.length;
-			}
-			position -= searchString.length;
-			var lastIndex = subjectString.indexOf(searchString, position);
-			return lastIndex !== -1 && lastIndex === position;
-		}
-	});
-}
-
-module.exports = {
+var h = {
+	/**
+	 * Wrap String.prototype.trim in a way that is easily mappable
+	 *
+	 * @param {String} str - The string to trim
+	 * @return {String} - The trimmed string
+	 */
+	stringTrim: function(str) {
+		return str.trim();
+	},
 	/**
 	 * Split a string by a character, trim the string
 	 * and rejoin the string
@@ -65,45 +94,150 @@ module.exports = {
 	 * @return {String}
 	 */
 	splitTrim: function(char, string) {
-		return string.split(char).map(String.trim).join(char);
+		return string.split(char).map(h.stringTrim).join(char);
 	},
 	/**
-	 * Determine whether an object is a string
+	 * Get the type of the variable passed
+	 *
+	 * @see https://techblog.badoo.com/blog/2013/11/01/type-checking-in-javascript/
+	 * @see http://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/
+	 * @param {mixed} o
+	 * @return {String}
+	 */
+	type: function (o) {
+	    var type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
+
+	    // handle NaN and Infinity
+	    if (type === 'number') {
+	        if (isNaN(o)) {
+	            return 'nan';
+	        }
+	        if (!isFinite(o)) {
+	            return 'infinity';
+	        }
+	    }
+
+	    return type;
+	},
+	/**
+	 * Determine whether an object is scalar
+	 *
 	 * @param {mixed} obj
 	 * @return {bool}
 	 */
-	isString: function(obj) {
-		return (typeof obj === 'string' || obj instanceof String);
-	},
-	/**
-	 * Determine whether an object is numeric
-	 * @param {mixed} obj
-	 * @return {bool}
-	 */
-	isNumber: function(obj) {
-		return ! isNaN(parseFloat(obj)) && isFinite(obj);
+	isScalar: function(obj) {
+		var scalar = ['string', 'number', 'boolean'];
+		return scalar.indexOf(h.type(obj)) !== -1;
 	}
 };
-
-
-
+ +// Define an 'is' method for each type +var types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite']; +types.forEach(function (t) { + h['is' + t] = function (o) { + return h.type(o) === t.toLowerCase(); + }; +}); + +module.exports = h;
+
+
+ + + + + +
+ +
+ + + + +
+ + + + + + + + + + + - - + + -
- - - - - diff --git a/docs/img/glyphicons-halflings-white.png b/docs/img/glyphicons-halflings-white.png new file mode 100644 index 0000000..3bf6484 Binary files /dev/null and b/docs/img/glyphicons-halflings-white.png differ diff --git a/docs/img/glyphicons-halflings.png b/docs/img/glyphicons-halflings.png new file mode 100644 index 0000000..a996999 Binary files /dev/null and b/docs/img/glyphicons-halflings.png differ diff --git a/docs/index.html b/docs/index.html index 3fcd5d8..cad7eaa 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,79 +1,213 @@ + - - JSDoc: Index - - - - - - + + DocStrap Index + + + + + + +
+ - +
+ +
+ +
+ + + + + Index + + + + + + + + + + + + + + + + + + + + + +
+

Node-query

A node query builder for various SQL databases, based on CodeIgniter's query builder.

+

Basic use

var nodeQuery = require('node-query');
 
-    

+var mysql = ... // Database module connection +// Three arguments: database type, database connection, database connection library +var query = nodeQuery('mysql', mysql, 'mysql2'); - - - - - - - - - - - - -
-

Node-query

-

A node query builder for various SQL databases, based on CodeIgniter's query builder.

-

Probable use

-
var query = require('node-query');
-
-var res = query
-    .select('foo')
+query.select('foo')
     .from('bar')
     .where('x', 3)
     .orWhere('y', 2)
     .orderBy('x')
     .limit(2, 3)
-    .get();
-
+ .get(function(err, result) { + // Database module result handling + });
+
+ + + + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + - - - - -
- - - - - \ No newline at end of file diff --git a/docs/module-adapter.html b/docs/module-adapter.html new file mode 100644 index 0000000..f9ace9e --- /dev/null +++ b/docs/module-adapter.html @@ -0,0 +1,437 @@ + + + + + + DocStrap Module: adapter + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: adapter

+
+ +
+

+ adapter +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Methods

+ +
+ +
+

<static> execute(sql, params, callback)

+ + +
+
+ + +
+

Run the sql query as a prepared statement

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
sql + + +String + + + +

The sql with placeholders

params + + +Array + + + +

The values to insert into the query

callback + + +function + + + +

Callback to run when a response is recieved

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

void

+
+ + + + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/module-adapters_mysql.html b/docs/module-adapters_mysql.html new file mode 100644 index 0000000..942f655 --- /dev/null +++ b/docs/module-adapters_mysql.html @@ -0,0 +1,255 @@ + + + + + + DocStrap Module: adapters/mysql + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: adapters/mysql

+
+ +
+

+ adapters/mysql +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/module-adapters_mysql2.html b/docs/module-adapters_mysql2.html new file mode 100644 index 0000000..0571c05 --- /dev/null +++ b/docs/module-adapters_mysql2.html @@ -0,0 +1,255 @@ + + + + + + DocStrap Module: adapters/mysql2 + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: adapters/mysql2

+
+ +
+

+ adapters/mysql2 +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-dev + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/module-driver.html b/docs/module-driver.html index aad217c..52a22e5 100644 --- a/docs/module-driver.html +++ b/docs/module-driver.html @@ -1,989 +1,253 @@ + - - JSDoc: Module: driver - - - - - - + + DocStrap Module: driver + + + + + + +
+ +
-
- -
-

- driver -

- -
- -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - -
- - - - - - - - - - - - - - -

Methods

- -
- -
-

<private, static> _prefix(str) → {String}

- - -
-
- - -
- Sets the table prefix on the passed string -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - +
+ +
+ + + +

Module: driver

+
+ +
+

+ driver +

+ +
+ +
+
+ + + + +

Base Database Driver

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + - + + + - - - - - - - - - - - - - - - - - -
NameTypeDescription
str - - -String - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- - - -
-

<private, static> _quote(str) → {String}

- - -
-
- - -
- Low level function for naive quoting of strings -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
str - - -String - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- - - -
-

<static> limit(sql, limit, offset) → {String}

- - -
-
- - -
- Set the limit clause -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
sql - - -String - - - -
limit - - -Number - - - -
offset - - -Number -| - -null - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- - - -
-

<static> prefixTable(table) → {String}

- - -
-
- - -
- Prefixes a table if it is not already prefixed -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
table - - -String - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- - - -
-

<static> quoteIdentifiers(str) → {String|Array}

- - -
-
- - -
- Use the driver's escape character to quote identifiers -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
str - - -String -| - -Array - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String -| - -Array - - -
-
- - - - -
- - - -
-

<static> quoteTable(table) → {String}

- - -
-
- - -
- Quote database table name, and set prefix -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
table - - -String - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- -
- - - - - -
- -
- - - - -
- - - -
- - - - - \ No newline at end of file diff --git a/docs/module-helpers.html b/docs/module-helpers.html index e97eee0..96cfa44 100644 --- a/docs/module-helpers.html +++ b/docs/module-helpers.html @@ -1,553 +1,251 @@ + - - JSDoc: Module: helpers - - - - - - + + DocStrap Module: helpers + + + + + + +
+ +
-
- -
-

- helpers -

- -
- -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - -
- - - - - - - - - - - - - - -

Methods

- -
- -
-

<static> isNumber(obj) → {bool}

- - -
-
- - -
- Determine whether an object is numeric -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - +
+ +
+ + + +

Module: helpers

+
+ +
+

+ helpers +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + - + + + - - - - - - - - - - - - - - - - - -
NameTypeDescription
obj - - -mixed - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -bool - - -
-
- - - - -
- - - -
-

<static> isString(obj) → {bool}

- - -
-
- - -
- Determine whether an object is a string -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
obj - - -mixed - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -bool - - -
-
- - - - -
- - - -
-

<static> splitTrim(char, string) → {String}

- - -
-
- - -
- Split a string by a character, trim the string -and rejoin the string -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
char - - -String - - - -
string - - -String - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- -
- - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.2.2 on Mon Oct 20 2014 16:34:02 GMT-0400 (EDT) -
- - - \ No newline at end of file diff --git a/docs/module-node-query.html b/docs/module-node-query.html new file mode 100644 index 0000000..52146b6 --- /dev/null +++ b/docs/module-node-query.html @@ -0,0 +1,441 @@ + + + + + + DocStrap Module: node-query + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: node-query

+
+ +
+

+ node-query +

+ +
+ +
+
+ + +
+

require("node-query")(drivername, connObject, connLib) → {queryBuilder}

+ + +
+
+ + +
+

Create a query builder object

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
drivername + + +String + + + +

The name of the database type, eg. mysql or pg

connObject + + +Object + + + +

A connection object from the database library you are connecting with

connLib + + +String + + + +

The name of the db connection library you are using, eg. mysql or mysql2

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +queryBuilder + + +
+
+ + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/module-query-builder.html b/docs/module-query-builder.html index 52385d4..606ac0f 100644 --- a/docs/module-query-builder.html +++ b/docs/module-query-builder.html @@ -1,772 +1,5731 @@ + - - JSDoc: Module: query-builder - - - - - - + + DocStrap Module: query-builder + + + + + + +
+ +
-
- -
-

- query-builder -

- -
- -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - -
- - - - - - - - - - - - -

Members

- -
- -
-

<private, inner> _p

- - -
-
- -
- "Private" methods -
- - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - -
- - - -
-

<private, inner> state :Object

- - -
-
- -
- Variables controlling the sql building -
- - - -
Type:
-
    -
  • - -Object - - -
  • -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - -
- -
- - - -

Methods

- -
- -
-

<static> from(tableName) → {exports}

- - -
-
- - -
- Specify the database table to select from -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - +
+ +
+ + + +

Module: query-builder

+
+ +
+

+ query-builder +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Members

+ +
+ +
+

<private, inner> state

+ + +
+
+ +
+

Variables controlling the sql building

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + +
+ +
+ + + +

Methods

+ +
+ +
+

from(tableName)

+ + +
+
+ + +
+

Specify the database table to select from

+
+ + + + + + + +
Parameters:
+ + +
NameTypeDescription
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tableName + + +String + + + +

The table to use for the current query

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

get(table, limit, offset, callback)

+ + +
+
+ + +
+

Get the results of the compiled query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
table + + +String + + + + + + <optional>
+ + + + + +

The table to select from

limit + + +Number + + + + + + <optional>
+ + + + + +

A limit for the query

offset + + +Number + + + + + + <optional>
+ + + + + +

An offset for the query

callback + + +function + + + + + + + + + +

A callback for receiving the result

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

void

+
+ + + + + + + +
+ + + +
+

getCompiledDelete(table, reset) → {String}

+ + +
+
+ + +
+

Return generated delete query SQL

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
table + + +String + + + + + + + + + + + +

the name of the table to delete from

reset + + +Boolean + + + + + + <optional>
+ + + + + +
+ + true + +

Whether to reset the query builder so another query can be built

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + +
+ + + +
+

getCompiledInsert(table, reset) → {String}

+ + +
+
+ + +
+

Return generated insert query SQL

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
table + + +String + + + + + + + + + + + +

the name of the table to insert into

reset + + +Boolean + + + + + + <optional>
+ + + + + +
+ + true + +

Whether to reset the query builder so another query can be built

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + +
+ + + +
+

getCompiledSelect(table, reset)

+ + +
+
+ + +
+

Return generated select query SQL

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
table + + +String + + + + + + <optional>
+ + + + + +
+ +

the name of the table to retrieve from

reset + + +Boolean + + + + + + <optional>
+ + + + + +
+ + true + +

Whether to reset the query builder so another query can be built

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

String

+
+ + + + + + + +
+ + + +
+

getCompiledUpdate(table, reset) → {String}

+ + +
+
+ + +
+

Return generated update query SQL

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
table + + +String + + + + + + + + + + + +

the name of the table to update

reset + + +Boolean + + + + + + <optional>
+ + + + + +
+ + true + +

Whether to reset the query builder so another query can be built

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +String + + +
+
+ + + + + +
+ + + +
+

<private> getState() → {Object}

+ + +
+
+ + +
+

Returns the current class state for testing or other purposes

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + +
+ + + +
+

groupBy(field)

+ + +
+
+ + +
+

Group the results by the selected field(s)

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
field + + +String +| + +Array + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

groupEnd()

+ + +
+
+ + +
+

Ends a logical grouping started with one of the groupStart methods

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

groupStart()

+ + +
+
+ + +
+

Adds an open paren to the current query for logical grouping

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

having(key, val)

+ + +
+
+ + +
+

Add a 'having' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
key + + +String +| + +Object + + + + + + + + + +

The name of the field and the comparision operator, or an object

val + + +String +| + +Number + + + + + + <optional>
+ + + + + +

The value to compare if the value of key is a string

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

insert(table, data, callback)

+ + +
+
+ + +
+

Run the generated insert query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
table + + +String + + + + + + + + + +

The table to insert into

data + + +Object + + + + + + <optional>
+ + + + + +

Data to insert, if not already added with the 'set' method

callback + + +function + + + + + + + + + +

Callback for handling response from the database

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

void

+
+ + + + + + + +
+ + + +
+

join(joinOn, cond, joinTo, type)

+ + +
+
+ + +
+

Add a join clause to the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
joinOn + + +String + + + + + + + + + + + +

The table you are joining

cond + + +String + + + + + + <optional>
+ + + + + +
+ + '=' + +

The join condition, eg. =,<,>,<>,!=,etc.

joinTo + + +String + + + + + + + + + + + +

The value of the condition you are joining on, whether another table's field, or a literal value

type + + +String + + + + + + <optional>
+ + + + + +
+ + 'inner' + +

The type of join, which defaults to inner

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

like(field, val, pos)

+ + +
+
+ + +
+

Add a 'like/ and like' clause to the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
field + + +String + + + + + + + + + + + +

The name of the field to compare to

val + + +String + + + + + + + + + + + +

The value to compare to

pos + + +String + + + + + + <optional>
+ + + + + +
+ + both + +

The placement of the wildcard character(s): before, after, or both

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

limit(limit, offset)

+ + +
+
+ + +
+

Put a limit on the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
limit + + +Number + + + + + + + + + +

The maximum number of rows to fetch

offset + + +Number + + + + + + <optional>
+ + + + + +

The row number to start from

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

notLike(field, val, pos)

+ + +
+
+ + +
+

Add a 'not like/ and not like' clause to the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
field + + +String + + + + + + + + + + + +

The name of the field to compare to

val + + +String + + + + + + + + + + + +

The value to compare to

pos + + +String + + + + + + <optional>
+ + + + + +
+ + both + +

The placement of the wildcard character(s): before, after, or both

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orderBy(field, type)

+ + +
+
+ + +
+

Order the results by the selected field(s)

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
field + + +String + + + + + + + + + + + +

The field to order by

type + + +String + + + + + + <optional>
+ + + + + +
+ + 'ASC' + +

The order direction, ASC or DESC

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orGroupStart()

+ + +
+
+ + +
+

Adds an open paren to the current query for logical grouping, +prefixed with 'OR'

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orHaving(key, val)

+ + +
+
+ + +
+

Add an 'or having' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
key + + +String +| + +Object + + + + + + + + + +

The name of the field and the comparision operator, or an object

val + + +String +| + +Number + + + + + + <optional>
+ + + + + +

The value to compare if the value of key is a string

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orLike(field, val, pos)

+ + +
+
+ + +
+

Add an 'or like' clause to the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
field + + +String + + + + + + + + + + + +

The name of the field to compare to

val + + +String + + + + + + + + + + + +

The value to compare to

pos + + +String + + + + + + <optional>
+ + + + + +
+ + both + +

The placement of the wildcard character(s): before, after, or both

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orNotGroupStart()

+ + +
+
+ + +
+

Adds an open paren to the current query for logical grouping, +prefixed with 'OR NOT'

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orNotLike(field, val, pos)

+ + +
+
+ + +
+

Add an 'or not like' clause to the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
field + + +String + + + + + + + + + + + +

The name of the field to compare to

val + + +String + + + + + + + + + + + +

The value to compare to

pos + + +String + + + + + + <optional>
+ + + + + +
+ + both + +

The placement of the wildcard character(s): before, after, or both

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orWhere(key, val)

+ + +
+
+ + +
+

Set a 'or where' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
key + + +String +| + +Object + + + + + + + + + +

The name of the field and the comparision operator, or an object

val + + +String +| + +Number + + + + + + <optional>
+ + + + + +

The value to compare if the value of key is a string

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orWhereIn(key, val)

+ + +
+
+ + +
+

Set a 'or where in' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +String + + + +

the field to search

val + + +Array + + + +

the array of items to search in

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

orWhereNotIn(key, val)

+ + +
+
+ + +
+

Set a 'or where not in' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +String + + + +

the field to search

val + + +Array + + + +

the array of items to search in

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

select(fields)

+ + +
+
+ + +
+

Specify rows to select in the query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fields + + +String +| + +Array + + + +

The fields to select from the current table

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

set(key, val)

+ + +
+
+ + +
+

Set values for insertion or updating

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
key + + +String +| + +Object + + + + + + + + + +

The key or object to use

val + + +String + + + + + + <optional>
+ + + + + +

The value if using a scalar key

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

update(table, data, callback)

+ + +
+
+ + +
+

Run the generated update query

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
table + + +String + + + + + + + + + +

The table to insert into

data + + +Object + + + + + + <optional>
+ + + + + +

Data to insert, if not already added with the 'set' method

callback + + +function + + + + + + + + + +

Callback for handling response from the database

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

void

+
+ + + + + + + +
+ + + +
+

where(key, val)

+ + +
+
+ + +
+

Set a 'where' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
key + + +String +| + +Object + + + + + + + + + +

The name of the field and the comparision operator, or an object

val + + +String +| + +Number + + + + + + <optional>
+ + + + + +

The value to compare if the value of key is a string

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

whereIn(key, val)

+ + +
+
+ + +
+

Set a 'where in' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +String + + + +

the field to search

val + + +Array + + + +

the array of items to search in

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ + + +
+

whereNotIn(key, val)

+ + +
+
+ + +
+

Set a 'where not in' clause

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +String + + + +

the field to search

val + + +Array + + + +

the array of items to search in

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+

this

+
+ + + + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + - + + + - - - - tableName - - - - - -String - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -exports - - -
-
- - - - - - - - -
-

<static> getCompiledSelect() → {String}

- - -
-
- - -
- Return generated select query SQL -
- - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -String - - -
-
- - - - -
- - - -
-

<static> getState() → {Object}

- - -
-
- - -
- Returns the current class state for testing or other purposes -
- - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -Object - - -
-
- - - - -
- - - -
-

<static> resetQuery()

- - -
-
- - -
- Reset the object state for a new query -
- - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - -
- void -
- - - - - - -
- - - -
-

<static> select(fields) → {exports}

- - -
-
- - -
- Specify rows to select in the query -
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
fields - - -String -| - -Array - - - -
- - - -
- - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -exports - - -
-
- - - - -
- - - - - - - - - - - - - - - - - - -
- - - - - \ No newline at end of file diff --git a/docs/modules.list.html b/docs/modules.list.html new file mode 100644 index 0000000..26212b8 --- /dev/null +++ b/docs/modules.list.html @@ -0,0 +1,241 @@ + + + + + + DocStrap Modules + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Modules

+
+ +
+

+ +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/mysql.html b/docs/mysql.html new file mode 100644 index 0000000..e2a46ef --- /dev/null +++ b/docs/mysql.html @@ -0,0 +1,261 @@ + + + + + + DocStrap Module: drivers/mysql + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: drivers/mysql

+
+ +
+

+ drivers/mysql +

+ +
+ +
+
+ + + + +

Driver for MySQL databases

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/mysql.js.html b/docs/mysql.js.html index b4c6490..873e73e 100644 --- a/docs/mysql.js.html +++ b/docs/mysql.js.html @@ -1,38 +1,91 @@ + - - JSDoc: Source: drivers/mysql.js - - - - - - + + DocStrap Source: drivers/mysql.js + + + + + + +
+ - -
-
-
"use strict";
+	
+ + +
+ +
+ + + +

Source: drivers/mysql.js

+ +
+
+
"use strict";
 
 /**
  * Driver for MySQL databases
  *
- * @returns {driver}
+ * @module drivers/mysql
  */
-module.exports = function() {
+module.exports = (function() {
+
 	var driver = require('../driver'),
 		helpers = require('../helpers');
 
@@ -51,26 +104,106 @@ module.exports = function() {
 	};
 
 	return driver;
-};
-
-
+ +}());
+
+
+ + + + + +
+ +
+ + + + +
+ + + + + + + + + + + - - + + -
- - - - - diff --git a/docs/mysql.js_.html b/docs/mysql.js_.html new file mode 100644 index 0000000..9868cfc --- /dev/null +++ b/docs/mysql.js_.html @@ -0,0 +1,193 @@ + + + + + + DocStrap Source: adapters/mysql.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: adapters/mysql.js

+ +
+
+
'use strict';
+
+var adapter = require('../lib/adapter.js');
+
+/** @module adapters/mysql */
+module.exports = function(instance) {
+
+	adapter.execute = function(sql, params, callback) {
+		instance.query(sql, params, callback);
+	};
+
+	return adapter;
+}
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/mysql2.html b/docs/mysql2.html new file mode 100644 index 0000000..8dc4d7d --- /dev/null +++ b/docs/mysql2.html @@ -0,0 +1,259 @@ + + + + + + DocStrap Module: adapters/mysql2 + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: adapters/mysql2

+
+ +
+

+ adapters/mysql2 +

+ +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/mysql2.js.html b/docs/mysql2.js.html new file mode 100644 index 0000000..6484c2b --- /dev/null +++ b/docs/mysql2.js.html @@ -0,0 +1,201 @@ + + + + + + DocStrap Source: adapters/mysql2.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: adapters/mysql2.js

+ +
+
+
'use strict';
+
+var adapter = require('../lib/adapter.js');
+
+/** @module adapters/mysql2 */
+module.exports = function(instance) {
+
+	/**
+	 * Run the sql query as a prepared statement
+	 *
+	 * @param {String} sql - The sql with placeholders
+	 * @param {Array} params - The values to insert into the query
+	 * @param {Function} callback - Callback to run when a response is recieved
+	 * @return void
+	 */
+	adapter.execute = function(sql, params, callback) {
+		instance.execute(sql, params, callback);
+	};
+
+	return adapter;
+}
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Tue Oct 21st 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/node-query.js.html b/docs/node-query.js.html new file mode 100644 index 0000000..636017c --- /dev/null +++ b/docs/node-query.js.html @@ -0,0 +1,208 @@ + + + + + + DocStrap Source: node-query.js + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Source: node-query.js

+ +
+
+
"use strict";
+
+/** @module node-query */
+var nodeQuery = {};
+
+/**
+ * Create a query builder object
+ *
+ * @alias module:node-query
+ * @param {String} drivername - The name of the database type, eg. mysql or pg
+ * @param {Object} connObject - A connection object from the database library you are connecting with
+ * @param {String} connLib - The name of the db connection library you are using, eg. mysql or mysql2
+ * @return {queryBuilder}
+ */
+nodeQuery.init = function (driverType, connObject, connLib) {
+	var fs = require('fs'),
+		qb = require('./query-builder');
+
+	var paths = {
+		driver: __dirname + '/drivers/' + driverType + '.js',
+		adapter: __dirname + '/adapters/' + connLib + '.js'
+	};
+
+	Object.keys(paths).forEach(function(type) {
+		if ( ! fs.existsSync(paths[type]))
+		{
+			console.log(paths[type]);
+			throw new Error('Selected ' + type + ' does not exist!');
+		}
+	});
+
+	return new qb(require(paths.driver), require(paths.adapter)(connObject));
+};
+
+
+module.exports = nodeQuery.init;
+
+
+ + + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/docs/pg.html b/docs/pg.html new file mode 100644 index 0000000..5df9b6d --- /dev/null +++ b/docs/pg.html @@ -0,0 +1,261 @@ + + + + + + DocStrap Module: drivers/pg + + + + + + + + + +
+ + +
+ + +
+ +
+ + + +

Module: drivers/pg

+
+ +
+

+ drivers/pg +

+ +
+ +
+
+ + + + +

Driver for PostgreSQL databases

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+
+ + + + DocStrap Copyright © 2012-2014 The contributors to the JSDoc3 and DocStrap projects. + +
+ + + Documentation generated by JSDoc 3.3.0-alpha9 + on Thu Oct 23rd 2014 using the DocStrap template. + +
+
+ + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/pg.js.html b/docs/pg.js.html index fa7a074..8e42118 100644 --- a/docs/pg.js.html +++ b/docs/pg.js.html @@ -1,61 +1,192 @@ + - - JSDoc: Source: drivers/pg.js - - - - - - + + DocStrap Source: drivers/pg.js + + + + + + +
+ - -
-
-
"use strict";
+	
+ + +
+ +
+ + + +

Source: drivers/pg.js

+ +
+
+
"use strict";
 
 /**
  * Driver for PostgreSQL databases
  *
- * @returns {driver}
+ * @module drivers/pg
  */
-module.exports = function() {
+module.exports = (function() {
 	var driver = require('../driver');
 
 	return driver;
-};
-
-
+}());
+
+
+ + + + + +
+ +
+ + + + +
+ + + + + + + + + + + - - + + -
- - - - - diff --git a/docs/query-builder.js.html b/docs/query-builder.js.html index 5d6bf43..aeacb93 100644 --- a/docs/query-builder.js.html +++ b/docs/query-builder.js.html @@ -1,47 +1,103 @@ + - - JSDoc: Source: query-builder.js - - - - - - + + DocStrap Source: query-builder.js + + + + + + +
+ - -
-
-
/** @module query-builder */
+	
-var async = require('async'); + +
+ +
+ + + +

Source: query-builder.js

+ +
+
+
'use strict';
 
-module.exports = function(driver) {
+/** @module query-builder */
 
-	"use strict";
+var getArgs = require('getargs'),
+	helpers = require('./helpers');
 
-	/**
-	 * Variables controlling the sql building
-	 *
-	 * @private
-	 * @type {{}}
-	 */
-	var state = {};
+/**
+ * Variables controlling the sql building
+ *
+ * @private
+ */
+var state = {};
 
-	// ------------------------------------------------------------------------
+/*
+ * SQL generation object
+ *
+ * @param {driver} - The syntax driver for the database
+ * @param {adapter} - The database module adapter for running queries
+ * @constructor
+ */
+var QueryBuilder = function(driver, adapter) {
+
+	// That 'new' keyword is annoying
+	if ( ! (this instanceof QueryBuilder)) return new QueryBuilder(driver, adapter);
+
+	// Keep these properties as object members so they can be mocked/substituted
+	this.driver = driver;
+	this.adapter = adapter;
 
 	/**
 	 * "Private" methods
@@ -54,28 +110,300 @@ module.exports = function(driver) {
 		 *
 		 * @param {String} type
 		 * @param {String} table
+		 * @private
 		 * @return {String}
 		 */
 		compile: function (type, table) {
+			// Put together the basic query
+			var sql = _p.compileType(type, table);
+
+			// Set each subClause
+			['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(function(clause) {
+				var param = state[clause];
+
+				if ( ! helpers.isScalar(param))
+				{
+					Object.keys(param).forEach(function(part) {
+						sql += param[part].conjunction + param[part].string;
+					});
+				}
+				else
+				{
+					sql += param;
+				}
+			});
+
+			// Append the limit, if it exists
+			if (helpers.isNumber(state.limit))
+			{
+				sql = this.driver.limit(sql, state.limit, state.offset);
+			}
+
+			return sql;
+		},
+		compileType: function (type, table) {
+			var sql = '';
+
 			switch(type) {
 				case "insert":
+					var paramCount = state.setArrayKeys.length;
+					var params = [];
+					params.fill('?', 0, paramCount);
+
+					sql = "INSERT INTO " + table + " (";
+					sql += state.setArrayKeys.join(',');
+					sql += ") VALUES (";
+					sql += params.join(',') + ')';
 				break;
 
 				case "update":
+					sql = "UPDATE " + table + " SET " + state.setString;
 				break;
 
 				case "delete":
+					sql = "DELETE FROM " + table;
 				break;
 
 				default:
+					sql = "SELECT * FROM " + state.fromString;
+
+					// Set the select string
+					if (state.selectString.length > 0)
+					{
+						// Replace the star with the selected fields
+						sql = sql.replace('*', state.selectString);
+					}
 				break;
 			}
-		},
-		compileType: function (type, table) {
 
+			return sql;
+		},
+		like: function (field, val, pos, like, conj) {
+			field = this.driver.quoteIdentifiers(field);
+
+			like = field + " " + like + " ?";
+
+			if (pos == 'before')
+			{
+				val = "%" + val;
+			}
+			else if (pos == 'after')
+			{
+				val = val + "%";
+			}
+			else
+			{
+				val = "%" + val + "%";
+			}
+
+			conj = (state.queryMap.length < 1) ? ' WHERE ' : ' ' + conj + '';
+			_p.appendMap(conj, like, 'like');
+
+			state.whereValues.push(val);
+		},
+		/**
+		 * Append a clause to the query map
+		 *
+		 * @param {String} conjunction
+		 * @param {String} string
+		 * @param {String} type
+		 * @return void
+		 */
+		appendMap: function(conjunction, string, type) {
+			state.queryMap.push({
+				type: type,
+				conjunction: conjunction,
+				string: string
+			});
+		},
+		/**
+		 * Handle key/value pairs in an object the same way as individual arguments,
+		 * when appending to state
+		 *
+		 * @private
+		 */
+		mixedSet: function(/* varName, key, [val], valType */) {
+			var args = getArgs('varName:string, key:string|array, [val]:string, valType:string', arguments);
+
+			var obj = {};
+
+			if (helpers.isString(args.key) && helpers.isString(args.val))
+			{
+				obj[args.key] = args.val;
+			}
+			else
+			{
+				obj = args.key;
+			}
+
+			Object.keys(obj).forEach(function(k) {
+				if (args.valType != 'both')
+				{
+					state[args.varName].push(
+						(args.valType === 'key')? k: obj[k]
+					);
+				}
+				else
+				{
+					state[args.varName][k] = obj[k];
+				}
+			});
+
+			return state[args.varName];
+		},
+		whereMixedSet: function(key, val) {
+			_p.mixedSet('whereValues', key, val, 'value');
+			_p.mixedSet('whereMap', key, val, 'both');
+		},
+		where: function(key, val, conj) {
+			conj = conj || 'AND';
+
+			// Get an object to iterate over
+			_p.whereMixedSet(key, val);
+
+			Object.keys(state.whereMap).forEach(function(field) {
+				// Split each key by spaces, in case there
+				// is an operator such as >, <, !=, etc.
+				var fieldArray = field.split(' ');
+
+				var item = this.driver.quoteIdentifiers(fieldArray[0]);
+
+				// Simple key value, or an operator?
+				item += (fieldArray.length === 1) ? '=?' : " " + fieldArray[1] + " ?";
+
+				var firstItem = state.queryMap[0],
+					lastItem = state.queryMap[state.queryMap.length - 1];
+
+				// Determine the correct conjunction
+				if (state.queryMap.length < 1 || firstItem.contains('JOIN'))
+				{
+					conj = " WHERE ";
+				}
+				else if (lastItem.type === 'groupStart')
+				{
+					conj = '';
+				}
+				else
+				{
+					conj = ' ' + conj + ' ';
+				}
+
+				_p.appendMap(conj, item, 'where');
+			});
+		},
+		having: function(key, val, conj) {
+			conj = conj || 'AND';
+
+			_p.whereMixedSet(key, val);
+
+			Object.keys(state.whereMap).forEach(function(field) {
+				// Split each key by spaces, in case there
+				// is an operator such as >, <, !=, etc.
+				var fieldArray = field.split(' ').map(helpers.stringTrim);
+
+				var item = this.driver.quoteIdentifiers(fieldArray[0]);
+
+				// Simple key value, or an operator?
+				item += (fieldArray.length === 1) ? '=?' : " " + fieldArray[1] + " ?";
+
+				// Put in the having map
+				state.havingMap.push({
+					conjunction: (state.havingMap.length > 0) ? " " + conj + " " : ' HAVING ',
+					string: item
+				});
+			});
+		},
+		whereIn: function(key, val, inClause, conj) {
+			key = this.driver.quoteIdentifiers(key);
+			var params = [];
+			params.fill('?', 0, val.length);
+
+			val.forEach(function(value) {
+				state.whereValues.push(value);
+			});
+
+			conj = (state.queryMap.length > 0) ? " " + conj + " " : ' WHERE ';
+			var str = key + " " + inClause + " (" + params.join(',') + ") ";
+
+			_p.appendMap(conj, str, 'whereIn');
+		},
+		run: function(type, table, callback, sql, vals) {
+			if ( ! sql)
+			{
+				sql = _p.compile(type, table);
+			}
+
+			if ( ! vals)
+			{
+				vals = state.values.concat(state.whereValues);
+			}
+
+			// Reset the state so another query can be built
+			_p.resetState();
+
+			// Pass the sql and values to the adapter to run on the database
+			adapter.execute(sql, vals, callback);
+
+		},
+		getCompile: function(type, table, reset) {
+			reset = reset || false;
+
+			var sql = _p.compile(type, table);
+
+			if (reset) _p.resetState();
+
+			return sql;
+		},
+		resetState: function() {
+			state = {
+				// Arrays/Maps
+				queryMap: {},
+				values: [],
+				whereValues: [],
+				setArrayKeys: [],
+				orderArray: [],
+				groupArray: [],
+				havingMap: [],
+				whereMap: [],
+
+				// Partials
+				selectString: '',
+				fromString: '',
+				setString: '',
+				orderString: '',
+				groupString: '',
+
+				// Other various values
+				limit: null,
+				offset: null
+			};
 		}
 	};
 
+	// ----------------------------------------------------------------------------
+	// ! Miscellaneous Methods
+	// ----------------------------------------------------------------------------
+
+	/**
+	 * Reset the object state for a new query
+	 *
+	 * @memberOf query-builder
+	 * @return void
+	 */
+	this.resetQuery = function() {
+		_p.resetState();
+	};
+
+	/**
+	 * Returns the current class state for testing or other purposes
+	 *
+	 * @private
+	 * @return {Object}
+	 */
+	this.getState = function() {
+		return state;
+	};
+
 	// ------------------------------------------------------------------------
 
 	// Set up state object
@@ -88,23 +416,23 @@ module.exports = function(driver) {
 	/**
 	 * Specify rows to select in the query
 	 *
-	 * @param {String|Array} fields
-	 * @returns {exports}
+	 * @param {String|Array} fields - The fields to select from the current table
+	 * @return this
 	 */
 	this.select = function(fields) {
 
 		// Split/trim fields by comma
-		fields = (Array.isArray(fields)) ? fields : fields.split(",").map(String.trim);
+		fields = (Array.isArray(fields)) ? fields : fields.split(",").map(helpers.stringTrim);
 
 		// Split on 'As'
 		fields.forEach(function (field, index) {
 			if (field.match(/as/i))
 			{
-				fields[index] = field.split(/ as /i).map(String.trim);
+				fields[index] = field.split(/ as /i).map(helpers.stringTrim);
 			}
 		});
 
-		var safeArray = driver.quoteIdentifiers(fields);
+		var safeArray = this.driver.quoteIdentifiers(fields);
 
 		// Join the strings back together
 		safeArray.forEach(function (field, index) {
@@ -122,16 +450,16 @@ module.exports = function(driver) {
 	/**
 	 * Specify the database table to select from
 	 *
-	 * @param {String} tableName
-	 * @returns {exports}
+	 * @param {String} tableName - The table to use for the current query
+	 * @return this
 	 */
 	this.from = function(tableName) {
 		// Split identifiers on spaces
-		var identArray = String.trim(tableName).split(' ').map(String.trim);
+		var identArray = tableName.trim().split(' ').map(helpers.stringTrim);
 
 		// Quote/prefix identifiers
-		identArray[0] = driver.quoteTable(identArray[0]);
-		identArray = driver.quoteIdentifiers(identArray);
+		identArray[0] = this.driver.quoteTable(identArray[0]);
+		identArray = this.driver.quoteIdentifiers(identArray);
 
 		// Put it back together
 		state.fromString = identArray.join(' ');
@@ -139,118 +467,378 @@ module.exports = function(driver) {
 		return this;
 	};
 
+	/**
+	 * Add a 'like/ and like' clause to the query
+	 *
+	 * @param {String} field - The name of the field  to compare to
+	 * @param {String} val - The value to compare to
+	 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
+	 * @return this
+	 */
 	this.like = function(field, val, pos) {
-
+		_p.like(field, val, pos, ' LIKE ', 'AND');
+		return this;
 	};
 
+	/**
+	 * Add a 'not like/ and not like' clause to the query
+	 *
+	 * @param {String} field - The name of the field  to compare to
+	 * @param {String} val - The value to compare to
+	 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
+	 * @return this
+	 */
+	this.notLike = function(field, val, pos) {
+		_p.like(field, val, pos, ' NOT LIKE ', 'AND');
+		return this;
+	};
+
+	/**
+	 * Add an 'or like' clause to the query
+	 *
+	 * @param {String} field - The name of the field  to compare to
+	 * @param {String} val - The value to compare to
+	 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
+	 * @return this
+	 */
 	this.orLike = function(field, val, pos) {
-
+		_p.like(field, val, pos, ' LIKE ', 'OR');
+		return this;
 	};
 
+	/**
+	 * Add an 'or not like' clause to the query
+	 *
+	 * @param {String} field - The name of the field  to compare to
+	 * @param {String} val - The value to compare to
+	 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
+	 * @return this
+	 */
 	this.orNotLike = function(field, val, pos) {
-
+		_p.like(field, val, pos, ' NOT LIKE ', 'OR');
+		return this;
 	};
 
+	/**
+	 * Add a 'having' clause
+	 *
+	 * @param {String|Object} key - The name of the field and the comparision operator, or an object
+	 * @param {String|Number} [val] - The value to compare if the value of key is a string
+	 * @return this
+	 */
 	this.having = function(key, val) {
-
+		_p.having(key, val, 'AND');
+		return this;
 	};
 
+	/**
+	 * Add an 'or having' clause
+	 *
+	 * @param {String|Object} key - The name of the field and the comparision operator, or an object
+	 * @param {String|Number} [val] - The value to compare if the value of key is a string
+	 * @return this
+	 */
 	this.orHaving = function(key, val) {
-
+		_p.having(key, val, 'OR');
+		return this;
 	};
 
+	/**
+	 * Set a 'where' clause
+	 *
+	 * @param {String|Object} key - The name of the field and the comparision operator, or an object
+	 * @param {String|Number} [val] - The value to compare if the value of key is a string
+	 * @return this
+	 */
 	this.where = function(key, val) {
-
+		_p.where(key, val, 'AND');
+		return this;
 	};
 
+	/**
+	 * Set a 'or where' clause
+	 *
+	 * @param {String|Object} key - The name of the field and the comparision operator, or an object
+	 * @param {String|Number} [val] - The value to compare if the value of key is a string
+	 * @return this
+	 */
 	this.orWhere = function(key, val) {
-
+		_p.where(key, val, 'OR');
+		return this;
 	};
 
+	/**
+	 * Set a 'where in' clause
+	 *
+	 * @param {String} key - the field to search
+	 * @param {Array} val - the array of items to search in
+	 * @return this
+	 */
 	this.whereIn = function(key, val) {
-
+		_p.whereIn(key, val, 'IN', 'AND');
+		return this;
 	};
 
+	/**
+	 * Set a 'or where in' clause
+	 *
+	 * @param {String} key - the field to search
+	 * @param {Array} val - the array of items to search in
+	 * @return this
+	 */
 	this.orWhereIn = function(key, val) {
-
+		_p.whereIn(key, val, 'IN', 'OR');
+		return this;
 	};
 
+	/**
+	 * Set a 'where not in' clause
+	 *
+	 * @param {String} key - the field to search
+	 * @param {Array} val - the array of items to search in
+	 * @return this
+	 */
 	this.whereNotIn = function(key, val) {
-
+		_p.whereIn(key, val, 'NOT IN', 'AND');
+		return this;
 	};
 
+	/**
+	 * Set a 'or where not in' clause
+	 *
+	 * @param {String} key - the field to search
+	 * @param {Array} val - the array of items to search in
+	 * @return this
+	 */
 	this.orWhereNotIn = function(key, val) {
-
-	};
-
-	this.set = function(key, val) {
+		_p.whereIn(key, val, 'NOT IN', 'OR');
 		return this;
 	};
 
-	this.join = function(table1, cond, table2, type) {
-		type = type || "inner";
+	/**
+	 * Set values for insertion or updating
+	 *
+	 * @param {String|Object} key - The key or object to use
+	 * @param {String} [val] - The value if using a scalar key
+	 * @return this
+	 */
+	this.set = function(/* key, [val] */) {
+		var args = getArgs('key:string|object, [val]:string', arguments);
+
+		// Set the appropriate state variables
+		_p.mixedSet('setArrayKeys', args.key, args.val, 'key');
+		_p.mixedSet('values', args.key, args.val, 'value');
+
+		// Use the keys of the array to make the insert/update string
+		// and escape the field names
+		state.setArrayKeys = state.setArrayKeys.map(this.driver._quote);
+
+		// Generate the "set" string
+		state.setString = state.setArrayKeys.join('=?,');
+		state.setString += '=?';
 
 		return this;
 	};
 
+	/**
+	 * Add a join clause to the query
+	 *
+	 * @param {String} joinOn - The table you are joining
+	 * @param {String} [cond='='] - The join condition, eg. =,<,>,<>,!=,etc.
+	 * @param {String} joinTo - The value of the condition you are joining on, whether another table's field, or a literal value
+	 * @param {String} [type='inner'] - The type of join, which defaults to inner
+	 * @return this
+	 */
+	this.join = function(/* joinOn, [cond='='], joinTo, [type='inner']*/) {
+		var args = getArgs('joinOn:string, [cond]:string, joinTo:string, [type]:string', arguments);
+		args.cond = args.cond || '=';
+		args.type = args.type || "inner";
+
+		return this;
+	};
+
+	/**
+	 * Group the results by the selected field(s)
+	 *
+	 * @param {String|Array} field
+	 * @return this
+	 */
 	this.groupBy = function(field) {
+		if (Array.isArray(field))
+		{
+			var newGroupArray = field.map(this.driver.quoteIdentifiers);
+			state.groupArray.concat(newGroupArray);
+		}
+		else
+		{
+			state.groupArray.push(this.driver.quoteIdentifiers(field));
+		}
 
+		state.groupString = ' GROUP BY ' + state.groupArray.join(',');
+
+		return this;
 	};
 
-	this.orderBy = function(field) {
+	/**
+	 * Order the results by the selected field(s)
+	 *
+	 * @param {String} field - The field to order by
+	 * @param {String} [type='ASC'] - The order direction, ASC or DESC
+	 * @return this
+	 */
+	this.orderBy = function(field, type) {
+		type = type || 'ASC';
 
+		// Set the fields for later manipulation
+		field = this.driver.quoteIdentifiers(field);
+		state.orderArray[field] = type;
+
+		var orderClauses = [];
+
+		// Flatten key/val pairs into an array of space-separated pairs
+		Object.keys(state.orderArray).forEach(function(key) {
+			orderClauses.push(key + ' ' + state.orderArray[key].toUpperCase());
+		});
+
+		// Set the final string
+		state.orderString = ' ORDER BY ' + orderClauses.join(', ');
+
+		return this;
 	};
 
+	/**
+	 * Put a limit on the query
+	 *
+	 * @param {Number} limit - The maximum number of rows to fetch
+	 * @param {Number} [offset] - The row number to start from
+	 * @return this
+	 */
 	this.limit = function(limit, offset) {
 		state.limit = limit;
-		state.offset = offset;
+		state.offset = offset || null;
 
 		return this;
 	};
 
+	/**
+	 * Adds an open paren to the current query for logical grouping
+	 *
+	 * @return this
+	 */
 	this.groupStart = function() {
+		var conj = (state.queryMap.length < 1) ? ' WHERE ' : ' ';
+		_p.appendMap(conj, '(', 'groupStart');
 
+		return this;
 	};
 
+	/**
+	 * Adds an open paren to the current query for logical grouping,
+	 * prefixed with 'OR'
+	 *
+	 * @return this
+	 */
 	this.orGroupStart = function() {
+		_p.appendMap('', ' OR (', 'groupStart');
 
+		return this;
 	};
 
+	/**
+	 * Adds an open paren to the current query for logical grouping,
+	 * prefixed with 'OR NOT'
+	 *
+	 * @return this
+	 */
 	this.orNotGroupStart = function() {
+		_p.appendMap('', ' OR NOT (', 'groupStart');
 
+		return this;
 	};
 
+	/**
+	 * Ends a logical grouping started with one of the groupStart methods
+	 *
+	 * @return this
+	 */
 	this.groupEnd = function() {
+		_p.appendMap('', ')', 'groupEnd');
 
+		return this;
 	};
 
 	// ------------------------------------------------------------------------
 	// ! Result Methods
 	// ------------------------------------------------------------------------
 
-	this.get = function(table, limit, offset) {
-		// Reset state
-		this.resetQuery();
+	/**
+	 * Get the results of the compiled query
+	 *
+	 * @param {String} [table] - The table to select from
+	 * @param {Number} [limit] - A limit for the query
+	 * @param {Number} [offset] - An offset for the query
+	 * @param {Function} callback - A callback for receiving the result
+	 * @return void
+	 */
+	this.get = function(/* [table], [limit], [offset], callback */) {
+		var args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments);
+
+		if (args.table) {
+			this.from(args.table);
+		}
+
+		if (args.limit) {
+			this.limit(args.limit, args.offset);
+		}
+
+		// Run the query
+		_p.run('get', args.table, args.callback);
 	};
 
-	this.getWhere = function(table, where, limit, offset) {
-		// Reset state
-		this.resetQuery();
+	/**
+	 * Run the generated insert query
+	 *
+	 * @param {String} table - The table to insert into
+	 * @param {Object} [data] - Data to insert, if not already added with the 'set' method
+	 * @param {Function} callback - Callback for handling response from the database
+	 * @return void
+	 */
+	this.insert = function(table, data, callback) {
+		if (data) {
+			this.set(data);
+		}
+
+		// Run the query
+		_p.run('insert', table, callback);
 	};
 
-	this.insert = function(table, data) {
-		// Reset state
-		this.resetQuery();
+	/**
+	 * Run the generated update query
+	 *
+	 * @param {String} table - The table to insert into
+	 * @param {Object} [data] - Data to insert, if not already added with the 'set' method
+	 * @param {Function} callback - Callback for handling response from the database
+	 * @return void
+	 */
+	this.update = function(table, data, callback) {
+		if (data) {
+			this.set(data);
+		}
+
+		// Run the query
+		_p.run('update', table, callback);
 	};
 
-	this.update = function(table, data) {
-		// Reset state
-		this.resetQuery();
-	};
-
-	this['delete'] = function (table, where) {
-		// Reset state
-		this.resetQuery();
+	/**
+	 * Run the generated delete query
+	 *
+	 * @param {String} table - The table to insert into
+	 * @param {Function} callback - Callback for handling response from the database
+	 * @return void
+	 */
+	this['delete'] = function (table, callback) {
+		// Run the query
+		_p.run('delete', table, callback);
 	};
 
 	// ------------------------------------------------------------------------
@@ -260,99 +848,154 @@ module.exports = function(driver) {
 	/**
 	 * Return generated select query SQL
 	 *
+	 * @param {String} [table] - the name of the table to retrieve from
+	 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
+	 * @return String
+	 */
+	this.getCompiledSelect = function(table, reset) {
+		if (table)
+		{
+			this.from(table);
+		}
+
+		return _p.getCompile('get', table, reset);
+	};
+
+	/**
+	 * Return generated insert query SQL
+	 *
+	 * @param {String} table - the name of the table to insert into
+	 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
 	 * @return {String}
 	 */
-	this.getCompiledSelect = function() {
-		// Return sql
-
-		// Reset state
-		this.resetQuery();
-	};
-
-	this.getCompiledInsert = function() {
-		// Return sql
-
-		// Reset state
-		this.resetQuery();
-	};
-
-	this.getCompiledUpdate = function() {
-		// Return sql
-
-		// Reset state
-		this.resetQuery();
-	};
-
-	this.getCompiledDelete = function() {
-		// Return sql
-
-		// Reset state
-		this.resetQuery();
-	};
-
-	// ----------------------------------------------------------------------------
-	// ! Miscellaneous Methods
-	// ----------------------------------------------------------------------------
-
-	/**
-	 * Reset the object state for a new query
-	 *
-	 * @return void
-	 */
-	this.resetQuery = function() {
-		state = {
-			// Arrays/Maps
-			queryMap: {},
-			values: [],
-			whereValues: [],
-			setArrayKeys: [],
-			orderArray: [],
-			groupArray: [],
-			havingMap: [],
-
-			// Partials
-			selectString: '',
-			fromString: '',
-			setString: '',
-			orderString: '',
-			groupString: '',
-
-			// Other various values
-			limit: null,
-			offset: null
-		};
+	this.getCompiledInsert = function(table, reset) {
+		return _p.getCompile('insert', table, reset);
 	};
 
 	/**
-	 * Returns the current class state for testing or other purposes
+	 * Return generated update query SQL
 	 *
-	 * @return {Object}
+	 * @param {String} table - the name of the table to update
+	 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
+	 * @return {String}
 	 */
-	this.getState = function() {
-		return state;
+	this.getCompiledUpdate = function(table, reset) {
+		return _p.getCompile('update', table, reset);
+	};
+
+	/**
+	 * Return generated delete query SQL
+	 *
+	 * @param {String} table - the name of the table to delete from
+	 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
+	 * @return {String}
+	 */
+	this.getCompiledDelete = function(table, reset) {
+		return _p.getCompile('delete', table, reset);
 	};
 
 	return this;
-};
-
-
+}; + +module.exports = QueryBuilder;
+
+
+ + + + + +
+ +
+ + + + +
+ + + + + + + + + + + - - + + -
- - - - - diff --git a/docs/scripts/URI.js b/docs/scripts/URI.js new file mode 100644 index 0000000..f8546c7 --- /dev/null +++ b/docs/scripts/URI.js @@ -0,0 +1,1429 @@ +/*! + * URI.js - Mutating URLs + * + * Version: 1.8.3 + * + * Author: Rodney Rehm + * Web: http://medialize.github.com/URI.js/ + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * GPL v3 http://opensource.org/licenses/GPL-3.0 + * + */ +(function(root, factory) { + // https://github.com/umdjs/umd/blob/master/returnExports.js + if (typeof exports === 'object') { + // Node + module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains')); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['./punycode', './IPv6', './SecondLevelDomains'], factory); + } else { + // Browser globals (root is window) + root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains); + } +}(this, function(punycode, IPv6, SLD) { + "use strict"; + + function URI(url, base) { + // Allow instantiation without the 'new' keyword + if (!(this instanceof URI)) { + return new URI(url, base); + } + if (url === undefined) { + if (typeof location !== 'undefined') { + url = location.href + ""; + } else { + url = ""; + } + } + this.href(url); + // resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor + if (base !== undefined) { + return this.absoluteTo(base); + } + return this; + }; + var p = URI.prototype; + var hasOwn = Object.prototype.hasOwnProperty; + + function escapeRegEx(string) { + // https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963 + return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); + } + + function isArray(obj) { + return String(Object.prototype.toString.call(obj)) === "[object Array]"; + } + + function filterArrayValues(data, value) { + var lookup = {}; + var i, length; + if (isArray(value)) { + for (i = 0, length = value.length; i < length; i++) { + lookup[value[i]] = true; + } + } else { + lookup[value] = true; + } + for (i = 0, length = data.length; i < length; i++) { + if (lookup[data[i]] !== undefined) { + data.splice(i, 1); + length--; + i--; + } + } + return data; + } + URI._parts = function() { + return { + protocol: null, + username: null, + password: null, + hostname: null, + urn: null, + port: null, + path: null, + query: null, + fragment: null, + // state + duplicateQueryParameters: URI.duplicateQueryParameters + }; + }; + // state: allow duplicate query parameters (a=1&a=1) + URI.duplicateQueryParameters = false; + // static properties + URI.protocol_expression = /^[a-z][a-z0-9-+-]*$/i; + URI.idn_expression = /[^a-z0-9\.-]/i; + URI.punycode_expression = /(xn--)/i; + // well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care? + URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; + // credits to Rich Brown + // source: http://forums.intermapper.com/viewtopic.php?p=1096#1096 + // specification: http://www.ietf.org/rfc/rfc4291.txt + URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; + // gruber revised expression - http://rodneyrehm.de/t/url-regex.html + URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig; + // http://www.iana.org/assignments/uri-schemes.html + // http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports + URI.defaultPorts = { + http: "80", + https: "443", + ftp: "21", + gopher: "70", + ws: "80", + wss: "443" + }; + // allowed hostname characters according to RFC 3986 + // ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded + // I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . - + URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/; + // encoding / decoding according to RFC3986 + + function strictEncodeURIComponent(string) { + // see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent + return encodeURIComponent(string).replace(/[!'()*]/g, escape).replace(/\*/g, "%2A"); + } + URI.encode = strictEncodeURIComponent; + URI.decode = decodeURIComponent; + URI.iso8859 = function() { + URI.encode = escape; + URI.decode = unescape; + }; + URI.unicode = function() { + URI.encode = strictEncodeURIComponent; + URI.decode = decodeURIComponent; + }; + URI.characters = { + pathname: { + encode: { + // RFC3986 2.1: For consistency, URI producers and normalizers should + // use uppercase hexadecimal digits for all percent-encodings. + expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig, + map: { + // -._~!'()* + "%24": "$", + "%26": "&", + "%2B": "+", + "%2C": ",", + "%3B": ";", + "%3D": "=", + "%3A": ":", + "%40": "@" + } + }, + decode: { + expression: /[\/\?#]/g, + map: { + "/": "%2F", + "?": "%3F", + "#": "%23" + } + } + }, + reserved: { + encode: { + // RFC3986 2.1: For consistency, URI producers and normalizers should + // use uppercase hexadecimal digits for all percent-encodings. + expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig, + map: { + // gen-delims + "%3A": ":", + "%2F": "/", + "%3F": "?", + "%23": "#", + "%5B": "[", + "%5D": "]", + "%40": "@", + // sub-delims + "%21": "!", + "%24": "$", + "%26": "&", + "%27": "'", + "%28": "(", + "%29": ")", + "%2A": "*", + "%2B": "+", + "%2C": ",", + "%3B": ";", + "%3D": "=" + } + } + } + }; + URI.encodeQuery = function(string) { + return URI.encode(string + "").replace(/%20/g, '+'); + }; + URI.decodeQuery = function(string) { + return URI.decode((string + "").replace(/\+/g, '%20')); + }; + URI.recodePath = function(string) { + var segments = (string + "").split('/'); + for (var i = 0, length = segments.length; i < length; i++) { + segments[i] = URI.encodePathSegment(URI.decode(segments[i])); + } + return segments.join('/'); + }; + URI.decodePath = function(string) { + var segments = (string + "").split('/'); + for (var i = 0, length = segments.length; i < length; i++) { + segments[i] = URI.decodePathSegment(segments[i]); + } + return segments.join('/'); + }; + // generate encode/decode path functions + var _parts = { + 'encode': 'encode', + 'decode': 'decode' + }; + var _part; + var generateAccessor = function(_group, _part) { + return function(string) { + return URI[_part](string + "").replace(URI.characters[_group][_part].expression, function(c) { + return URI.characters[_group][_part].map[c]; + }); + }; + }; + for (_part in _parts) { + URI[_part + "PathSegment"] = generateAccessor("pathname", _parts[_part]); + } + URI.encodeReserved = generateAccessor("reserved", "encode"); + URI.parse = function(string, parts) { + var pos, t; + if (!parts) { + parts = {}; + } + // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment] + // extract fragment + pos = string.indexOf('#'); + if (pos > -1) { + // escaping? + parts.fragment = string.substring(pos + 1) || null; + string = string.substring(0, pos); + } + // extract query + pos = string.indexOf('?'); + if (pos > -1) { + // escaping? + parts.query = string.substring(pos + 1) || null; + string = string.substring(0, pos); + } + // extract protocol + if (string.substring(0, 2) === '//') { + // relative-scheme + parts.protocol = ''; + string = string.substring(2); + // extract "user:pass@host:port" + string = URI.parseAuthority(string, parts); + } else { + pos = string.indexOf(':'); + if (pos > -1) { + parts.protocol = string.substring(0, pos); + if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) { + // : may be within the path + parts.protocol = undefined; + } else if (parts.protocol === 'file') { + // the file scheme: does not contain an authority + string = string.substring(pos + 3); + } else if (string.substring(pos + 1, pos + 3) === '//') { + string = string.substring(pos + 3); + // extract "user:pass@host:port" + string = URI.parseAuthority(string, parts); + } else { + string = string.substring(pos + 1); + parts.urn = true; + } + } + } + // what's left must be the path + parts.path = string; + // and we're done + return parts; + }; + URI.parseHost = function(string, parts) { + // extract host:port + var pos = string.indexOf('/'); + var bracketPos; + var t; + if (pos === -1) { + pos = string.length; + } + if (string[0] === "[") { + // IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6 + // I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts + // IPv6+port in the format [2001:db8::1]:80 (for the time being) + bracketPos = string.indexOf(']'); + parts.hostname = string.substring(1, bracketPos) || null; + parts.port = string.substring(bracketPos + 2, pos) || null; + } else if (string.indexOf(':') !== string.lastIndexOf(':')) { + // IPv6 host contains multiple colons - but no port + // this notation is actually not allowed by RFC 3986, but we're a liberal parser + parts.hostname = string.substring(0, pos) || null; + parts.port = null; + } else { + t = string.substring(0, pos).split(':'); + parts.hostname = t[0] || null; + parts.port = t[1] || null; + } + if (parts.hostname && string.substring(pos)[0] !== '/') { + pos++; + string = "/" + string; + } + return string.substring(pos) || '/'; + }; + URI.parseAuthority = function(string, parts) { + string = URI.parseUserinfo(string, parts); + return URI.parseHost(string, parts); + }; + URI.parseUserinfo = function(string, parts) { + // extract username:password + var pos = string.indexOf('@'); + var firstSlash = string.indexOf('/'); + var t; + // authority@ must come before /path + if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) { + t = string.substring(0, pos).split(':'); + parts.username = t[0] ? URI.decode(t[0]) : null; + t.shift(); + parts.password = t[0] ? URI.decode(t.join(':')) : null; + string = string.substring(pos + 1); + } else { + parts.username = null; + parts.password = null; + } + return string; + }; + URI.parseQuery = function(string) { + if (!string) { + return {}; + } + // throw out the funky business - "?"[name"="value"&"]+ + string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, ''); + if (!string) { + return {}; + } + var items = {}; + var splits = string.split('&'); + var length = splits.length; + var v, name, value; + for (var i = 0; i < length; i++) { + v = splits[i].split('='); + name = URI.decodeQuery(v.shift()); + // no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters + value = v.length ? URI.decodeQuery(v.join('=')) : null; + if (items[name]) { + if (typeof items[name] === "string") { + items[name] = [items[name]]; + } + items[name].push(value); + } else { + items[name] = value; + } + } + return items; + }; + URI.build = function(parts) { + var t = ""; + if (parts.protocol) { + t += parts.protocol + ":"; + } + if (!parts.urn && (t || parts.hostname)) { + t += '//'; + } + t += (URI.buildAuthority(parts) || ''); + if (typeof parts.path === "string") { + if (parts.path[0] !== '/' && typeof parts.hostname === "string") { + t += '/'; + } + t += parts.path; + } + if (typeof parts.query === "string" && parts.query) { + t += '?' + parts.query; + } + if (typeof parts.fragment === "string" && parts.fragment) { + t += '#' + parts.fragment; + } + return t; + }; + URI.buildHost = function(parts) { + var t = ""; + if (!parts.hostname) { + return ""; + } else if (URI.ip6_expression.test(parts.hostname)) { + if (parts.port) { + t += "[" + parts.hostname + "]:" + parts.port; + } else { + // don't know if we should always wrap IPv6 in [] + // the RFC explicitly says SHOULD, not MUST. + t += parts.hostname; + } + } else { + t += parts.hostname; + if (parts.port) { + t += ':' + parts.port; + } + } + return t; + }; + URI.buildAuthority = function(parts) { + return URI.buildUserinfo(parts) + URI.buildHost(parts); + }; + URI.buildUserinfo = function(parts) { + var t = ""; + if (parts.username) { + t += URI.encode(parts.username); + if (parts.password) { + t += ':' + URI.encode(parts.password); + } + t += "@"; + } + return t; + }; + URI.buildQuery = function(data, duplicates) { + // according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html + // being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed + // the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax! + // URI.js treats the query string as being application/x-www-form-urlencoded + // see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type + var t = ""; + var unique, key, i, length; + for (key in data) { + if (hasOwn.call(data, key) && key) { + if (isArray(data[key])) { + unique = {}; + for (i = 0, length = data[key].length; i < length; i++) { + if (data[key][i] !== undefined && unique[data[key][i] + ""] === undefined) { + t += "&" + URI.buildQueryParameter(key, data[key][i]); + if (duplicates !== true) { + unique[data[key][i] + ""] = true; + } + } + } + } else if (data[key] !== undefined) { + t += '&' + URI.buildQueryParameter(key, data[key]); + } + } + } + return t.substring(1); + }; + URI.buildQueryParameter = function(name, value) { + // http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded + // don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization + return URI.encodeQuery(name) + (value !== null ? "=" + URI.encodeQuery(value) : ""); + }; + URI.addQuery = function(data, name, value) { + if (typeof name === "object") { + for (var key in name) { + if (hasOwn.call(name, key)) { + URI.addQuery(data, key, name[key]); + } + } + } else if (typeof name === "string") { + if (data[name] === undefined) { + data[name] = value; + return; + } else if (typeof data[name] === "string") { + data[name] = [data[name]]; + } + if (!isArray(value)) { + value = [value]; + } + data[name] = data[name].concat(value); + } else { + throw new TypeError("URI.addQuery() accepts an object, string as the name parameter"); + } + }; + URI.removeQuery = function(data, name, value) { + var i, length, key; + if (isArray(name)) { + for (i = 0, length = name.length; i < length; i++) { + data[name[i]] = undefined; + } + } else if (typeof name === "object") { + for (key in name) { + if (hasOwn.call(name, key)) { + URI.removeQuery(data, key, name[key]); + } + } + } else if (typeof name === "string") { + if (value !== undefined) { + if (data[name] === value) { + data[name] = undefined; + } else if (isArray(data[name])) { + data[name] = filterArrayValues(data[name], value); + } + } else { + data[name] = undefined; + } + } else { + throw new TypeError("URI.addQuery() accepts an object, string as the first parameter"); + } + }; + URI.commonPath = function(one, two) { + var length = Math.min(one.length, two.length); + var pos; + // find first non-matching character + for (pos = 0; pos < length; pos++) { + if (one[pos] !== two[pos]) { + pos--; + break; + } + } + if (pos < 1) { + return one[0] === two[0] && one[0] === '/' ? '/' : ''; + } + // revert to last / + if (one[pos] !== '/') { + pos = one.substring(0, pos).lastIndexOf('/'); + } + return one.substring(0, pos + 1); + }; + URI.withinString = function(string, callback) { + // expression used is "gruber revised" (@gruber v2) determined to be the best solution in + // a regex sprint we did a couple of ages ago at + // * http://mathiasbynens.be/demo/url-regex + // * http://rodneyrehm.de/t/url-regex.html + return string.replace(URI.find_uri_expression, callback); + }; + URI.ensureValidHostname = function(v) { + // Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986) + // they are not part of DNS and therefore ignored by URI.js + if (v.match(URI.invalid_hostname_characters)) { + // test punycode + if (!punycode) { + throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-] and Punycode.js is not available"); + } + if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) { + throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-]"); + } + } + }; + p.build = function(deferBuild) { + if (deferBuild === true) { + this._deferred_build = true; + } else if (deferBuild === undefined || this._deferred_build) { + this._string = URI.build(this._parts); + this._deferred_build = false; + } + return this; + }; + p.clone = function() { + return new URI(this); + }; + p.valueOf = p.toString = function() { + return this.build(false)._string; + }; + // generate simple accessors + _parts = { + protocol: 'protocol', + username: 'username', + password: 'password', + hostname: 'hostname', + port: 'port' + }; + generateAccessor = function(_part) { + return function(v, build) { + if (v === undefined) { + return this._parts[_part] || ""; + } else { + this._parts[_part] = v; + this.build(!build); + return this; + } + }; + }; + for (_part in _parts) { + p[_part] = generateAccessor(_parts[_part]); + } + // generate accessors with optionally prefixed input + _parts = { + query: '?', + fragment: '#' + }; + generateAccessor = function(_part, _key) { + return function(v, build) { + if (v === undefined) { + return this._parts[_part] || ""; + } else { + if (v !== null) { + v = v + ""; + if (v[0] === _key) { + v = v.substring(1); + } + } + this._parts[_part] = v; + this.build(!build); + return this; + } + }; + }; + for (_part in _parts) { + p[_part] = generateAccessor(_part, _parts[_part]); + } + // generate accessors with prefixed output + _parts = { + search: ['?', 'query'], + hash: ['#', 'fragment'] + }; + generateAccessor = function(_part, _key) { + return function(v, build) { + var t = this[_part](v, build); + return typeof t === "string" && t.length ? (_key + t) : t; + }; + }; + for (_part in _parts) { + p[_part] = generateAccessor(_parts[_part][1], _parts[_part][0]); + } + p.pathname = function(v, build) { + if (v === undefined || v === true) { + var res = this._parts.path || (this._parts.urn ? '' : '/'); + return v ? URI.decodePath(res) : res; + } else { + this._parts.path = v ? URI.recodePath(v) : "/"; + this.build(!build); + return this; + } + }; + p.path = p.pathname; + p.href = function(href, build) { + var key; + if (href === undefined) { + return this.toString(); + } + this._string = ""; + this._parts = URI._parts(); + var _URI = href instanceof URI; + var _object = typeof href === "object" && (href.hostname || href.path); + // window.location is reported to be an object, but it's not the sort + // of object we're looking for: + // * location.protocol ends with a colon + // * location.query != object.search + // * location.hash != object.fragment + // simply serializing the unknown object should do the trick + // (for location, not for everything...) + if (!_URI && _object && Object.prototype.toString.call(href) !== "[object Object]") { + href = href.toString(); + } + if (typeof href === "string") { + this._parts = URI.parse(href, this._parts); + } else if (_URI || _object) { + var src = _URI ? href._parts : href; + for (key in src) { + if (hasOwn.call(this._parts, key)) { + this._parts[key] = src[key]; + } + } + } else { + throw new TypeError("invalid input"); + } + this.build(!build); + return this; + }; + // identification accessors + p.is = function(what) { + var ip = false; + var ip4 = false; + var ip6 = false; + var name = false; + var sld = false; + var idn = false; + var punycode = false; + var relative = !this._parts.urn; + if (this._parts.hostname) { + relative = false; + ip4 = URI.ip4_expression.test(this._parts.hostname); + ip6 = URI.ip6_expression.test(this._parts.hostname); + ip = ip4 || ip6; + name = !ip; + sld = name && SLD && SLD.has(this._parts.hostname); + idn = name && URI.idn_expression.test(this._parts.hostname); + punycode = name && URI.punycode_expression.test(this._parts.hostname); + } + switch (what.toLowerCase()) { + case 'relative': + return relative; + case 'absolute': + return !relative; + // hostname identification + case 'domain': + case 'name': + return name; + case 'sld': + return sld; + case 'ip': + return ip; + case 'ip4': + case 'ipv4': + case 'inet4': + return ip4; + case 'ip6': + case 'ipv6': + case 'inet6': + return ip6; + case 'idn': + return idn; + case 'url': + return !this._parts.urn; + case 'urn': + return !!this._parts.urn; + case 'punycode': + return punycode; + } + return null; + }; + // component specific input validation + var _protocol = p.protocol; + var _port = p.port; + var _hostname = p.hostname; + p.protocol = function(v, build) { + if (v !== undefined) { + if (v) { + // accept trailing :// + v = v.replace(/:(\/\/)?$/, ''); + if (v.match(/[^a-zA-z0-9\.+-]/)) { + throw new TypeError("Protocol '" + v + "' contains characters other than [A-Z0-9.+-]"); + } + } + } + return _protocol.call(this, v, build); + }; + p.scheme = p.protocol; + p.port = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v !== undefined) { + if (v === 0) { + v = null; + } + if (v) { + v += ""; + if (v[0] === ":") { + v = v.substring(1); + } + if (v.match(/[^0-9]/)) { + throw new TypeError("Port '" + v + "' contains characters other than [0-9]"); + } + } + } + return _port.call(this, v, build); + }; + p.hostname = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v !== undefined) { + var x = {}; + URI.parseHost(v, x); + v = x.hostname; + } + return _hostname.call(this, v, build); + }; + // compound accessors + p.host = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined) { + return this._parts.hostname ? URI.buildHost(this._parts) : ""; + } else { + URI.parseHost(v, this._parts); + this.build(!build); + return this; + } + }; + p.authority = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined) { + return this._parts.hostname ? URI.buildAuthority(this._parts) : ""; + } else { + URI.parseAuthority(v, this._parts); + this.build(!build); + return this; + } + }; + p.userinfo = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined) { + if (!this._parts.username) { + return ""; + } + var t = URI.buildUserinfo(this._parts); + return t.substring(0, t.length - 1); + } else { + if (v[v.length - 1] !== '@') { + v += '@'; + } + URI.parseUserinfo(v, this._parts); + this.build(!build); + return this; + } + }; + p.resource = function(v, build) { + var parts; + if (v === undefined) { + return this.path() + this.search() + this.hash(); + } + parts = URI.parse(v); + this._parts.path = parts.path; + this._parts.query = parts.query; + this._parts.fragment = parts.fragment; + this.build(!build); + return this; + }; + // fraction accessors + p.subdomain = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + // convenience, return "www" from "www.example.org" + if (v === undefined) { + if (!this._parts.hostname || this.is('IP')) { + return ""; + } + // grab domain and add another segment + var end = this._parts.hostname.length - this.domain().length - 1; + return this._parts.hostname.substring(0, end) || ""; + } else { + var e = this._parts.hostname.length - this.domain().length; + var sub = this._parts.hostname.substring(0, e); + var replace = new RegExp('^' + escapeRegEx(sub)); + if (v && v[v.length - 1] !== '.') { + v += "."; + } + if (v) { + URI.ensureValidHostname(v); + } + this._parts.hostname = this._parts.hostname.replace(replace, v); + this.build(!build); + return this; + } + }; + p.domain = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (typeof v === 'boolean') { + build = v; + v = undefined; + } + // convenience, return "example.org" from "www.example.org" + if (v === undefined) { + if (!this._parts.hostname || this.is('IP')) { + return ""; + } + // if hostname consists of 1 or 2 segments, it must be the domain + var t = this._parts.hostname.match(/\./g); + if (t && t.length < 2) { + return this._parts.hostname; + } + // grab tld and add another segment + var end = this._parts.hostname.length - this.tld(build).length - 1; + end = this._parts.hostname.lastIndexOf('.', end - 1) + 1; + return this._parts.hostname.substring(end) || ""; + } else { + if (!v) { + throw new TypeError("cannot set domain empty"); + } + URI.ensureValidHostname(v); + if (!this._parts.hostname || this.is('IP')) { + this._parts.hostname = v; + } else { + var replace = new RegExp(escapeRegEx(this.domain()) + "$"); + this._parts.hostname = this._parts.hostname.replace(replace, v); + } + this.build(!build); + return this; + } + }; + p.tld = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (typeof v === 'boolean') { + build = v; + v = undefined; + } + // return "org" from "www.example.org" + if (v === undefined) { + if (!this._parts.hostname || this.is('IP')) { + return ""; + } + var pos = this._parts.hostname.lastIndexOf('.'); + var tld = this._parts.hostname.substring(pos + 1); + if (build !== true && SLD && SLD.list[tld.toLowerCase()]) { + return SLD.get(this._parts.hostname) || tld; + } + return tld; + } else { + var replace; + if (!v) { + throw new TypeError("cannot set TLD empty"); + } else if (v.match(/[^a-zA-Z0-9-]/)) { + if (SLD && SLD.is(v)) { + replace = new RegExp(escapeRegEx(this.tld()) + "$"); + this._parts.hostname = this._parts.hostname.replace(replace, v); + } else { + throw new TypeError("TLD '" + v + "' contains characters other than [A-Z0-9]"); + } + } else if (!this._parts.hostname || this.is('IP')) { + throw new ReferenceError("cannot set TLD on non-domain host"); + } else { + replace = new RegExp(escapeRegEx(this.tld()) + "$"); + this._parts.hostname = this._parts.hostname.replace(replace, v); + } + this.build(!build); + return this; + } + }; + p.directory = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined || v === true) { + if (!this._parts.path && !this._parts.hostname) { + return ''; + } + if (this._parts.path === '/') { + return '/'; + } + var end = this._parts.path.length - this.filename().length - 1; + var res = this._parts.path.substring(0, end) || (this._parts.hostname ? "/" : ""); + return v ? URI.decodePath(res) : res; + } else { + var e = this._parts.path.length - this.filename().length; + var directory = this._parts.path.substring(0, e); + var replace = new RegExp('^' + escapeRegEx(directory)); + // fully qualifier directories begin with a slash + if (!this.is('relative')) { + if (!v) { + v = '/'; + } + if (v[0] !== '/') { + v = "/" + v; + } + } + // directories always end with a slash + if (v && v[v.length - 1] !== '/') { + v += '/'; + } + v = URI.recodePath(v); + this._parts.path = this._parts.path.replace(replace, v); + this.build(!build); + return this; + } + }; + p.filename = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined || v === true) { + if (!this._parts.path || this._parts.path === '/') { + return ""; + } + var pos = this._parts.path.lastIndexOf('/'); + var res = this._parts.path.substring(pos + 1); + return v ? URI.decodePathSegment(res) : res; + } else { + var mutatedDirectory = false; + if (v[0] === '/') { + v = v.substring(1); + } + if (v.match(/\.?\//)) { + mutatedDirectory = true; + } + var replace = new RegExp(escapeRegEx(this.filename()) + "$"); + v = URI.recodePath(v); + this._parts.path = this._parts.path.replace(replace, v); + if (mutatedDirectory) { + this.normalizePath(build); + } else { + this.build(!build); + } + return this; + } + }; + p.suffix = function(v, build) { + if (this._parts.urn) { + return v === undefined ? '' : this; + } + if (v === undefined || v === true) { + if (!this._parts.path || this._parts.path === '/') { + return ""; + } + var filename = this.filename(); + var pos = filename.lastIndexOf('.'); + var s, res; + if (pos === -1) { + return ""; + } + // suffix may only contain alnum characters (yup, I made this up.) + s = filename.substring(pos + 1); + res = (/^[a-z0-9%]+$/i).test(s) ? s : ""; + return v ? URI.decodePathSegment(res) : res; + } else { + if (v[0] === '.') { + v = v.substring(1); + } + var suffix = this.suffix(); + var replace; + if (!suffix) { + if (!v) { + return this; + } + this._parts.path += '.' + URI.recodePath(v); + } else if (!v) { + replace = new RegExp(escapeRegEx("." + suffix) + "$"); + } else { + replace = new RegExp(escapeRegEx(suffix) + "$"); + } + if (replace) { + v = URI.recodePath(v); + this._parts.path = this._parts.path.replace(replace, v); + } + this.build(!build); + return this; + } + }; + p.segment = function(segment, v, build) { + var separator = this._parts.urn ? ':' : '/'; + var path = this.path(); + var absolute = path.substring(0, 1) === '/'; + var segments = path.split(separator); + if (typeof segment !== 'number') { + build = v; + v = segment; + segment = undefined; + } + if (segment !== undefined && typeof segment !== 'number') { + throw new Error("Bad segment '" + segment + "', must be 0-based integer"); + } + if (absolute) { + segments.shift(); + } + if (segment < 0) { + // allow negative indexes to address from the end + segment = Math.max(segments.length + segment, 0); + } + if (v === undefined) { + return segment === undefined ? segments : segments[segment]; + } else if (segment === null || segments[segment] === undefined) { + if (isArray(v)) { + segments = v; + } else if (v || (typeof v === "string" && v.length)) { + if (segments[segments.length - 1] === "") { + // empty trailing elements have to be overwritten + // to prefent results such as /foo//bar + segments[segments.length - 1] = v; + } else { + segments.push(v); + } + } + } else { + if (v || (typeof v === "string" && v.length)) { + segments[segment] = v; + } else { + segments.splice(segment, 1); + } + } + if (absolute) { + segments.unshift(""); + } + return this.path(segments.join(separator), build); + }; + // mutating query string + var q = p.query; + p.query = function(v, build) { + if (v === true) { + return URI.parseQuery(this._parts.query); + } else if (v !== undefined && typeof v !== "string") { + this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters); + this.build(!build); + return this; + } else { + return q.call(this, v, build); + } + }; + p.addQuery = function(name, value, build) { + var data = URI.parseQuery(this._parts.query); + URI.addQuery(data, name, value === undefined ? null : value); + this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters); + if (typeof name !== "string") { + build = value; + } + this.build(!build); + return this; + }; + p.removeQuery = function(name, value, build) { + var data = URI.parseQuery(this._parts.query); + URI.removeQuery(data, name, value); + this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters); + if (typeof name !== "string") { + build = value; + } + this.build(!build); + return this; + }; + p.addSearch = p.addQuery; + p.removeSearch = p.removeQuery; + // sanitizing URLs + p.normalize = function() { + if (this._parts.urn) { + return this.normalizeProtocol(false).normalizeQuery(false).normalizeFragment(false).build(); + } + return this.normalizeProtocol(false).normalizeHostname(false).normalizePort(false).normalizePath(false).normalizeQuery(false).normalizeFragment(false).build(); + }; + p.normalizeProtocol = function(build) { + if (typeof this._parts.protocol === "string") { + this._parts.protocol = this._parts.protocol.toLowerCase(); + this.build(!build); + } + return this; + }; + p.normalizeHostname = function(build) { + if (this._parts.hostname) { + if (this.is('IDN') && punycode) { + this._parts.hostname = punycode.toASCII(this._parts.hostname); + } else if (this.is('IPv6') && IPv6) { + this._parts.hostname = IPv6.best(this._parts.hostname); + } + this._parts.hostname = this._parts.hostname.toLowerCase(); + this.build(!build); + } + return this; + }; + p.normalizePort = function(build) { + // remove port of it's the protocol's default + if (typeof this._parts.protocol === "string" && this._parts.port === URI.defaultPorts[this._parts.protocol]) { + this._parts.port = null; + this.build(!build); + } + return this; + }; + p.normalizePath = function(build) { + if (this._parts.urn) { + return this; + } + if (!this._parts.path || this._parts.path === '/') { + return this; + } + var _was_relative; + var _was_relative_prefix; + var _path = this._parts.path; + var _parent, _pos; + // handle relative paths + if (_path[0] !== '/') { + if (_path[0] === '.') { + _was_relative_prefix = _path.substring(0, _path.indexOf('/')); + } + _was_relative = true; + _path = '/' + _path; + } + // resolve simples + _path = _path.replace(/(\/(\.\/)+)|\/{2,}/g, '/'); + // resolve parents + while (true) { + _parent = _path.indexOf('/../'); + if (_parent === -1) { + // no more ../ to resolve + break; + } else if (_parent === 0) { + // top level cannot be relative... + _path = _path.substring(3); + break; + } + _pos = _path.substring(0, _parent).lastIndexOf('/'); + if (_pos === -1) { + _pos = _parent; + } + _path = _path.substring(0, _pos) + _path.substring(_parent + 3); + } + // revert to relative + if (_was_relative && this.is('relative')) { + if (_was_relative_prefix) { + _path = _was_relative_prefix + _path; + } else { + _path = _path.substring(1); + } + } + _path = URI.recodePath(_path); + this._parts.path = _path; + this.build(!build); + return this; + }; + p.normalizePathname = p.normalizePath; + p.normalizeQuery = function(build) { + if (typeof this._parts.query === "string") { + if (!this._parts.query.length) { + this._parts.query = null; + } else { + this.query(URI.parseQuery(this._parts.query)); + } + this.build(!build); + } + return this; + }; + p.normalizeFragment = function(build) { + if (!this._parts.fragment) { + this._parts.fragment = null; + this.build(!build); + } + return this; + }; + p.normalizeSearch = p.normalizeQuery; + p.normalizeHash = p.normalizeFragment; + p.iso8859 = function() { + // expect unicode input, iso8859 output + var e = URI.encode; + var d = URI.decode; + URI.encode = escape; + URI.decode = decodeURIComponent; + this.normalize(); + URI.encode = e; + URI.decode = d; + return this; + }; + p.unicode = function() { + // expect iso8859 input, unicode output + var e = URI.encode; + var d = URI.decode; + URI.encode = strictEncodeURIComponent; + URI.decode = unescape; + this.normalize(); + URI.encode = e; + URI.decode = d; + return this; + }; + p.readable = function() { + var uri = this.clone(); + // removing username, password, because they shouldn't be displayed according to RFC 3986 + uri.username("").password("").normalize(); + var t = ''; + if (uri._parts.protocol) { + t += uri._parts.protocol + '://'; + } + if (uri._parts.hostname) { + if (uri.is('punycode') && punycode) { + t += punycode.toUnicode(uri._parts.hostname); + if (uri._parts.port) { + t += ":" + uri._parts.port; + } + } else { + t += uri.host(); + } + } + if (uri._parts.hostname && uri._parts.path && uri._parts.path[0] !== '/') { + t += '/'; + } + t += uri.path(true); + if (uri._parts.query) { + var q = ''; + for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) { + var kv = (qp[i] || "").split('='); + q += '&' + URI.decodeQuery(kv[0]).replace(/&/g, '%26'); + if (kv[1] !== undefined) { + q += "=" + URI.decodeQuery(kv[1]).replace(/&/g, '%26'); + } + } + t += '?' + q.substring(1); + } + t += uri.hash(); + return t; + }; + // resolving relative and absolute URLs + p.absoluteTo = function(base) { + var resolved = this.clone(); + var properties = ['protocol', 'username', 'password', 'hostname', 'port']; + var basedir, i, p; + if (this._parts.urn) { + throw new Error('URNs do not have any generally defined hierachical components'); + } + if (this._parts.hostname) { + return resolved; + } + if (!(base instanceof URI)) { + base = new URI(base); + } + for (i = 0, p; p = properties[i]; i++) { + resolved._parts[p] = base._parts[p]; + } + properties = ['query', 'path']; + for (i = 0, p; p = properties[i]; i++) { + if (!resolved._parts[p] && base._parts[p]) { + resolved._parts[p] = base._parts[p]; + } + } + if (resolved.path()[0] !== '/') { + basedir = base.directory(); + resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path; + resolved.normalizePath(); + } + resolved.build(); + return resolved; + }; + p.relativeTo = function(base) { + var relative = this.clone(); + var properties = ['protocol', 'username', 'password', 'hostname', 'port']; + var common, _base, _this, _base_diff, _this_diff; + if (this._parts.urn) { + throw new Error('URNs do not have any generally defined hierachical components'); + } + if (!(base instanceof URI)) { + base = new URI(base); + } + if (this.path()[0] !== '/' || base.path()[0] !== '/') { + throw new Error('Cannot calculate common path from non-relative URLs'); + } + // determine common sub path + common = URI.commonPath(relative.path(), base.path()); + // no relation if there's nothing in common + if (!common || common === '/') { + return relative; + } + // relative paths don't have authority + for (var i = 0, p; p = properties[i]; i++) { + relative._parts[p] = null; + } + _base = base.directory(); + _this = this.directory(); + // base and this are on the same level + if (_base === _this) { + relative._parts.path = './' + relative.filename(); + return relative.build(); + } + _base_diff = _base.substring(common.length); + _this_diff = _this.substring(common.length); + // this is a descendant of base + if (_base + '/' === common) { + if (_this_diff) { + _this_diff += '/'; + } + relative._parts.path = './' + _this_diff + relative.filename(); + return relative.build(); + } + // this is a descendant of base + var parents = '../'; + var _common = new RegExp('^' + escapeRegEx(common)); + var _parents = _base.replace(_common, '/').match(/\//g).length - 1; + while (_parents--) { + parents += '../'; + } + relative._parts.path = relative._parts.path.replace(_common, parents); + return relative.build(); + }; + // comparing URIs + p.equals = function(uri) { + var one = this.clone(); + var two = new URI(uri); + var one_map = {}; + var two_map = {}; + var checked = {}; + var one_query, two_query, key; + one.normalize(); + two.normalize(); + // exact match + if (one.toString() === two.toString()) { + return true; + } + // extract query string + one_query = one.query(); + two_query = two.query(); + one.query(""); + two.query(""); + // definitely not equal if not even non-query parts match + if (one.toString() !== two.toString()) { + return false; + } + // query parameters have the same length, even if they're permutated + if (one_query.length !== two_query.length) { + return false; + } + one_map = URI.parseQuery(one_query); + two_map = URI.parseQuery(two_query); + for (key in one_map) { + if (hasOwn.call(one_map, key)) { + if (!isArray(one_map[key])) { + if (one_map[key] !== two_map[key]) { + return false; + } + } else { + if (!isArray(two_map[key])) { + return false; + } + // arrays can't be equal if they have different amount of content + if (one_map[key].length !== two_map[key].length) { + return false; + } + one_map[key].sort(); + two_map[key].sort(); + for (var i = 0, l = one_map[key].length; i < l; i++) { + if (one_map[key][i] !== two_map[key][i]) { + return false; + } + } + } + checked[key] = true; + } + } + for (key in two_map) { + if (hasOwn.call(two_map, key)) { + if (!checked[key]) { + // two contains a parameter not present in one + return false; + } + } + } + return true; + }; + // state + p.duplicateQueryParameters = function(v) { + this._parts.duplicateQueryParameters = !! v; + return this; + }; + return URI; +})); \ No newline at end of file diff --git a/docs/scripts/bootstrap-dropdown.js b/docs/scripts/bootstrap-dropdown.js new file mode 100644 index 0000000..d04da5d --- /dev/null +++ b/docs/scripts/bootstrap-dropdown.js @@ -0,0 +1,169 @@ +/* ============================================================ + * bootstrap-dropdown.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#dropdowns + * ============================================================ + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* DROPDOWN CLASS DEFINITION + * ========================= */ + + var toggle = '[data-toggle=dropdown]' + , Dropdown = function (element) { + var $el = $(element).on('click.dropdown.data-api', this.toggle) + $('html').on('click.dropdown.data-api', function () { + $el.parent().removeClass('open') + }) + } + + Dropdown.prototype = { + + constructor: Dropdown + + , toggle: function (e) { + var $this = $(this) + , $parent + , isActive + + if ($this.is('.disabled, :disabled')) return + + $parent = getParent($this) + + isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement) { + // if mobile we we use a backdrop because click events don't delegate + $('