angular-jsv
Version:
JSV (JSON+CSV) httpParamSerializer for AngularJS
489 lines (435 loc) • 16.8 kB
JavaScript
angular.module("JSV", [])
.service("JsvHttpParamSerializer", function () {
function isWindow(obj) {
return obj && obj.window === obj;
}
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
function toJsvReplacer(key, value) {
var val = value;
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
return val;
}
function toJsv(obj, pretty) {
if (typeof obj === 'undefined') return undefined;
if (!angular.isNumber(pretty)) {
pretty = pretty ? 2 : null;
}
return JSV.stringify(obj, toJsvReplacer, pretty);
}
function serializeValue(v) {
if (angular.isObject(v)) {
return angular.isDate(v) ? v.toISOString() : toJsv(v);
}
return v;
}
function forEachSorted(obj, iterator, context) {
var keys = Object.keys(obj).sort();
for (var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
}
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%3B/gi, ';').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
return function (params) {
if (!params) return '';
var parts = [];
forEachSorted(params, function (value, key) {
if (value === null || angular.isUndefined(value)) return;
if (angular.isArray(value)) {
angular.forEach(value, function (v, k) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
});
} else {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
}
});
return parts.join('&');
}
});
/**
* Created by IntelliJ IDEA.
* User: mythz
* Date: 16-Jun-2010
* Time: 00:51:17
* To change this template use File | Settings | File Templates.
*/
(function () {
if (typeof window !== "undefined" && window !== null) {
window.JSV = JSV = {};
} else if (typeof module !== "undefined" && module !== null) {
module.exports = JSV = {};
}
/**
* parses JSV text into a JavaScript type
* @param str
*/
JSV.parse = function (str) {
if (!str) return str;
if (str[0] == '{') {
return JSV.parseObject_(str);
}
else if (str[0] == '[') {
return JSV.parseArray_(str);
}
else {
return JSV.parseString(str);
}
}
JSV.ESCAPE_CHARS = ['"', ',', '{', '}', '[', ']'];
JSV.parseArray_ = function (str) {
var to = [], value = JSV.stripList_(str);
if (!value) return to;
if (value[0] == '{') {
var ref = { i: 0 };
do {
var itemValue = JSV.eatMapValue_(value, ref);
to.push(JSV.parse(itemValue));
} while (++ref.i < value.length);
}
else {
for (var ref = { i: 0 }; ref.i < value.length; ref.i++) {
var elementValue = JSV.eatElementValue_(value, ref);
to.push(JSV.parse(elementValue));
}
}
return to;
};
JSV.parseObject_ = function (str) {
if (str[0] != '{') {
throw "Type definitions should start with a '{', got string starting with: "
+ str.substr(0, str.length < 50 ? str.length : 50);
}
var name, obj = {};
if (str == '{}') return null;
for (var ref = { i: 1 }, strTypeLength = str.length; ref.i < strTypeLength; ref.i++) {
name = JSV.eatMapKey_(str, ref);
ref.i++;
var value = JSV.eatMapValue_(str, ref);
obj[name] = JSV.parse(value);
}
return obj;
}
JSV.eatElementValue_ = function (value, ref) {
return JSV.eatUntilCharFound_(value, ref, ',');
}
JSV.containsAny_ = function (str, tests) {
if (!is.String(str)) return;
for (var i = 0, len = tests.length; i < len; i++) {
if (str.indexOf(tests[i]) != -1) return true;
}
return false;
};
JSV.toCsvField = function (text) {
return !text || JSV.containsAny_(JSV.ESCAPE_CHARS)
? text
: '"' + text.replace(/"/g, '""') + '"';
}
JSV.parseString = JSV.fromCsvField = function (text) {
return !text || text[0] != '"'
? text
: text.substr(1, text.length - 2).replace(/""/g, '"');
}
JSV.stripList_ = function (value) {
if (!value) return null;
return value[0] == '['
? value.substr(1, value.length - 2)
: value;
};
/**
* @param value {string}
* @param ref {ref int}
* @param findChar {char}
*/
JSV.eatUntilCharFound_ = function (value, ref, findChar) {
var tokenStartPos = ref.i;
var valueLength = value.length;
if (value[tokenStartPos] != '"') {
ref.i = value.indexOf(findChar, tokenStartPos);
if (ref.i == -1) ref.i = valueLength;
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
while (++ref.i < valueLength) {
if (value[ref.i] == '"') {
if (ref.i + 1 >= valueLength) {
return value.substr(tokenStartPos, ++ref.i - tokenStartPos);
}
if (value[ref.i + 1] == '"') {
ref.i++;
}
else if (value[ref.i + 1] == findChar) {
return value.substr(tokenStartPos, ++ref.i - tokenStartPos);
}
}
}
throw "Could not find ending quote";
}
/**
*
* @param value {string}
* @param i {ref int}
*/
JSV.eatMapKey_ = function (value, ref) {
var tokenStartPos = ref.i;
while (value[++ref.i] != ':' && ref.i < value.length) { }
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
/**
*
* @param value {string}
* @param ref {ref int}
*/
JSV.eatMapValue_ = function (value, ref) {
var tokenStartPos = ref.i;
var valueLength = value.length;
if (ref.i == valueLength) return null;
var valueChar = value[ref.i];
//If we are at the end, return.
if (valueChar == ',' || valueChar == '}') {
return null;
}
//Is List, i.e. [...]
var withinQuotes = false;
if (valueChar == '[') {
var endsToEat = 1;
while (++ref.i < valueLength && endsToEat > 0) {
valueChar = value[ref.i];
if (valueChar == '"')
withinQuotes = !withinQuotes;
if (withinQuotes)
continue;
if (valueChar == '[')
endsToEat++;
if (valueChar == ']')
endsToEat--;
}
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
//Is Type/Map, i.e. {...}
if (valueChar == '{') {
var endsToEat = 1;
while (++ref.i < valueLength && endsToEat > 0) {
valueChar = value[ref.i];
if (valueChar == '"')
withinQuotes = !withinQuotes;
if (withinQuotes)
continue;
if (valueChar == '{')
endsToEat++;
if (valueChar == '}')
endsToEat--;
}
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
//Is Within Quotes, i.e. "..."
if (valueChar == '"') {
while (++ref.i < valueLength) {
valueChar = value[ref.i];
if (valueChar != '"') continue;
var isLiteralQuote = ref.i + 1 < valueLength && value[ref.i + 1] == '"';
ref.i++; //skip quote
if (!isLiteralQuote)
break;
}
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
//Is Value
while (++ref.i < valueLength) {
valueChar = value[ref.i];
if (valueChar == ',' || valueChar == '}')
break;
}
return value.substr(tokenStartPos, ref.i - tokenStartPos);
}
JSV.isEmpty_ = function (a) {
return (a === null || a === undefined || a === "");
}
JSV.isFunction_ = function (a) {
return (typeof (a) === 'function') ? a.constructor.toString().match(/Function/) !== null : false;
};
JSV.isString_ = function (a) {
if (a === null || a === undefined) return false;
return (typeof (a) === 'string') ? true : (typeof (a) === 'object') ? a.constructor.toString().match(/string/i) !== null : false;
};
JSV.isDate_ = function (a) {
if (JSV.isEmpty_(a)) return false;
return (typeof (a) === 'date') ? true : (typeof (a) === 'object') ? a.constructor.toString().match(/date/i) !== null : false;
};
JSV.isArray_ = function (a) {
if (a === null || a === undefined || a === "") return false;
return (typeof (a) === 'object') ? a.constructor.toString().match(/array/i) !== null || a.length !== undefined : false;
};
JSV.toXsdDateTime = function (date) {
function pad(n) {
var s = n.toString();
return s.length < 2 ? '0' + s : s;
};
var yyyy = date.getUTCFullYear();
var MM = pad(date.getUTCMonth() + 1);
var dd = pad(date.getUTCDate());
var hh = pad(date.getUTCHours());
var mm = pad(date.getUTCMinutes());
var ss = pad(date.getUTCSeconds());
var ms = pad(date.getUTCMilliseconds());
return yyyy + '-' + MM + '-' + dd + 'T' + hh + ':' + mm + ':' + ss + '.' + ms + 'Z';
}
JSV.serialize = JSV.stringify = function (obj) {
if (obj === null || obj === undefined) return null;
var typeOf = typeof (obj);
if (obj === 'function') return null;
if (typeOf === 'object') {
var ctorStr = obj.constructor.toString().toLowerCase();
if (ctorStr.indexOf('string') != -1)
return JSV.escapeString(obj);
if (ctorStr.indexOf('boolean') != -1)
return obj ? "True" : "False";
if (ctorStr.indexOf('number') != -1)
return obj;
if (ctorStr.indexOf('date') != -1)
return JSV.escapeString(JSV.toXsdDateTime(obj));
if (ctorStr.indexOf('array') != -1)
return JSV.serializeArray(obj);
return JSV.serializeObject(obj);
}
else {
switch (typeOf) {
case 'string':
return JSV.escapeString(obj);
break;
case 'boolean':
return obj ? "True" : "False";
break;
case 'date':
return JSV.escapeString(JSV.toXsdDateTime(obj));
break;
case 'array':
return JSV.serializeArray(obj);
break;
case 'number':
default:
return obj;
}
}
};
JSV.serializeObject = function (obj) {
var value, sb = new StringBuffer();
for (var key in obj) {
value = obj[key];
if (!obj.hasOwnProperty(key) || JSV.isEmpty_(value) || JSV.isFunction_(value)) continue;
if (sb.getLength() > 0)
sb.append(',');
sb.append(JSV.escapeString(key));
sb.append(':');
sb.append(JSV.serialize(value));
}
return '{' + sb.toString() + '}';
};
JSV.serializeArray = function (array) {
var value, sb = new StringBuffer();
for (var i = 0, len = array.length; i < len; i++) {
value = array[i];
if (JSV.isEmpty_(value) || JSV.isFunction_(value)) continue;
if (sb.getLength() > 0)
sb.append(',');
sb.append(JSV.serialize(value));
}
return '[' + sb.toString() + ']';
};
JSV.escapeString = function (str) {
if (str === undefined || str === null) return null;
if (str === '') return '""';
if (str.indexOf('"')) {
str = str.replace(/"/g, '""');
}
if (JSV.containsAny_(str, JSV.ESCAPE_CHARS)) {
return '"' + str + '"';
}
return str;
};
JSV.containsAny_ = function (str, tests) {
if (!JSV.isString_(str)) return;
for (var i = 0, len = tests.length; i < len; i++) {
if (str.indexOf(tests[i]) != -1) return true;
}
return false;
};
/* Closure Library StringBuffer for efficient string concatenation */
var hasScriptEngine = 'ScriptEngine' in window;
var HAS_JSCRIPT = hasScriptEngine && window['ScriptEngine']() == 'JScript';
StringBuffer = function (opt_a1, var_args) {
this.buffer_ = HAS_JSCRIPT ? [] : '';
if (opt_a1 != null) {
this.append.apply(this, arguments);
}
};
StringBuffer.prototype.set = function (s) {
this.clear();
this.append(s);
};
if (HAS_JSCRIPT) {
StringBuffer.prototype.bufferLength_ = 0;
StringBuffer.prototype.append = function (a1, opt_a2, var_args) {
// IE version.
if (opt_a2 == null) { // second argument is undefined (null == undefined)
// Array assignment is 2x faster than Array push. Also, use a1
// directly to avoid arguments instantiation, another 2x improvement.
this.buffer_[this.bufferLength_++] = a1;
} else {
this.buffer_.push.apply(/** @type {Array} */(this.buffer_), arguments);
this.bufferLength_ = this.buffer_.length;
}
return this;
};
} else {
StringBuffer.prototype.append = function (a1, opt_a2, var_args) {
// W3 version.
this.buffer_ += a1;
if (opt_a2 != null) { // second argument is undefined (null == undefined)
for (var i = 1; i < arguments.length; i++) {
this.buffer_ += arguments[i];
}
}
return this;
};
}
StringBuffer.prototype.clear = function () {
if (HAS_JSCRIPT) {
this.buffer_.length = 0; // Reuse the array to avoid creating new object.
this.bufferLength_ = 0;
} else {
this.buffer_ = '';
}
};
StringBuffer.prototype.getLength = function () {
return this.toString().length;
};
StringBuffer.prototype.toString = function () {
if (HAS_JSCRIPT) {
var str = this.buffer_.join('');
this.clear();
if (str) {
this.append(str);
}
return str;
} else {
return /** @type {string} */ (this.buffer_);
}
};
})();