/* Copyright (c) 2013, Rodrigo González, Sapienlab All Rights Reserved. Available via MIT LICENSE. See https://github.com/roro89/jsonpack/blob/master/LICENSE.md for details. */ (function(define) { define([], function() { var TOKEN_TRUE = -1; var TOKEN_FALSE = -2; var TOKEN_NULL = -3; var TOKEN_EMPTY_STRING = -4; var TOKEN_UNDEFINED = -5; var pack = function(json, options) { // Canonizes the options options = options || {}; // A shorthand for debugging var verbose = options.verbose || false; verbose && console.log('Normalize the JSON Object'); // JSON as Javascript Object (Not string representation) json = typeof json === 'string' ? this.JSON.parse(json) : json; verbose && console.log('Creating a empty dictionary'); // The dictionary var dictionary = { strings : [], integers : [], floats : [] }; verbose && console.log('Creating the AST'); // The AST var ast = (function recursiveAstBuilder(item) { verbose && console.log('Calling recursiveAstBuilder with ' + this.JSON.stringify(item)); // The type of the item var type = typeof item; // Case 7: The item is null if (item === null) { return { type : 'null', index : TOKEN_NULL }; } //add undefined if (typeof item === 'undefined') { return { type : 'undefined', index : TOKEN_UNDEFINED }; } // Case 1: The item is Array Object if ( item instanceof Array) { // Create a new sub-AST of type Array (@) var ast = ['@']; // Add each items for (var i in item) { if (!item.hasOwnProperty(i)) continue; ast.push(recursiveAstBuilder(item[i])); } // And return return ast; } // Case 2: The item is Object if (type === 'object') { // Create a new sub-AST of type Object ($) var ast = ['$']; // Add each items for (var key in item) { if (!item.hasOwnProperty(key)) continue; ast.push(recursiveAstBuilder(key)); ast.push(recursiveAstBuilder(item[key])); } // And return return ast; } // Case 3: The item empty string if (item === '') { return { type : 'empty', index : TOKEN_EMPTY_STRING }; } // Case 4: The item is String if (type === 'string') { // The index of that word in the dictionary var index = _indexOf.call(dictionary.strings, item); // If not, add to the dictionary and actualize the index if (index == -1) { dictionary.strings.push(_encode(item)); index = dictionary.strings.length - 1; } // Return the token return { type : 'strings', index : index }; } // Case 5: The item is integer if (type === 'number' && item % 1 === 0) { // The index of that number in the dictionary var index = _indexOf.call(dictionary.integers, item); // If not, add to the dictionary and actualize the index if (index == -1) { dictionary.integers.push(_base10To36(item)); index = dictionary.integers.length - 1; } // Return the token return { type : 'integers', index : index }; } // Case 6: The item is float if (type === 'number') { // The index of that number in the dictionary var index = _indexOf.call(dictionary.floats, item); // If not, add to the dictionary and actualize the index if (index == -1) { // Float not use base 36 dictionary.floats.push(item); index = dictionary.floats.length - 1; } // Return the token return { type : 'floats', index : index }; } // Case 7: The item is boolean if (type === 'boolean') { return { type : 'boolean', index : item ? TOKEN_TRUE : TOKEN_FALSE }; } // Default throw new Error('Unexpected argument of type ' + typeof (item)); })(json); // A set of shorthands proxies for the length of the dictionaries var stringLength = dictionary.strings.length; var integerLength = dictionary.integers.length; var floatLength = dictionary.floats.length; verbose && console.log('Parsing the dictionary'); // Create a raw dictionary var packed = dictionary.strings.join('|'); packed += '^' + dictionary.integers.join('|'); packed += '^' + dictionary.floats.join('|'); verbose && console.log('Parsing the structure'); // And add the structure packed += '^' + (function recursiveParser(item) { verbose && console.log('Calling a recursiveParser with ' + this.JSON.stringify(item)); // If the item is Array, then is a object of // type [object Object] or [object Array] if ( item instanceof Array) { // The packed resulting var packed = item.shift(); for (var i in item) { if (!item.hasOwnProperty(i)) continue; packed += recursiveParser(item[i]) + '|'; } return (packed[packed.length - 1] === '|' ? packed.slice(0, -1) : packed) + ']'; } // A shorthand proxies var type = item.type, index = item.index; if (type === 'strings') { // Just return the base 36 of index return _base10To36(index); } if (type === 'integers') { // Return a base 36 of index plus stringLength offset return _base10To36(stringLength + index); } if (type === 'floats') { // Return a base 36 of index plus stringLength and integerLength offset return _base10To36(stringLength + integerLength + index); } if (type === 'boolean') { return item.index; } if (type === 'null') { return TOKEN_NULL; } if (type === 'undefined') { return TOKEN_UNDEFINED; } if (type === 'empty') { return TOKEN_EMPTY_STRING; } throw new TypeError('The item is alien!'); })(ast); verbose && console.log('Ending parser'); // If debug, return a internal representation of dictionary and stuff if (options.debug) return { dictionary : dictionary, ast : ast, packed : packed }; return packed; }; var unpack = function(packed, options) { // Canonizes the options options = options || {}; // A raw buffer var rawBuffers = packed.split('^'); // Create a dictionary options.verbose && console.log('Building dictionary'); var dictionary = []; // Add the strings values var buffer = rawBuffers[0]; if (buffer !== '') { buffer = buffer.split('|'); options.verbose && console.log('Parse the strings dictionary'); for (var i=0, n=buffer.length; i