Permalink
Browse files

Added client-side support. Closes #33

  • Loading branch information...
1 parent 587d699 commit 35ddd4c22237a0ebf80a0d1374b5d0e155e07274 @tj committed Feb 29, 2012
Showing with 14,205 additions and 13 deletions.
  1. +3 −1 Makefile
  2. +1 −0 lib/head.js
  3. +1 −0 lib/tail.js
  4. +265 −0 querystring.js
  5. +1,202 −0 test/browser/expect.js
  6. +18 −0 test/browser/index.html
  7. +8,981 −0 test/browser/jquery.js
  8. +135 −0 test/browser/mocha.css
  9. +3,589 −0 test/browser/mocha.js
  10. +0 −2 test/mocha.opts
  11. +6 −4 test/parse.js
  12. +4 −6 test/stringify.js
View
4 Makefile
@@ -1,7 +1,9 @@
+querystring.js: lib/head.js lib/querystring.js lib/tail.js
+ cat $^ > $@
+
test:
@./node_modules/.bin/mocha \
- --require should \
--ui bdd
.PHONY: test
View
1 lib/head.js
@@ -0,0 +1 @@
+(function(exports){
View
1 lib/tail.js
@@ -0,0 +1 @@
+})('undefined' == typeof exports ? qs = {} : exports);
View
265 querystring.js
@@ -0,0 +1,265 @@
+(function(exports){
+/*!
+ * querystring
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Library version.
+ */
+
+exports.version = '0.4.2';
+
+/**
+ * Object#toString() ref for stringify().
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Cache non-integer test regexp.
+ */
+
+var isint = /^[0-9]+$/;
+
+function promote(parent, key) {
+ if (parent[key].length == 0) return parent[key] = {};
+ var t = {};
+ for (var i in parent[key]) t[i] = parent[key][i];
+ parent[key] = t;
+ return t;
+}
+
+function parse(parts, parent, key, val) {
+ var part = parts.shift();
+ // end
+ if (!part) {
+ if (Array.isArray(parent[key])) {
+ parent[key].push(val);
+ } else if ('object' == typeof parent[key]) {
+ parent[key] = val;
+ } else if ('undefined' == typeof parent[key]) {
+ parent[key] = val;
+ } else {
+ parent[key] = [parent[key], val];
+ }
+ // array
+ } else {
+ var obj = parent[key] = parent[key] || [];
+ if (']' == part) {
+ if (Array.isArray(obj)) {
+ if ('' != val) obj.push(val);
+ } else if ('object' == typeof obj) {
+ obj[Object.keys(obj).length] = val;
+ } else {
+ obj = parent[key] = [parent[key], val];
+ }
+ // prop
+ } else if (~part.indexOf(']')) {
+ part = part.substr(0, part.length - 1);
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
+ parse(parts, obj, part, val);
+ // key
+ } else {
+ if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key);
+ parse(parts, obj, part, val);
+ }
+ }
+}
+
+/**
+ * Merge parent key/val pair.
+ */
+
+function merge(parent, key, val){
+ if (~key.indexOf(']')) {
+ var parts = key.split('[')
+ , len = parts.length
+ , last = len - 1;
+ parse(parts, parent, 'base', val);
+ // optimize
+ } else {
+ if (!isint.test(key) && Array.isArray(parent.base)) {
+ var t = {};
+ for (var k in parent.base) t[k] = parent.base[k];
+ parent.base = t;
+ }
+ set(parent.base, key, val);
+ }
+
+ return parent;
+}
+
+/**
+ * Parse the given obj.
+ */
+
+function parseObject(obj){
+ var ret = { base: {} };
+ Object.keys(obj).forEach(function(name){
+ merge(ret, name, obj[name]);
+ });
+ return ret.base;
+}
+
+/**
+ * Parse the given str.
+ */
+
+function parseString(str){
+ return String(str)
+ .split('&')
+ .reduce(function(ret, pair){
+ try{
+ pair = decodeURIComponent(pair.replace(/\+/g, ' '));
+ } catch(e) {
+ // ignore
+ }
+
+ var eql = pair.indexOf('=')
+ , brace = lastBraceInKey(pair)
+ , key = pair.substr(0, brace || eql)
+ , val = pair.substr(brace || eql, pair.length)
+ , val = val.substr(val.indexOf('=') + 1, val.length);
+
+ // ?foo
+ if ('' == key) key = pair, val = '';
+
+ return merge(ret, key, val);
+ }, { base: {} }).base;
+}
+
+/**
+ * Parse the given query `str` or `obj`, returning an object.
+ *
+ * @param {String} str | {Object} obj
+ * @return {Object}
+ * @api public
+ */
+
+exports.parse = function(str){
+ if (null == str || '' == str) return {};
+ return 'object' == typeof str
+ ? parseObject(str)
+ : parseString(str);
+};
+
+/**
+ * Turn the given `obj` into a query string
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api public
+ */
+
+var stringify = exports.stringify = function(obj, prefix) {
+ if (Array.isArray(obj)) {
+ return stringifyArray(obj, prefix);
+ } else if ('[object Object]' == toString.call(obj)) {
+ return stringifyObject(obj, prefix);
+ } else if ('string' == typeof obj) {
+ return stringifyString(obj, prefix);
+ } else {
+ return prefix + '=' + obj;
+ }
+};
+
+/**
+ * Stringify the given `str`.
+ *
+ * @param {String} str
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyString(str, prefix) {
+ if (!prefix) throw new TypeError('stringify expects an object');
+ return prefix + '=' + encodeURIComponent(str);
+}
+
+/**
+ * Stringify the given `arr`.
+ *
+ * @param {Array} arr
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyArray(arr, prefix) {
+ var ret = [];
+ if (!prefix) throw new TypeError('stringify expects an object');
+ for (var i = 0; i < arr.length; i++) {
+ ret.push(stringify(arr[i], prefix + '['+i+']'));
+ }
+ return ret.join('&');
+}
+
+/**
+ * Stringify the given `obj`.
+ *
+ * @param {Object} obj
+ * @param {String} prefix
+ * @return {String}
+ * @api private
+ */
+
+function stringifyObject(obj, prefix) {
+ var ret = []
+ , keys = Object.keys(obj)
+ , key;
+
+ for (var i = 0, len = keys.length; i < len; ++i) {
+ key = keys[i];
+ ret.push(stringify(obj[key], prefix
+ ? prefix + '[' + encodeURIComponent(key) + ']'
+ : encodeURIComponent(key)));
+ }
+
+ return ret.join('&');
+}
+
+/**
+ * Set `obj`'s `key` to `val` respecting
+ * the weird and wonderful syntax of a qs,
+ * where "foo=bar&foo=baz" becomes an array.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {String} val
+ * @api private
+ */
+
+function set(obj, key, val) {
+ var v = obj[key];
+ if (undefined === v) {
+ obj[key] = val;
+ } else if (Array.isArray(v)) {
+ v.push(val);
+ } else {
+ obj[key] = [v, val];
+ }
+}
+
+/**
+ * Locate last brace in `str` within the key.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function lastBraceInKey(str) {
+ var len = str.length
+ , brace
+ , c;
+ for (var i = 0; i < len; ++i) {
+ c = str[i];
+ if (']' == c) brace = false;
+ if ('[' == c) brace = true;
+ if ('=' == c && !brace) return i;
+ }
+}
+})('undefined' == typeof exports ? qs = {} : exports);
View
1,202 test/browser/expect.js
@@ -0,0 +1,1202 @@
+
+(function (global, module) {
+
+ if ('undefined' == typeof module) {
+ var module = { exports: {} }
+ , exports = module.exports
+ }
+
+ /**
+ * Exports.
+ */
+
+ module.exports = expect;
+ expect.Assertion = Assertion;
+
+ /**
+ * Exports version.
+ */
+
+ expect.version = '0.1.2';
+
+ /**
+ * Possible assertion flags.
+ */
+
+ var flags = {
+ not: ['to', 'be', 'have', 'include', 'only']
+ , to: ['be', 'have', 'include', 'only', 'not']
+ , only: ['have']
+ , have: ['own']
+ , be: ['an']
+ };
+
+ function expect (obj) {
+ return new Assertion(obj);
+ }
+
+ /**
+ * Constructor
+ *
+ * @api private
+ */
+
+ function Assertion (obj, flag, parent) {
+ this.obj = obj;
+ this.flags = {};
+
+ if (undefined != parent) {
+ this.flags[flag] = true;
+
+ for (var i in parent.flags) {
+ if (parent.flags.hasOwnProperty(i)) {
+ this.flags[i] = true;
+ }
+ }
+ }
+
+ var $flags = flag ? flags[flag] : keys(flags)
+ , self = this
+
+ if ($flags) {
+ for (var i = 0, l = $flags.length; i < l; i++) {
+ // avoid recursion
+ if (this.flags[$flags[i]]) continue;
+
+ var name = $flags[i]
+ , assertion = new Assertion(this.obj, name, this)
+
+ if ('function' == typeof Assertion.prototype[name]) {
+ // clone the function, make sure we dont touch the prot reference
+ var old = this[name];
+ this[name] = function () {
+ return old.apply(self, arguments);
+ }
+
+ for (var fn in Assertion.prototype) {
+ if (Assertion.prototype.hasOwnProperty(fn) && fn != name) {
+ this[name][fn] = bind(assertion[fn], assertion);
+ }
+ }
+ } else {
+ this[name] = assertion;
+ }
+ }
+ }
+ };
+
+ /**
+ * Performs an assertion
+ *
+ * @api private
+ */
+
+ Assertion.prototype.assert = function (truth, msg, error) {
+ var msg = this.flags.not ? error : msg
+ , ok = this.flags.not ? !truth : truth;
+
+ if (!ok) {
+ throw new Error(msg);
+ }
+
+ this.and = new Assertion(this.obj);
+ };
+
+ /**
+ * Check if the value is truthy
+ *
+ * @api public
+ */
+
+ Assertion.prototype.ok = function () {
+ this.assert(
+ !!this.obj
+ , 'expected ' + i(this.obj) + ' to be truthy'
+ , 'expected ' + i(this.obj) + ' to be falsy');
+ };
+
+ /**
+ * Assert that the function throws.
+ *
+ * @param {Function|RegExp} callback, or regexp to match error string against
+ * @api public
+ */
+
+ Assertion.prototype.throwError =
+ Assertion.prototype.throwException = function (fn) {
+ expect(this.obj).to.be.a('function');
+
+ var thrown = false
+ , not = this.flags.not
+
+ try {
+ this.obj();
+ } catch (e) {
+ if ('function' == typeof fn) {
+ fn(e);
+ } else if ('object' == typeof fn) {
+ var subject = 'string' == typeof e ? e : e.message;
+ if (not) {
+ expect(subject).to.not.match(fn);
+ } else {
+ expect(subject).to.match(fn);
+ }
+ }
+ thrown = true;
+ }
+
+ if ('object' == typeof fn && not) {
+ // in the presence of a matcher, ensure the `not` only applies to
+ // the matching.
+ this.flags.not = false;
+ }
+
+ var name = this.obj.name || 'fn';
+ this.assert(
+ thrown
+ , 'expected ' + name + ' to throw an exception'
+ , 'expected ' + name + ' not to throw an exception');
+ };
+
+ /**
+ * Checks if the array is empty.
+ *
+ * @api public
+ */
+
+ Assertion.prototype.empty = function () {
+ var expectation;
+
+ if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) {
+ if ('number' == typeof this.obj.length) {
+ expectation = !this.obj.length;
+ } else {
+ expectation = !keys(this.obj).length;
+ }
+ } else {
+ if ('string' != typeof this.obj) {
+ expect(this.obj).to.be.an('object');
+ }
+
+ expect(this.obj).to.have.property('length');
+ expectation = !this.obj.length;
+ }
+
+ this.assert(
+ expectation
+ , 'expected ' + i(this.obj) + ' to be empty'
+ , 'expected ' + i(this.obj) + ' to not be empty');
+ return this;
+ };
+
+ /**
+ * Checks if the obj exactly equals another.
+ *
+ * @api public
+ */
+
+ Assertion.prototype.be =
+ Assertion.prototype.equal = function (obj) {
+ this.assert(
+ obj === this.obj
+ , 'expected ' + i(this.obj) + ' to equal ' + i(obj)
+ , 'expected ' + i(this.obj) + ' to not equal ' + i(obj));
+ return this;
+ };
+
+ /**
+ * Checks if the obj sortof equals another.
+ *
+ * @api public
+ */
+
+ Assertion.prototype.eql = function (obj) {
+ this.assert(
+ expect.eql(obj, this.obj)
+ , 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj)
+ , 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj));
+ return this;
+ };
+
+ /**
+ * Assert within start to finish (inclusive).
+ *
+ * @param {Number} start
+ * @param {Number} finish
+ * @api public
+ */
+
+ Assertion.prototype.within = function (start, finish) {
+ var range = start + '..' + finish;
+ this.assert(
+ this.obj >= start && this.obj <= finish
+ , 'expected ' + i(this.obj) + ' to be within ' + range
+ , 'expected ' + i(this.obj) + ' to not be within ' + range);
+ return this;
+ };
+
+ /**
+ * Assert typeof / instance of
+ *
+ * @api public
+ */
+
+ Assertion.prototype.a =
+ Assertion.prototype.an = function (type) {
+ if ('string' == typeof type) {
+ // proper english in error msg
+ var n = /^[aeiou]/.test(type) ? 'n' : '';
+
+ // typeof with support for 'array'
+ this.assert(
+ 'array' == type ? isArray(this.obj) :
+ 'object' == type
+ ? 'object' == typeof this.obj && null !== this.obj
+ : type == typeof this.obj
+ , 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type
+ , 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type);
+ } else {
+ // instanceof
+ var name = type.name || 'supplied constructor';
+ this.assert(
+ this.obj instanceof type
+ , 'expected ' + i(this.obj) + ' to be an instance of ' + name
+ , 'expected ' + i(this.obj) + ' not to be an instance of ' + name);
+ }
+
+ return this;
+ };
+
+ /**
+ * Assert numeric value above _n_.
+ *
+ * @param {Number} n
+ * @api public
+ */
+
+ Assertion.prototype.greaterThan =
+ Assertion.prototype.above = function (n) {
+ this.assert(
+ this.obj > n
+ , 'expected ' + i(this.obj) + ' to be above ' + n
+ , 'expected ' + i(this.obj) + ' to be below ' + n);
+ return this;
+ };
+
+ /**
+ * Assert numeric value below _n_.
+ *
+ * @param {Number} n
+ * @api public
+ */
+
+ Assertion.prototype.lessThan =
+ Assertion.prototype.below = function (n) {
+ this.assert(
+ this.obj < n
+ , 'expected ' + i(this.obj) + ' to be below ' + n
+ , 'expected ' + i(this.obj) + ' to be above ' + n);
+ return this;
+ };
+
+ /**
+ * Assert string value matches _regexp_.
+ *
+ * @param {RegExp} regexp
+ * @api public
+ */
+
+ Assertion.prototype.match = function (regexp) {
+ this.assert(
+ regexp.exec(this.obj)
+ , 'expected ' + i(this.obj) + ' to match ' + regexp
+ , 'expected ' + i(this.obj) + ' not to match ' + regexp);
+ return this;
+ };
+
+ /**
+ * Assert property "length" exists and has value of _n_.
+ *
+ * @param {Number} n
+ * @api public
+ */
+
+ Assertion.prototype.length = function (n) {
+ expect(this.obj).to.have.property('length');
+ var len = this.obj.length;
+ this.assert(
+ n == len
+ , 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len
+ , 'expected ' + i(this.obj) + ' to not have a length of ' + len);
+ return this;
+ };
+
+ /**
+ * Assert property _name_ exists, with optional _val_.
+ *
+ * @param {String} name
+ * @param {Mixed} val
+ * @api public
+ */
+
+ Assertion.prototype.property = function (name, val) {
+ if (this.flags.own) {
+ this.assert(
+ Object.prototype.hasOwnProperty.call(this.obj, name)
+ , 'expected ' + i(this.obj) + ' to have own property ' + i(name)
+ , 'expected ' + i(this.obj) + ' to not have own property ' + i(name));
+ return this;
+ }
+
+ if (this.flags.not && undefined !== val) {
+ if (undefined === this.obj[name]) {
+ throw new Error(i(this.obj) + ' has no property ' + i(name));
+ }
+ } else {
+ var hasProp;
+ try {
+ hasProp = name in this.obj
+ } catch (e) {
+ hasProp = undefined !== this.obj[name]
+ }
+
+ this.assert(
+ hasProp
+ , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
+ , 'expected ' + i(this.obj) + ' to not have a property ' + i(name));
+ }
+
+ if (undefined !== val) {
+ this.assert(
+ val === this.obj[name]
+ , 'expected ' + i(this.obj) + ' to have a property ' + i(name)
+ + ' of ' + i(val) + ', but got ' + i(this.obj[name])
+ , 'expected ' + i(this.obj) + ' to not have a property ' + i(name)
+ + ' of ' + i(val));
+ }
+
+ this.obj = this.obj[name];
+ return this;
+ };
+
+ /**
+ * Assert that the array contains _obj_ or string contains _obj_.
+ *
+ * @param {Mixed} obj|string
+ * @api public
+ */
+
+ Assertion.prototype.string =
+ Assertion.prototype.contain = function (obj) {
+ if ('string' == typeof this.obj) {
+ this.assert(
+ ~this.obj.indexOf(obj)
+ , 'expected ' + i(this.obj) + ' to contain ' + i(obj)
+ , 'expected ' + i(this.obj) + ' to not contain ' + i(obj));
+ } else {
+ this.assert(
+ ~indexOf(this.obj, obj)
+ , 'expected ' + i(this.obj) + ' to contain ' + i(obj)
+ , 'expected ' + i(this.obj) + ' to not contain ' + i(obj));
+ }
+ return this;
+ };
+
+ /**
+ * Assert exact keys or inclusion of keys by using
+ * the `.own` modifier.
+ *
+ * @param {Array|String ...} keys
+ * @api public
+ */
+
+ Assertion.prototype.key =
+ Assertion.prototype.keys = function ($keys) {
+ var str
+ , ok = true;
+
+ $keys = isArray($keys)
+ ? $keys
+ : Array.prototype.slice.call(arguments);
+
+ if (!$keys.length) throw new Error('keys required');
+
+ var actual = keys(this.obj)
+ , len = $keys.length;
+
+ // Inclusion
+ ok = every($keys, function (key) {
+ return ~indexOf(actual, key);
+ });
+
+ // Strict
+ if (!this.flags.not && this.flags.only) {
+ ok = ok && $keys.length == actual.length;
+ }
+
+ // Key string
+ if (len > 1) {
+ $keys = map($keys, function (key) {
+ return i(key);
+ });
+ var last = $keys.pop();
+ str = $keys.join(', ') + ', and ' + last;
+ } else {
+ str = i($keys[0]);
+ }
+
+ // Form
+ str = (len > 1 ? 'keys ' : 'key ') + str;
+
+ // Have / include
+ str = (!this.flags.only ? 'include ' : 'only have ') + str;
+
+ // Assertion
+ this.assert(
+ ok
+ , 'expected ' + i(this.obj) + ' to ' + str
+ , 'expected ' + i(this.obj) + ' to not ' + str);
+
+ return this;
+ };
+
+ /**
+ * Function bind implementation.
+ */
+
+ function bind (fn, scope) {
+ return function () {
+ return fn.apply(scope, arguments);
+ }
+ }
+
+ /**
+ * Array every compatibility
+ *
+ * @see bit.ly/5Fq1N2
+ * @api public
+ */
+
+ function every (arr, fn, thisObj) {
+ var scope = thisObj || global;
+ for (var i = 0, j = arr.length; i < j; ++i) {
+ if (!fn.call(scope, arr[i], i, arr)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ /**
+ * Array indexOf compatibility.
+ *
+ * @see bit.ly/a5Dxa2
+ * @api public
+ */
+
+ function indexOf (arr, o, i) {
+ if (Array.prototype.indexOf) {
+ return Array.prototype.indexOf.call(arr, o, i);
+ }
+
+ if (arr.length === undefined) {
+ return -1;
+ }
+
+ for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0
+ ; i < j && arr[i] !== o; i++);
+
+ return j <= i ? -1 : i;
+ };
+
+ /**
+ * Inspects an object.
+ *
+ * @see taken from node.js `util` module (copyright Joyent, MIT license)
+ * @api private
+ */
+
+ function i (obj, showHidden, depth) {
+ var seen = [];
+
+ function stylize (str) {
+ return str;
+ };
+
+ function format (value, recurseTimes) {
+ // Provide a hook for user-specified inspect functions.
+ // Check that value is an object with an inspect function on it
+ if (value && typeof value.inspect === 'function' &&
+ // Filter out the util module, it's inspect function is special
+ value !== exports &&
+ // Also filter out any prototype objects using the circular check.
+ !(value.constructor && value.constructor.prototype === value)) {
+ return value.inspect(recurseTimes);
+ }
+
+ // Primitive types cannot have properties
+ switch (typeof value) {
+ case 'undefined':
+ return stylize('undefined', 'undefined');
+
+ case 'string':
+ var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '')
+ .replace(/'/g, "\\'")
+ .replace(/\\"/g, '"') + '\'';
+ return stylize(simple, 'string');
+
+ case 'number':
+ return stylize('' + value, 'number');
+
+ case 'boolean':
+ return stylize('' + value, 'boolean');
+ }
+ // For some reason typeof null is "object", so special case here.
+ if (value === null) {
+ return stylize('null', 'null');
+ }
+
+ // Look up the keys of the object.
+ var visible_keys = keys(value);
+ var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
+
+ // Functions without properties can be shortcutted.
+ if (typeof value === 'function' && $keys.length === 0) {
+ if (isRegExp(value)) {
+ return stylize('' + value, 'regexp');
+ } else {
+ var name = value.name ? ': ' + value.name : '';
+ return stylize('[Function' + name + ']', 'special');
+ }
+ }
+
+ // Dates without properties can be shortcutted
+ if (isDate(value) && $keys.length === 0) {
+ return stylize(value.toUTCString(), 'date');
+ }
+
+ var base, type, braces;
+ // Determine the object type
+ if (isArray(value)) {
+ type = 'Array';
+ braces = ['[', ']'];
+ } else {
+ type = 'Object';
+ braces = ['{', '}'];
+ }
+
+ // Make functions say that they are functions
+ if (typeof value === 'function') {
+ var n = value.name ? ': ' + value.name : '';
+ base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
+ } else {
+ base = '';
+ }
+
+ // Make dates with properties first say the date
+ if (isDate(value)) {
+ base = ' ' + value.toUTCString();
+ }
+
+ if ($keys.length === 0) {
+ return braces[0] + base + braces[1];
+ }
+
+ if (recurseTimes < 0) {
+ if (isRegExp(value)) {
+ return stylize('' + value, 'regexp');
+ } else {
+ return stylize('[Object]', 'special');
+ }
+ }
+
+ seen.push(value);
+
+ var output = map($keys, function (key) {
+ var name, str;
+ if (value.__lookupGetter__) {
+ if (value.__lookupGetter__(key)) {
+ if (value.__lookupSetter__(key)) {
+ str = stylize('[Getter/Setter]', 'special');
+ } else {
+ str = stylize('[Getter]', 'special');
+ }
+ } else {
+ if (value.__lookupSetter__(key)) {
+ str = stylize('[Setter]', 'special');
+ }
+ }
+ }
+ if (indexOf(visible_keys, key) < 0) {
+ name = '[' + key + ']';
+ }
+ if (!str) {
+ if (indexOf(seen, value[key]) < 0) {
+ if (recurseTimes === null) {
+ str = format(value[key]);
+ } else {
+ str = format(value[key], recurseTimes - 1);
+ }
+ if (str.indexOf('\n') > -1) {
+ if (isArray(value)) {
+ str = map(str.split('\n'), function (line) {
+ return ' ' + line;
+ }).join('\n').substr(2);
+ } else {
+ str = '\n' + map(str.split('\n'), function (line) {
+ return ' ' + line;
+ }).join('\n');
+ }
+ }
+ } else {
+ str = stylize('[Circular]', 'special');
+ }
+ }
+ if (typeof name === 'undefined') {
+ if (type === 'Array' && key.match(/^\d+$/)) {
+ return str;
+ }
+ name = json.stringify('' + key);
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+ name = name.substr(1, name.length - 2);
+ name = stylize(name, 'name');
+ } else {
+ name = name.replace(/'/g, "\\'")
+ .replace(/\\"/g, '"')
+ .replace(/(^"|"$)/g, "'");
+ name = stylize(name, 'string');
+ }
+ }
+
+ return name + ': ' + str;
+ });
+
+ seen.pop();
+
+ var numLinesEst = 0;
+ var length = reduce(output, function (prev, cur) {
+ numLinesEst++;
+ if (indexOf(cur, '\n') >= 0) numLinesEst++;
+ return prev + cur.length + 1;
+ }, 0);
+
+ if (length > 50) {
+ output = braces[0] +
+ (base === '' ? '' : base + '\n ') +
+ ' ' +
+ output.join(',\n ') +
+ ' ' +
+ braces[1];
+
+ } else {
+ output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+ }
+
+ return output;
+ }
+ return format(obj, (typeof depth === 'undefined' ? 2 : depth));
+ };
+
+ function isArray (ar) {
+ return Object.prototype.toString.call(ar) == '[object Array]';
+ };
+
+ function isRegExp(re) {
+ var s = '' + re;
+ return re instanceof RegExp || // easy case
+ // duck-type for context-switching evalcx case
+ typeof(re) === 'function' &&
+ re.constructor.name === 'RegExp' &&
+ re.compile &&
+ re.test &&
+ re.exec &&
+ s.match(/^\/.*\/[gim]{0,3}$/);
+ };
+
+ function isDate(d) {
+ if (d instanceof Date) return true;
+ return false;
+ };
+
+ function keys (obj) {
+ if (Object.keys) {
+ return Object.keys(obj);
+ }
+
+ var keys = [];
+
+ for (var i in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
+ keys.push(i);
+ }
+ }
+
+ return keys;
+ }
+
+ function map (arr, mapper, that) {
+ if (Array.prototype.map) {
+ return Array.prototype.map.call(arr, mapper, that);
+ }
+
+ var other= new Array(arr.length);
+
+ for (var i= 0, n = arr.length; i<n; i++)
+ if (i in arr)
+ other[i] = mapper.call(that, arr[i], i, arr);
+
+ return other;
+ };
+
+ function reduce (arr, fun) {
+ if (Array.prototype.reduce) {
+ return Array.prototype.reduce.apply(
+ arr
+ , Array.prototype.slice.call(arguments, 1)
+ );
+ }
+
+ var len = +this.length;
+
+ if (typeof fun !== "function")
+ throw new TypeError();
+
+ // no value to return if no initial value and an empty array
+ if (len === 0 && arguments.length === 1)
+ throw new TypeError();
+
+ var i = 0;
+ if (arguments.length >= 2) {
+ var rv = arguments[1];
+ } else {
+ do {
+ if (i in this) {
+ rv = this[i++];
+ break;
+ }
+
+ // if array contains no values, no initial value to return
+ if (++i >= len)
+ throw new TypeError();
+ } while (true);
+ }
+
+ for (; i < len; i++) {
+ if (i in this)
+ rv = fun.call(null, rv, this[i], i, this);
+ }
+
+ return rv;
+ };
+
+ /**
+ * Asserts deep equality
+ *
+ * @see taken from node.js `assert` module (copyright Joyent, MIT license)
+ * @api private
+ */
+
+ expect.eql = function eql (actual, expected) {
+ // 7.1. All identical values are equivalent, as determined by ===.
+ if (actual === expected) {
+ return true;
+ } else if ('undefined' != typeof Buffer
+ && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
+ if (actual.length != expected.length) return false;
+
+ for (var i = 0; i < actual.length; i++) {
+ if (actual[i] !== expected[i]) return false;
+ }
+
+ return true;
+
+ // 7.2. If the expected value is a Date object, the actual value is
+ // equivalent if it is also a Date object that refers to the same time.
+ } else if (actual instanceof Date && expected instanceof Date) {
+ return actual.getTime() === expected.getTime();
+
+ // 7.3. Other pairs that do not both pass typeof value == "object",
+ // equivalence is determined by ==.
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
+ return actual == expected;
+
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
+ // determined by having the same number of owned properties (as verified
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
+ // (although not necessarily the same order), equivalent values for every
+ // corresponding key, and an identical "prototype" property. Note: this
+ // accounts for both named and indexed properties on Arrays.
+ } else {
+ return objEquiv(actual, expected);
+ }
+ }
+
+ function isUndefinedOrNull (value) {
+ return value === null || value === undefined;
+ }
+
+ function isArguments (object) {
+ return Object.prototype.toString.call(object) == '[object Arguments]';
+ }
+
+ function objEquiv (a, b) {
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
+ return false;
+ // an identical "prototype" property.
+ if (a.prototype !== b.prototype) return false;
+ //~~~I've managed to break Object.keys through screwy arguments passing.
+ // Converting to array solves the problem.
+ if (isArguments(a)) {
+ if (!isArguments(b)) {
+ return false;
+ }
+ a = pSlice.call(a);
+ b = pSlice.call(b);
+ return expect.eql(a, b);
+ }
+ try{
+ var ka = keys(a),
+ kb = keys(b),
+ key, i;
+ } catch (e) {//happens when one is a string literal and the other isn't
+ return false;
+ }
+ // having the same number of owned properties (keys incorporates hasOwnProperty)
+ if (ka.length != kb.length)
+ return false;
+ //the same set of keys (although not necessarily the same order),
+ ka.sort();
+ kb.sort();
+ //~~~cheap key test
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] != kb[i])
+ return false;
+ }
+ //equivalent values for every corresponding key, and
+ //~~~possibly expensive deep test
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ if (!expect.eql(a[key], b[key]))
+ return false;
+ }
+ return true;
+ }
+
+ var json = (function () {
+ "use strict";
+
+ if ('object' == typeof JSON && JSON.parse && JSON.stringify) {
+ return {
+ parse: nativeJSON.parse
+ , stringify: nativeJSON.stringify
+ }
+ }
+
+ var JSON = {};
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ function date(d, key) {
+ return isFinite(d.valueOf()) ?
+ d.getUTCFullYear() + '-' +
+ f(d.getUTCMonth() + 1) + '-' +
+ f(d.getUTCDate()) + 'T' +
+ f(d.getUTCHours()) + ':' +
+ f(d.getUTCMinutes()) + ':' +
+ f(d.getUTCSeconds()) + 'Z' : null;
+ };
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can safely slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe escape
+ // sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+ // Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+ // If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value instanceof Date) {
+ value = date(key);
+ }
+
+ // If we were called with a replacer function, then call the replacer to
+ // obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+ // What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+ // JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+ // If the value is a boolean or null, convert it to a string. Note:
+ // typeof null does not produce 'null'. The case is included here in
+ // the remote chance that this gets fixed someday.
+
+ return String(value);
+
+ // If the type is 'object', we might be dealing with an object or an array or
+ // null.
+
+ case 'object':
+
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
+ // so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+ // Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+ // Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+ // The value is an array. Stringify every element. Use null as a placeholder
+ // for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+ // Join all of the elements together, separated with commas, and wrap them in
+ // brackets.
+
+ v = partial.length === 0 ? '[]' : gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+ // If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+ // Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+ // Join all of the member texts together, separated with commas,
+ // and wrap them in braces.
+
+ v = partial.length === 0 ? '{}' : gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+ // If the JSON object does not yet have a stringify method, give it one.
+
+ JSON.stringify = function (value, replacer, space) {
+
+ // The stringify method takes a value and an optional replacer, and an optional
+ // space parameter, and returns a JSON text. The replacer can be a function
+ // that can replace values, or an array of strings that will select the keys.
+ // A default replacer method can be provided. Use of the space parameter can
+ // produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+ // If the space parameter is a number, make an indent string containing that
+ // many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+ // If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+ // If there is a replacer, it must be a function or an array.
+ // Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+ // Make a fake root object containing our value under the key of ''.
+ // Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+
+ // If the JSON object does not yet have a parse method, give it one.
+
+ JSON.parse = function (text, reviver) {
+ // The parse method takes a text and an optional reviver function, and returns
+ // a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+ // The walk method is used to recursively walk the resulting structure so
+ // that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+ // Parsing happens in four stages. In the first stage, we replace certain
+ // Unicode characters with escape sequences. JavaScript handles many characters
+ // incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+ // In the second stage, we run the text against regular expressions that look
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
+ // because they can cause invocation, and '=' because it can cause mutation.
+ // But just to be safe, we want to reject all unexpected forms.
+
+ // We split the second stage into 4 regexp operations in order to work around
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+ // replace all simple value tokens with ']' characters. Third, we delete all
+ // open brackets that follow a colon or comma or that begin the text. Finally,
+ // we look to see that the remaining characters are only whitespace or ']' or
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+ // In the third stage we use the eval function to compile the text into a
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
+ // in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+ // In the optional fourth stage, we recursively walk the new structure, passing
+ // each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({'': j}, '') : j;
+ }
+
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+ return JSON;
+ })();
+
+ if ('undefined' != typeof window) {
+ window.expect = module.exports;
+ }
+
+})(
+ this
+ , 'undefined' != typeof module ? module : {}
+ , 'undefined' != typeof exports ? exports : {}
+);
View
18 test/browser/index.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <title>Mocha</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" href="mocha.css" />
+ <script src="jquery.js" type="text/javascript"></script>
+ <script src="expect.js"></script>
+ <script src="mocha.js"></script>
+ <script>mocha.setup('bdd')</script>
+ <script src="../../querystring.js"></script>
+ <script src="../parse.js"></script>
+ <script src="../stringify.js"></script>
+ <script>onload = mocha.run;</script>
+ </head>
+ <body>
+ <div id="mocha"></div>
+ </body>
+</html>
View
8,981 test/browser/jquery.js
8,981 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
135 test/browser/mocha.css
@@ -0,0 +1,135 @@
+
+body {
+ font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 60px 50px;
+}
+
+#mocha h1, h2 {
+ margin: 0;
+}
+
+#mocha h1 {
+ font-size: 1em;
+ font-weight: 200;
+}
+
+#mocha .suite .suite h1 {
+ font-size: .8em;
+}
+
+#mocha h2 {
+ font-size: 12px;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+#mocha .suite {
+ margin-left: 15px;
+}
+
+#mocha .test {
+ margin-left: 15px;
+}
+
+#mocha .test:hover h2::after {
+ position: relative;
+ top: 0;
+ right: -10px;
+ content: '(view source)';
+ font-size: 12px;
+ font-family: arial;
+ color: #888;
+}
+
+#mocha .test.pending:hover h2::after {
+ content: '(pending)';
+ font-family: arial;
+}
+
+#mocha .test.pass::before {
+ content: '';
+ font-size: 12px;
+ display: block;
+ float: left;
+ margin-right: 5px;
+ color: #00c41c;
+}
+
+#mocha .test.pending {
+ color: #0b97c4;
+}
+
+#mocha .test.pending::before {
+ content: '';
+ color: #0b97c4;
+}
+
+#mocha .test.fail {
+ color: #c00;
+}
+
+#mocha .test.fail pre {
+ color: black;
+}
+
+#mocha .test.fail::before {
+ content: '';
+ font-size: 12px;
+ display: block;
+ float: left;
+ margin-right: 5px;
+ color: #c00;
+}
+
+#mocha .test pre.error {
+ color: #c00;
+}
+
+#mocha .test pre {
+ display: inline-block;
+ font: 12px/1.5 monaco, monospace;
+ margin: 5px;
+ padding: 15px;
+ border: 1px solid #eee;
+ border-bottom-color: #ddd;
+ -webkit-border-radius: 3px;
+ -webkit-box-shadow: 0 1px 3px #eee;
+}
+
+#error {
+ color: #c00;
+ font-size: 1.5 em;
+ font-weight: 100;
+ letter-spacing: 1px;
+}
+
+#stats {
+ position: fixed;
+ top: 15px;
+ right: 10px;
+ font-size: 12px;
+ margin: 0;
+ color: #888;
+}
+
+#stats .progress {
+ float: right;
+ padding-top: 0;
+}
+
+#stats em {
+ color: black;
+}
+
+#stats li {
+ display: inline-block;
+ margin: 0 5px;
+ list-style: none;
+ padding-top: 11px;
+}
+
+code .comment { color: #ddd }
+code .init { color: #2F6FAD }
+code .string { color: #5890AD }
+code .keyword { color: #8A6343 }
+code .number { color: #2F6FAD }
View
3,589 test/browser/mocha.js
3,589 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
2 test/mocha.opts
@@ -1,2 +0,0 @@
---require should
---ui exports
View
10 test/parse.js
@@ -1,6 +1,8 @@
-var qs = require('../')
- , expect = require('expect.js');
+if ('function' == typeof require) {
+ var qs = require('../')
+ , expect = require('expect.js');
+}
describe('qs.parse()', function(){
it('should support the basics', function(){
@@ -85,8 +87,8 @@ describe('qs.parse()', function(){
.to.eql({ user: { name: { first: ['tj', 'TJ'] }}});
var o = qs.parse('existing[fcbaebfecc][name][last]=tj')
- o.should.eql({ existing: { 'fcbaebfecc': { name: { last: 'tj' }}}})
- Array.isArray(o.existing).should.be.false;
+ expect(o).to.eql({ existing: { 'fcbaebfecc': { name: { last: 'tj' }}}})
+ expect(Array.isArray(o.existing)).to.equal(false);
})
it('should support arrays with indexes', function(){
View
10 test/stringify.js
@@ -1,10 +1,8 @@
-/**
- * Module dependencies.
- */
-
-var qs = require('../')
- , expect = require('expect.js');
+if ('function' == typeof require) {
+ var qs = require('../')
+ , expect = require('expect.js');
+}
var str_identities = {
'basics': [

0 comments on commit 35ddd4c

Please sign in to comment.