// Copyright 2014 Cognitect. All Rights Reserved. // // 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. "use strict"; goog.provide("com.cognitect.transit"); goog.require("com.cognitect.transit.impl.reader"); goog.require("com.cognitect.transit.impl.writer"); goog.require("com.cognitect.transit.types"); goog.require("com.cognitect.transit.eq"); goog.require("com.cognitect.transit.impl.decoder"); goog.require("com.cognitect.transit.caching"); /** @define {boolean} */ var TRANSIT_DEV = true; /** @define {boolean} */ var TRANSIT_NODE_TARGET = false; /** @define {boolean} */ var TRANSIT_BROWSER_TARGET = false; /** @define {boolean} */ var TRANSIT_BROWSER_AMD_TARGET = false; goog.scope(function() { /** * @class transit */ var transit = com.cognitect.transit; var reader = com.cognitect.transit.impl.reader, writer = com.cognitect.transit.impl.writer, decoder = com.cognitect.transit.impl.decoder, types = com.cognitect.transit.types, eq = com.cognitect.transit.eq, caching = com.cognitect.transit.caching; /** * Create a transit reader instance. * @method transit.reader * @param {string|null} type type of reader to construct. * Default to "json". For verbose mode supply "json-verbose". * @param {Object|null} opts reader options. A JavaScript object to * customize the writer Valid entries include "defaultHandler", * and "handler". "defaultHandler" should be JavaScript function * taking two arguments, the first is the tag, the second the * value. "handlers" should be an object of tags to handle. The * values are functions that will receive the value of matched * tag. "preferBuffers" may be supplied to customize binary * decoding. If available binary data will read as Node.js Buffers, * If Buffer is not available or "prefersBuffers" is set to false * data will be read as Uint8Array. If neither Buffer nor Uint8Array is * available - defaults to tagged value that simply wraps the * base64 encoded string. * @return {transit.reader} A transit reader. * @example * var r = transit.reader("json", { * handlers: { * "point": function(v) { return new Point(v[0], v[1]); } * } * }); */ transit.reader = function(type, opts) { if(type === "json" || type === "json-verbose" || type == null) { type = "json"; var unmarshaller = new reader.JSONUnmarshaller(opts); return new reader.Reader(unmarshaller, opts); } else { throw new Error("Cannot create reader of type " + type); } }; /** * Create a transit writer instance. * @method transit.writer * @param {String|null} type type of writer to construct. * Defaults to "json". For verbose mode supply "json-verbose". * @param {Object|null} opts writer options. A JavaScript object to * customize the writer. "handlers" options, a transit.map of * JavaScript constructor and transit writer handler instance * entries. "handlerForForeign" option, for dealing with values * from other JavaScript contexts. This function will be passed * the unmatchable value and the installed handlers. The function * should return the correct handler. Note if this function is * provided, special handling for Objects will also be * auto-installed to catch plain Objects from the foreign context. * @return {transit.writer} A transit writer. * @example * var r = transit.writer("json", { * handlers: transit.map([ * Point, PointHandler * ]) * }); */ transit.writer = function(type, opts) { if(type === "json" || type === "json-verbose" || type == null) { if(type === "json-verbose") { if(opts == null) opts = {}; opts["verbose"] = true; } type = "json"; var marshaller = new writer.JSONMarshaller(opts); return new writer.Writer(marshaller, opts); } else { var err = new Error("Type must be \"json\""); err.data = {type: type}; throw err; } }; /** * Create a transit writer handler. * @method transit.makeWriteHandler * @param {Object} obj An object containing 3 functions, tag, rep and stringRep. * "tag" should return a string representing the tag to be written on the wire. * "rep" should return the representation on the wire. "stringRep" is should return * the string representation of the value. Optional "getVerboseHandler" should return a * handler for writing verbose output. * @return {transit.handler} A transit write handler. * @example * var PointHandler = transit.makeWriteHandler({ * tag: "point", * rep: function(p) { return [p.x, p.y]; }, * stringRep: function(p) { return null; } * }); */ transit.makeWriteHandler = function(obj) { var Handler = function(){}; Handler.prototype.tag = obj["tag"]; Handler.prototype.rep = obj["rep"]; Handler.prototype.stringRep = obj["stringRep"]; Handler.prototype.getVerboseHandler = obj["getVerboseHandler"]; return new Handler(); }; transit.makeBuilder = function(obj) { var Builder = function(){}; Builder.prototype.init = obj["init"]; Builder.prototype.add = obj["add"]; Builder.prototype.finalize = obj["finalize"]; Builder.prototype.fromArray = obj["fromArray"]; return new Builder(); }; /** * Create a transit date. * @method transit.date * @param {Number|String} A number or string representing milliseconds since epoch. * @return {Date} A JavaScript Date. */ transit.date = types.date; /** * Create an integer. If given a transit integer or a JavaScript * number will simply return that value. Given a string will * return a JavaScript number if the string represents an integer * value in the 53bit range and a transit integer otherwise. * @method transit.integer * @param {Number|String|integer} s A value representing an integer. * @return {transit.integer} A JavaScript number or transit integer. */ transit.integer = types.intValue; /** * Test if an object is a transit integer. Will return true if argument * is a 64 bit integer or a JavaScript number that has an interpretation as * an integer value, i.e. parseFloat(n) === parseInt(n) * @method transit.isInteger * @param {Object} x Any JavaScript value. * @return {Boolean} true if the value is a transit integer, false otherwise. */ transit.isInteger = types.isInteger; /** * Create transit UUID from high and low 64 bits. These integer values * can be constructed with transit.integer. * @method transit.uuid * @param {transit.integer} high The high 64 bits. * @param {transit.integer} low The low 64 bits. * @return {transit.uuid} A transit UUID. */ transit.uuid = types.uuid; /** * Test if an object is a transit UUID. * @method transit.isUUID * @param {Object} x Any JavaScript value. * @return {Boolean} true if the vlaue is a transit UUID instance, false otherwise. */ transit.isUUID = types.isUUID; /** * Create a transit big integer. * @method transit.bigInt * @param {String} s A string representing an arbitrary size integer value. * @return {transit.bigdec} A transit big integer. */ transit.bigInt = types.bigInteger; /** * Test if an object is a transit big integer. * @method transit.isBigInt * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit big integer, false otherwise. */ transit.isBigInt = types.isBigInteger; /** * Create a transit big decimal. * @method transit.bigDec * @param {String} s A string representing an arbitrary precisions decimal value. * @return {transit.bigdec} A transit big decimal. */ transit.bigDec = types.bigDecimalValue; /** * Test if an object is a transit big decimal. * @method transit.isBigDec * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit big decimal, false otherwise. */ transit.isBigDec = types.isBigDecimal; /** * Create transit keyword. * @method transit.keyword * @param {String} name A string. * @return {transit.keyword} A transit keyword. * @example * transit.keyword("foo"); */ transit.keyword = types.keyword; /** * Test if an object is a transit keyword. * @method transit.isKeyword * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit keyword, false otherwise. */ transit.isKeyword = types.isKeyword; /** * Create a transit symbol. * @method transit.symbol * @param {s} name A string. * @return {transit.symbol} A transit symbol instance. * @example * transit.symbol("foo"); */ transit.symbol = types.symbol; /** * Test if an object is a transit symbol * @method transit.isSymbol * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit symbol, false otherwise. */ transit.isSymbol = types.isSymbol; /** * Create transit binary blob. * @method transit.binary * @param {String} s A base64 encoded string. * @return {transit.binary} A transit binary blob instance. */ transit.binary = types.binary; /** * Test if an object is a transit binary blob. * @method transit.isBinary * @param {Object} x Any JavaScript value. */ transit.isBinary = types.isBinary; /** * Create a transit URI. * @method transit.uri * @param {String} A string representing a valid URI. * @return {transit.uri} A transit URI. */ transit.uri = types.uri; /** * Test if an object is a transit URI. * @method transit.isURI * @param {Object} Any JavaScript value. * @return {Boolean} true if x is a transit symbol, false otherwise. */ transit.isURI = types.isURI; /** * Create a transit hash map. Transit maps satisfy the current version * of the ECMAScript 6 Map specification. * @method transit.map * @param {Array} xs A JavaScript array of alternating key value pairs. * @return {transit.map} A transit map. * @example * transit.map([new Date(), "foo", [1,2], 3]); */ transit.map = types.map; /** * Test if an object is a transit map. * @method transit.isMap * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit map, false otherwise. */ transit.isMap = types.isMap; /** * Create a transit set. Transit sets satisfy the current version of the * of the ECMAScript 6 Set specification. * @method transit.set * @param {Array} xs A JavaScript array of values. * @return {transit.set} A transit set. * @example * transit.set(["foo", [1,2], 3, {bar: "baz"}]); */ transit.set = types.set; /** * Test if an object is a transit set. * @method transit.isSet * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit set, false otherwise. */ transit.isSet = types.isSet; /** * Create a transit list. * @method transit.list * @param {Array} A JavaScript array. * @return {transit.list} A transit list. */ transit.list = types.list; /** * Test if an object is a transit list. * @method transit.isList * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit list, false otherwise. */ transit.isList = types.isList; /** * Create a transit quoted value. * @method transit.quoted * @param {Object} x Any JavaScript value. * @return {transit.quoted} A transit quoted value. */ transit.quoted = types.quoted; /** * Test if an object is a transit quoted value. * @method transit.isQuoted * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit value, false otherwise. */ transit.isQuoted = types.isQuoted; /** * Create a transit tagged value. * @method transit.tagged * @param {String} tag A tag. * @param {Object} value A JavaScrpt array, object, or string. * @return {transit.tagged} A transit tagged value. * @example * transit.tagged("point", new Point(1,2)); */ transit.tagged = types.taggedValue; /** * Test if an object is a transit tagged value. * @method transit.isTaggedValue * @param {Object} x Any JavaScript value. * @return {Boolean} true if x is a transit value, false otherwise. */ transit.isTaggedValue = types.isTaggedValue; /** * Create a transit link. * @method transit.link * @param {transit.map} A transit map which must contain at a * minimum the following keys: href, rel. It may optionally include * name, render, and prompt. href must be a transit.uri, all other * values are strings, and render must be either "image" or "link". * @return {Object} A transit link. */ transit.link = types.link; /** * Test if an object is a transit link. * @method transit.isLink * @param {Object} x Any JavaScript object. * @return {Boolean} true if x is a transit link, false otherwise. */ transit.isLink = types.isLink; /** * Compute the hashCode for any JavaScript object that has been * extend to transit's equality and hashing protocol. JavaScript * primitives and transit value are already extended to this protocol. * Custom types may be extended to the protocol via transit.extenToEQ. * @method transit.hash * @param {Object} x Any JavaScript object that has been extended to * transit's equality and hashing protocol. * @return {Number} Returns JavaScript number - semantically a 32bit integer. */ transit.hash = eq.hashCode; /** * Compute the hashCode for JavaScript map-like types - either a JavaScript * object or a JavaScript object that implements ES6 Map forEach. * @method transit.hashMapLike * @param {Object} x A plain JavaScript Object or Object that implements * ES6 Map forEach. * @return {Number} Returns JavaScript number - semantically a 32bit integer. */ transit.hashMapLike = eq.hashMapLike; /** * Compute the hashCode for JavaScript array-like types - either a JavaScript * array or a JavaScript object that implements Array forEach. * @method transit.hashArrayLike * @param {Object} x A JavaScript Array or Object that implements * Array forEach. * @return {Number} Returns JavaScript number - semantically a 32bit integer. */ transit.hashMapLike = eq.hashArrayLike; /** * Test whether two JavaScript objects represent equal values. The * objects to be tested should be extended to transit's equality * and hasing protocol. JavaScript natives and transit value have * already been extended to the protocol, including objects and * arrays. Also transit maps and JavaScript objects may be * compared for equality. Custom types may be extended via * transit.extendToEQ. * @param {Object} x A JavaScript object * @param {Object} y A JavaScript object * @return {Boolean} true if the x and y are equal, false otherwise. */ transit.equals = eq.equals; /** * Extend an object to hashing and equality required by * transit maps and sets. Only required for custom * types, JavaScript primitive types and transit * types are handled. * @method transit.extendToEQ * @param {Object} x A JavaScript object, will be mutated. * @return {Object} x * @example * transit.extendToEq(Point.protototype, { * hashCode: function() { * var bits = (this.x | 0) ^ ((this.y | 0) * 31); * return bits ^ (bits >>> 32); * }, * equals: function(other) { * return this.x == other.x && this.y == other.y; * } * }); */ transit.extendToEQ = eq.extendToEQ; /** * Convert a transit map instance into a JavaScript Object. * Throws if the map has keys which have no string representation. * @method transit.mapToObject * @param {transit.map} m a transit map * @return {Object} a JavaScript Object */ transit.mapToObject = function(m) { var ret = {}; m.forEach(function(v, k) { if(typeof k !== "string") { throw Error("Cannot convert map with non-string keys"); } else { ret[k] = v; } }) return ret; }; /** * Construct a Transit JSON decoder. * @method transit.decoder * @param {Object} options to the decoder. Can include map of * handlers. * @return {transit.decoder} a Transit JSON decoder * @example * var decoder = transit.decoder(); * var x = decoder.decode(json, transit.readCache()); */ transit.decoder = decoder.decoder; /** * Construct a Transit read cache * @method transit.readCache * @return {transit.readCAche} a Transit read cache */ transit.readCache = caching.readCache; /** * Construct a Transit write cache * @method transit.writeCache * @return {transit.writeCache} a Transit write cache */ transit.writeCache = caching.writeCache; transit.UUIDfromString = types.UUIDfromString; transit.randomUUID = types.randomUUID; transit.stringableKeys = writer.stringableKeys; if(TRANSIT_BROWSER_TARGET) { goog.exportSymbol("transit.reader", transit.reader); goog.exportSymbol("transit.writer", transit.writer); goog.exportSymbol("transit.makeBuilder", transit.makeBuilder); goog.exportSymbol("transit.makeWriteHandler", transit.makeWriteHandler); goog.exportSymbol("transit.date", types.date); goog.exportSymbol("transit.integer", types.intValue); goog.exportSymbol("transit.isInteger", types.isInteger); goog.exportSymbol("transit.uuid", types.uuid); goog.exportSymbol("transit.isUUID", types.isUUID); goog.exportSymbol("transit.bigInt", types.bigInteger); goog.exportSymbol("transit.isBigInt", types.isBigInteger); goog.exportSymbol("transit.bigDec", types.bigDecimalValue); goog.exportSymbol("transit.isBigDec", types.isBigDecimal); goog.exportSymbol("transit.keyword", types.keyword); goog.exportSymbol("transit.isKeyword", types.isKeyword); goog.exportSymbol("transit.symbol", types.symbol); goog.exportSymbol("transit.isSymbol", types.isSymbol); goog.exportSymbol("transit.binary", types.binary); goog.exportSymbol("transit.isBinary", types.isBinary); goog.exportSymbol("transit.uri", types.uri); goog.exportSymbol("transit.isURI", types.isURI); goog.exportSymbol("transit.map", types.map); goog.exportSymbol("transit.isMap", types.isMap); goog.exportSymbol("transit.set", types.set); goog.exportSymbol("transit.isSet", types.isSet); goog.exportSymbol("transit.list", types.list); goog.exportSymbol("transit.isList", types.isList); goog.exportSymbol("transit.quoted", types.quoted); goog.exportSymbol("transit.isQuoted", types.isQuoted); goog.exportSymbol("transit.tagged", types.taggedValue); goog.exportSymbol("transit.isTaggedValue", types.isTaggedValue); goog.exportSymbol("transit.link", types.link); goog.exportSymbol("transit.isLink", types.isLink); goog.exportSymbol("transit.hash", eq.hashCode); goog.exportSymbol("transit.hashMapLike", eq.hashMapLike); goog.exportSymbol("transit.hashArrayLike", eq.hashArrayLike); goog.exportSymbol("transit.equals", eq.equals); goog.exportSymbol("transit.extendToEQ", eq.extendToEQ); goog.exportSymbol("transit.mapToObject", transit.mapToObject); goog.exportSymbol("transit.decoder", decoder.decoder); goog.exportSymbol("transit.UUIDfromString", types.UUIDfromString); goog.exportSymbol("transit.randomUUID", types.randomUUID); goog.exportSymbol("transit.stringableKeys", writer.stringableKeys); goog.exportSymbol("transit.readCache", caching.readCache); goog.exportSymbol("transit.writeCache", caching.writeCache); } if(TRANSIT_NODE_TARGET) { module.exports = { reader: transit.reader, writer: transit.writer, makeBuilder: transit.makeBuilder, makeWriteHandler: transit.makeWriteHandler, date: types.date, integer: types.intValue, isInteger: types.isInteger, uuid: types.uuid, isUUID: types.isUUID, bigInt: types.bigInteger, isBigInt: types.isBigInteger, bigDec: types.bigDecimalValue, isBigDec: types.isBigDecimal, keyword: types.keyword, isKeyword: types.isKeyword, symbol: types.symbol, isSymbol: types.isSymbol, binary: types.binary, isBinary: types.isBinary, uri: types.uri, isURI: types.isURI, map: types.map, isMap: types.isMap, set: types.set, isSet: types.isSet, list: types.list, isList: types.isList, quoted: types.quoted, isQuoted: types.isQuoted, tagged: types.taggedValue, isTaggedValue: types.isTaggedValue, link: types.link, isLink: types.isLink, hash: eq.hashCode, hashArrayLike: eq.hashArrayLike, hashMapLike: eq.hashMapLike, equals: eq.equals, extendToEQ: eq.extendToEQ, mapToObject: transit.mapToObject, decoder: decoder.decoder, UUIDfromString: types.UUIDfromString, randomUUID: types.randomUUID, stringableKeys: writer.stringableKeys, readCache: caching.readCache, writeCache: caching.writeCache }; } });