Permalink
Browse files

added Monkey PubNub API Lib by http://www.skn3.com/ Jonathan Pittock.

  • Loading branch information...
1 parent 1e1e40f commit 903c3173f4d75782905f2ddb1957408d5a31b3b2 @stephenlb stephenlb committed Mar 6, 2012
View
31 monkey/README.md
@@ -0,0 +1,31 @@
+# Monkey Multiplayer
+
+Following from my previous thread where someone was talking about networking in monkey,
+I spent a little time today getting pubnub working in html5 and flash!
+
+###### See the demo here:
+[Monkey Multiplayer Gaming](http://www.skn3.com/junk/pubnub/MonkeyGameHtml.html)
+
+###### Forum Link:
+[Monkey Forum Discussions](http://monkeycoder.co.nz/Community/posts.php?topic=2397)
+
+It was incredibly straight forward,
+and doesn't require anything special to be done
+to your project files.
+The required pubnub source and dom
+elements are attached at runtime.
+
+You can download the example + module here:
+[Code Download](https://github.com/pubnub/pubnub-api/tree/master/monkey)
+
+#### [update]
+
+I tweaked the demo to use hard coded font colors
+(seperate font for each color) and not apply any SetColor.
+It is much faster now.
+
+#### [update again]
+
+I have now added a flash target to the module!
+You can check the example and switch between
+html5 and flash versions.
View
488 monkey/native/flash/PubNub.as
@@ -0,0 +1,488 @@
+import flash.events.Event;
+import flash.events.EventDispatcher;
+import flash.events.IOErrorEvent;
+import flash.events.SecurityErrorEvent;
+import flash.events.TimerEvent;
+import flash.net.URLLoader;
+import flash.net.URLRequest;
+import flash.utils.SetIntervalTimer;
+import flash.utils.Timer;
+
+/**
+ * PubNub Static Class
+ *
+ * This should allow creating threads of listeners to each individual channel
+ *
+ * @author Fan
+ */
+class PubNub extends EventDispatcher
+{
+ public static var LIMIT:int = 1700;
+ private static var INSTANCE:PubNub;
+ private static const ALPHA_CHAR_CODES:Array = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70];
+
+ public var initialized:Boolean = false;
+ public var origin:String = "http://pubsub.pubnub.com";
+ public var ssl:Boolean = false;
+ public var interval:Number = 10;
+
+ private var publish_key:String = "demo";
+ private var sub_key:String = "demo";
+ private var secret_key:String = null;
+ private var subscriptions:Array;
+
+ private var start_time_token:Number = 0;
+ private var queue:Array;
+
+ public function PubNub(enforcer:SingletonEnforcer)
+ {
+
+ }
+
+ public static function getInstance():PubNub
+ {
+ if (!INSTANCE) {
+ INSTANCE = new PubNub(new SingletonEnforcer());
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * ??
+ * Hash Array
+ * origin = https:// or http://
+ * @param config
+ */
+ public function init(config:Object):void
+ {
+ if(config.ssl && config.origin) {
+ origin = "https://" + config.origin;
+ } else if (config.origin) {
+ origin = "http://" + config.origin;
+ }
+
+ if(config.publish_key) {
+ publish_key = config.publish_key;
+ }
+
+ if(config.sub_key) {
+ sub_key = config.sub_key;
+ }
+
+ if(config.secret_key) {
+ secret_key = config.secret_key;
+ }
+
+ if (config.push_interval) {
+ interval = config.push_interval;
+ }
+
+ queue = [];
+ subscriptions = [];
+
+ function timeHandler( evt:Event ):void {
+ //trace("[PubNub] Subscription Handler Returned");
+ //var myUID:String = uid;
+ var node:Object = queue["init"];
+ var loader:URLLoader = node.loader;
+ if ( evt.type == Event.COMPLETE ) {
+ try {
+ var result:Object = JSON.decode(loader.data);
+ start_time_token = result[0];
+ trace("[PubNub] init complete: " + start_time_token);
+ } catch (e:*) {
+ trace("[PubNub] Bad JSON Content");
+ }
+ initialized = true;
+ dispatchEvent(new PubNubEvent(PubNubEvent.INIT));
+ }
+ }
+ var url:String = origin + "/" + "time" + "/" + 0;
+ // Loads Time Token
+ _request( { url:url, channel:"system", handler:timeHandler, uid:"init" } );
+ }
+
+ /**
+ * Wrapper for function below
+ * @param args
+ */
+ public static function history(args:Object):void
+ {
+ if (!INSTANCE.initialized) {
+ throw("[PUBNUB] Not initialized yet");
+ }
+ INSTANCE._history(args);
+ }
+
+ /**
+ * get old messages
+ * args: { callback:Function, channel:"String", limit:"Int", }
+ * ????
+ * @param args
+ */
+ public function _history(args:Object):void
+ {
+ var onResult:Function = args.callback || dispatchEvent;
+ var limit:int = args.limit ? args.limit : 100;
+
+ if (!args.channel) {
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:[-1,"Channel Not Given and"], timeout:1000 } ));
+ return;
+ }
+
+ var channel:String = args.channel;
+ var uid:String = _uid();
+ var url:String = origin + "/" + "history" + "/" + sub_key + "/" + _encode(channel) + "/" + 0 + "/" + limit;
+ var time:Number = start_time_token;
+
+ function historyHandler( evt:Event ):void {
+ //trace("[PubNub] History Handler Returned");
+ //var myUID:String = uid;
+ var node:Object = queue[uid];
+ var loader:URLLoader = node.loader;
+ if ( evt.type == Event.COMPLETE ) {
+ try {
+ var messages:Array = JSON.decode(loader.data);
+ if(messages is Array) { onResult(new PubNubEvent(PubNubEvent.HISTORY, { channel:channel, result:[1,messages], timeout:1 } )); }
+ } catch (e:Error) {
+ trace("[PubNub] Bad Data Content Ignored");
+ }
+ } else {
+ onResult(new PubNubEvent(PubNubEvent.HISTORY, { channel:channel, result:[-1,"Connection Issue"], timeout:1000 } ));
+ }
+ node.loader.close();
+ node.loader = null;
+ node.handler = null;
+ }
+
+ _request( { url:url, channel:channel, handler:historyHandler, uid:uid } );
+ }
+
+ /**
+ * Wrapper for function below
+ * @param args
+ */
+ public static function publish(args:Object):void
+ {
+ if (!INSTANCE.initialized) {
+ throw("[PUBNUB] Not initialized yet");
+ }
+ INSTANCE._publish(args);
+ }
+
+ /**
+ * Broadcasts a message
+ * args: { callback:Function, channel:"String", message:"String", }
+ * ????
+ * @param args
+ */
+ public function _publish(args:Object):void
+ {
+ var onResult:Function = args.callback || dispatchEvent;
+
+ if (!args.channel || !args.message) {
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:[-1,"Channel Not Given and/or Message"], timeout:1000 } ));
+ return;
+ }
+
+ var channel:String = args.channel;
+ var message:String = JSON.encode(args.message);
+ var signature:String = "0";
+
+ if (secret_key) {
+ // Create the signature for this message
+ // Using Crypto digest, md5
+ var concat:String = publish_key + "/" + sub_key + "/" + secret_key + "/" + channel + "/" + message;
+ signature = MD5.hash(concat);
+ }
+
+ if (message.length > LIMIT) {
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:[-1,"Message Too Long (" + LIMIT + ")"], timeout:1000 } ));
+ return;
+ }
+
+ var uid:String = _uid();
+
+ function publishHandler( evt:Event ):void {
+ var node:Object = queue[uid];
+ var loader:URLLoader = node.loader;
+ if ( evt.type == Event.COMPLETE ) {
+ try {
+ var result:Object = JSON.decode(loader.data);
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:result, timeout:1 } ));
+ } catch (e:*) {
+ trace("[PubNub] Bad Data Content Ignored");
+ }
+ } else {
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:[-1,"Connection Issue"], timeout:1000 } ));
+ }
+ node.loader.close();
+ node.loader = null;
+ node.handler = null;
+ }
+
+ var url:String = origin + "/" + "publish" + "/" + publish_key + "/" + sub_key + "/" + signature + "/" + _encode(channel) + "/" + 0 + "/" +_encode(message);
+ //trace("[PubNub] publish "+uid+": "+url);
+ _request( { url:url, channel:channel, handler:publishHandler, uid:uid } );
+ }
+
+ /**
+ * Subscription Wrapper
+ * @param args
+ */
+ public static function subscribe(args:Object):void
+ {
+ if (!INSTANCE.initialized) {
+ throw("[PUBNUB] Not initialized yet");
+ }
+ INSTANCE._subscribe(args);
+ }
+
+ /**
+ * Subscribes to a channel
+ * args: { callback:Function, channel:"String" }
+ * @param args
+ */
+ public function _subscribe(args:Object):void
+ {
+ var onResult:Function = args.callback || dispatchEvent;
+
+ if (!args.callback) {
+ throw("[PubNub] Missing Callback Function");
+ return;
+ }
+
+ if (!args.channel) {
+ onResult(new PubNubEvent(PubNubEvent.PUBLISH, { channel:channel, result:[-1,"Channel Not Given"], timeout:1000 } ));
+ return;
+ }
+
+ var channel:String = args.channel;
+
+ var time:Number = start_time_token;
+ if (!subscriptions[channel])
+ {
+ subscriptions[channel] = {}
+ }
+
+ if (subscriptions[channel].connected)
+ {
+ onResult(new PubNubEvent(PubNubEvent.SUBSCRIBE, { channel:channel, result:[-1, "Already Connected"], timeout:1000 } ));
+ return;
+ }
+
+ subscriptions[channel].connected = true;
+ var url:String = origin + "/" + "subscribe" + "/" + sub_key + "/" + _encode(channel) + "/" + 0;
+ var uid:String = _uid();
+
+ function subHandler( evt:Event ):void {
+ //trace("[PubNub] Subscription Handler Returned");
+ //var myUID:String = uid;
+ var node:Object = queue[uid];
+ var loader:URLLoader = node.loader;
+
+ if (!subscriptions[channel].connected) {
+ // Stops the connection or any further listening loops
+ loader.close();
+ delete node.loader;
+ delete node.channel;
+ delete node.uid;
+ delete node.timetoken;
+ delete node.request;
+ delete queue[uid];
+ return;
+ }
+ var timer:Timer;
+
+ if ( evt.type == Event.COMPLETE ) {
+ try {
+ var result:Object = JSON.decode(loader.data);
+ var updatedTime:Number = 0;
+ if(result is Array) {
+ var messages:Array = result[0];
+ if(messages) {
+ for (var i:int = 0; i < messages.length; i++) {
+ onResult(new PubNubEvent(PubNubEvent.SUBSCRIBE, { channel:channel, result:[i+1,messages[i]], timeout:1 } ));
+ }
+ }
+ updatedTime = result[1];
+ //trace(updatedTime)
+ }
+ } catch (e:Error) {
+ trace("[PubNub] Bad Data Content Ignored");
+ }
+
+ node.tries = 0;
+
+ timer = new Timer(interval, 1);
+ timer.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
+ _request({ url:url, channel:channel, handler:subHandler, uid:uid, timetoken:updatedTime });
+ });
+ timer.start();
+ } else {
+ // Possibly Network Issue, then try again after 1 second.
+ onResult(new PubNubEvent(PubNubEvent.SUBSCRIBE, { channel:channel, result:[ -1, "Connection Issue"], timeout:1000 } ));
+ //if (loader.data == null || loader.data == "") {
+ // Problems with the load or empty data
+ node.tries++;
+ //}
+ if (node.tries == 30) {
+ // After 30 tries, seeming the network is now dead after 30 seconds.
+ // Dispatches error event
+ _unsubscribe({ channel:channel, uid:uid });
+ dispatchEvent(new PubNubEvent(PubNubEvent.ERROR, { channel:channel, message:"Channel Dropped" } ));
+ } else {
+ timer = new Timer(1000, 1);
+ timer.addEventListener(TimerEvent.TIMER_COMPLETE, function():void {
+ _request({ url:url, channel:channel, handler:subHandler, uid:uid, timetoken:time });
+ });
+ timer.start();
+ }
+ }
+ }
+
+ _request( { url:url, channel:channel, handler:subHandler, uid:uid, timetoken:time } );
+ }
+
+ /**
+ * UnSubscription Wrapper
+ * @param args
+ */
+ public static function unsubscribe(args:Object):void
+ {
+ if (!INSTANCE.initialized) {
+ throw("[PUBNUB] Not initialized yet");
+ }
+ INSTANCE._unsubscribe(args);
+ }
+
+ /**
+ * UnSubscribes to a channel
+ * args: { callback:Function, channel:"String" }
+ * @param args
+ */
+ public function _unsubscribe(args:Object):void
+ {
+ var onResult:Function = args.callback || dispatchEvent;
+
+ if (!args.channel) {
+ onResult(new PubNubEvent(PubNubEvent.UNSUBSCRIBE, { channel:channel, result:[-1,"Channel Not Given"], timeout:1000 } ));
+ return;
+ }
+ var channel:String = args.channel;
+
+ if (subscriptions[channel] && subscriptions[channel].connected)
+ {
+ subscriptions[channel].connected = false;
+ }
+
+ var event:PubNubEvent = new PubNubEvent(PubNubEvent.UNSUBSCRIBE, { channel:channel, result:[1, "Channel '" + channel + "' Unsubscribed"], timeout:1000 } );
+ onResult(event);
+ }
+
+ /**
+ * Helper Functions
+ * ==============================================
+ */
+
+ /**
+ * Makes a pub nub request
+ * @param args
+ */
+ public function _request(args:Object):void
+ {
+ var node:Object = queue[args.uid] || { tries:0 };
+ var loader:URLLoader = node.loader;
+ var url:String = args.url;
+ if (args.timetoken != null) {
+ url += "/" + args.timetoken;
+ }
+ trace("[PubNub] request: "+url);
+ if (!loader) {
+ node.loader = loader = new URLLoader();
+ loader.addEventListener( Event.COMPLETE, args.handler );
+ loader.addEventListener( IOErrorEvent.IO_ERROR, args.handler );
+ loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, args.handler );
+ node.request = new URLRequest(url);
+ }
+ var r:URLRequest = node.request;
+ r.url = url;
+ loader.load(node.request);
+ node.uid = args.uid;
+ node.channel = args.channel;
+ queue[args.uid] = node;
+ }
+
+ /**
+ * Encodes a string into some format
+ * Should be the escape function
+ * @param args
+ * @return
+ */
+ public function _encode(args:String):String
+ {
+
+ return escape(args);
+ }
+
+ /**
+ * Apply function to all elements in a table
+ * @param f
+ * @param array
+ * @return
+ */
+ public function _map(f:Function, array:Array):Array
+ {
+ return [];
+ }
+
+ public function _uid():String
+ {
+ var uid:Array = new Array(36);
+ var index:int = 0;
+
+ var i:int;
+ var j:int;
+
+ for (i = 0; i < 8; i++)
+ {
+ uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ uid[index++] = 45; // charCode for "-"
+
+ for (j = 0; j < 4; j++)
+ {
+ uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
+ }
+ }
+
+ uid[index++] = 45; // charCode for "-"
+
+ var time:Number = new Date().getTime();
+ // Note: time is the number of milliseconds since 1970,
+ // which is currently more than one trillion.
+ // We use the low 8 hex digits of this number in the UID.
+ // Just in case the system clock has been reset to
+ // Jan 1-4, 1970 (in which case this number could have only
+ // 1-7 hex digits), we pad on the left with 7 zeros
+ // before taking the low digits.
+ var timeString:String = ("0000000" + time.toString(16).toUpperCase()).substr(-8);
+
+ for (i = 0; i < 8; i++)
+ {
+ uid[index++] = timeString.charCodeAt(i);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ uid[index++] = ALPHA_CHAR_CODES[Math.floor(Math.random() * 16)];
+ }
+
+ return String.fromCharCode.apply(null, uid);
+ }
+}
+
+internal class SingletonEnforcer{}
View
33 monkey/native/flash/PubNubEvent.as
@@ -0,0 +1,33 @@
+import flash.events.Event;
+
+/**
+ * Pub Nub Event
+ * @author Fan Di
+ */
+class PubNubEvent extends Event
+{
+ public static const PUBLISH:String = "Publish";
+ public static const SUBSCRIBE:String = "Subscribe";
+ public static const UNSUBSCRIBE:String = "Unsubscribe";
+ public static const HISTORY:String = "History";
+ public static const ERROR:String = "Error";
+ public static const INIT:String = "Init";
+ public var data:Object;
+
+ public function PubNubEvent(type:String, d:Object = null, bubbles:Boolean=false, cancelable:Boolean=false)
+ {
+ data = d;
+ super(type, bubbles, cancelable);
+ }
+
+ public override function clone():Event
+ {
+ return new PubNubEvent(type, data, bubbles, cancelable);
+ }
+
+ public override function toString():String
+ {
+ return formatToString("PubNubEvent", "type", "bubbles", "cancelable", "eventPhase");
+ }
+
+}
View
277 monkey/native/flash/com/adobe/crypto/MD5.as
@@ -0,0 +1,277 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ import flash.utils.ByteArray;
+ /**
+ * The MD5 Message-Digest Algorithm
+ *
+ * Implementation based on algorithm description at
+ * http://www.faqs.org/rfcs/rfc1321.html
+ */
+ class MD5 {
+
+ public static var digest:ByteArray;
+ /**
+ * Performs the MD5 hash algorithm on a string.
+ *
+ * @param s The string to hash
+ * @return A string containing the hash value of s
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+
+ public static function hash(s:String) :String{
+ //Convert to byteArray and send through hashBinary function
+ // so as to only have complex code in one location
+ var ba:ByteArray = new ByteArray();
+ ba.writeUTFBytes(s);
+ return hashBinary(ba);
+ }
+
+ public static function hashBytes(s:ByteArray) :String{
+ return hashBinary(s);
+ }
+
+ /**
+ * Performs the MD5 hash algorithm on a ByteArray.
+ *
+ * @param s The string to hash
+ * @return A string containing the hash value of s
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 8.5
+ * @tiptext
+ */
+ public static function hashBinary( s:ByteArray ):String {
+ // initialize the md buffers
+ var a:int = 1732584193;
+ var b:int = -271733879;
+ var c:int = -1732584194;
+ var d:int = 271733878;
+
+ // variables to store previous values
+ var aa:int;
+ var bb:int;
+ var cc:int;
+ var dd:int;
+
+ // create the blocks from the string and
+ // save the length as a local var to reduce
+ // lookup in the loop below
+ var x:Array = createBlocks( s );
+ var len:int = x.length;
+
+ // loop over all of the blocks
+ for ( var i:int = 0; i < len; i += 16) {
+ // save previous values
+ aa = a;
+ bb = b;
+ cc = c;
+ dd = d;
+
+ // Round 1
+ a = ff( a, b, c, d, x[int(i+ 0)], 7, -680876936 ); // 1
+ d = ff( d, a, b, c, x[int(i+ 1)], 12, -389564586 ); // 2
+ c = ff( c, d, a, b, x[int(i+ 2)], 17, 606105819 ); // 3
+ b = ff( b, c, d, a, x[int(i+ 3)], 22, -1044525330 ); // 4
+ a = ff( a, b, c, d, x[int(i+ 4)], 7, -176418897 ); // 5
+ d = ff( d, a, b, c, x[int(i+ 5)], 12, 1200080426 ); // 6
+ c = ff( c, d, a, b, x[int(i+ 6)], 17, -1473231341 ); // 7
+ b = ff( b, c, d, a, x[int(i+ 7)], 22, -45705983 ); // 8
+ a = ff( a, b, c, d, x[int(i+ 8)], 7, 1770035416 ); // 9
+ d = ff( d, a, b, c, x[int(i+ 9)], 12, -1958414417 ); // 10
+ c = ff( c, d, a, b, x[int(i+10)], 17, -42063 ); // 11
+ b = ff( b, c, d, a, x[int(i+11)], 22, -1990404162 ); // 12
+ a = ff( a, b, c, d, x[int(i+12)], 7, 1804603682 ); // 13
+ d = ff( d, a, b, c, x[int(i+13)], 12, -40341101 ); // 14
+ c = ff( c, d, a, b, x[int(i+14)], 17, -1502002290 ); // 15
+ b = ff( b, c, d, a, x[int(i+15)], 22, 1236535329 ); // 16
+
+ // Round 2
+ a = gg( a, b, c, d, x[int(i+ 1)], 5, -165796510 ); // 17
+ d = gg( d, a, b, c, x[int(i+ 6)], 9, -1069501632 ); // 18
+ c = gg( c, d, a, b, x[int(i+11)], 14, 643717713 ); // 19
+ b = gg( b, c, d, a, x[int(i+ 0)], 20, -373897302 ); // 20
+ a = gg( a, b, c, d, x[int(i+ 5)], 5, -701558691 ); // 21
+ d = gg( d, a, b, c, x[int(i+10)], 9, 38016083 ); // 22
+ c = gg( c, d, a, b, x[int(i+15)], 14, -660478335 ); // 23
+ b = gg( b, c, d, a, x[int(i+ 4)], 20, -405537848 ); // 24
+ a = gg( a, b, c, d, x[int(i+ 9)], 5, 568446438 ); // 25
+ d = gg( d, a, b, c, x[int(i+14)], 9, -1019803690 ); // 26
+ c = gg( c, d, a, b, x[int(i+ 3)], 14, -187363961 ); // 27
+ b = gg( b, c, d, a, x[int(i+ 8)], 20, 1163531501 ); // 28
+ a = gg( a, b, c, d, x[int(i+13)], 5, -1444681467 ); // 29
+ d = gg( d, a, b, c, x[int(i+ 2)], 9, -51403784 ); // 30
+ c = gg( c, d, a, b, x[int(i+ 7)], 14, 1735328473 ); // 31
+ b = gg( b, c, d, a, x[int(i+12)], 20, -1926607734 ); // 32
+
+ // Round 3
+ a = hh( a, b, c, d, x[int(i+ 5)], 4, -378558 ); // 33
+ d = hh( d, a, b, c, x[int(i+ 8)], 11, -2022574463 ); // 34
+ c = hh( c, d, a, b, x[int(i+11)], 16, 1839030562 ); // 35
+ b = hh( b, c, d, a, x[int(i+14)], 23, -35309556 ); // 36
+ a = hh( a, b, c, d, x[int(i+ 1)], 4, -1530992060 ); // 37
+ d = hh( d, a, b, c, x[int(i+ 4)], 11, 1272893353 ); // 38
+ c = hh( c, d, a, b, x[int(i+ 7)], 16, -155497632 ); // 39
+ b = hh( b, c, d, a, x[int(i+10)], 23, -1094730640 ); // 40
+ a = hh( a, b, c, d, x[int(i+13)], 4, 681279174 ); // 41
+ d = hh( d, a, b, c, x[int(i+ 0)], 11, -358537222 ); // 42
+ c = hh( c, d, a, b, x[int(i+ 3)], 16, -722521979 ); // 43
+ b = hh( b, c, d, a, x[int(i+ 6)], 23, 76029189 ); // 44
+ a = hh( a, b, c, d, x[int(i+ 9)], 4, -640364487 ); // 45
+ d = hh( d, a, b, c, x[int(i+12)], 11, -421815835 ); // 46
+ c = hh( c, d, a, b, x[int(i+15)], 16, 530742520 ); // 47
+ b = hh( b, c, d, a, x[int(i+ 2)], 23, -995338651 ); // 48
+
+ // Round 4
+ a = ii( a, b, c, d, x[int(i+ 0)], 6, -198630844 ); // 49
+ d = ii( d, a, b, c, x[int(i+ 7)], 10, 1126891415 ); // 50
+ c = ii( c, d, a, b, x[int(i+14)], 15, -1416354905 ); // 51
+ b = ii( b, c, d, a, x[int(i+ 5)], 21, -57434055 ); // 52
+ a = ii( a, b, c, d, x[int(i+12)], 6, 1700485571 ); // 53
+ d = ii( d, a, b, c, x[int(i+ 3)], 10, -1894986606 ); // 54
+ c = ii( c, d, a, b, x[int(i+10)], 15, -1051523 ); // 55
+ b = ii( b, c, d, a, x[int(i+ 1)], 21, -2054922799 ); // 56
+ a = ii( a, b, c, d, x[int(i+ 8)], 6, 1873313359 ); // 57
+ d = ii( d, a, b, c, x[int(i+15)], 10, -30611744 ); // 58
+ c = ii( c, d, a, b, x[int(i+ 6)], 15, -1560198380 ); // 59
+ b = ii( b, c, d, a, x[int(i+13)], 21, 1309151649 ); // 60
+ a = ii( a, b, c, d, x[int(i+ 4)], 6, -145523070 ); // 61
+ d = ii( d, a, b, c, x[int(i+11)], 10, -1120210379 ); // 62
+ c = ii( c, d, a, b, x[int(i+ 2)], 15, 718787259 ); // 63
+ b = ii( b, c, d, a, x[int(i+ 9)], 21, -343485551 ); // 64
+
+ a += aa;
+ b += bb;
+ c += cc;
+ d += dd;
+ }
+ digest = new ByteArray()
+ digest.writeInt(a);
+ digest.writeInt(b);
+ digest.writeInt(c);
+ digest.writeInt(d);
+ digest.position = 0;
+ // Finish up by concatening the buffers with their hex output
+ return IntUtil.toHex( a ) + IntUtil.toHex( b ) + IntUtil.toHex( c ) + IntUtil.toHex( d );
+ }
+
+ /**
+ * Auxiliary function f as defined in RFC
+ */
+ private static function f( x:int, y:int, z:int ):int {
+ return ( x & y ) | ( (~x) & z );
+ }
+
+ /**
+ * Auxiliary function g as defined in RFC
+ */
+ private static function g( x:int, y:int, z:int ):int {
+ return ( x & z ) | ( y & (~z) );
+ }
+
+ /**
+ * Auxiliary function h as defined in RFC
+ */
+ private static function h( x:int, y:int, z:int ):int {
+ return x ^ y ^ z;
+ }
+
+ /**
+ * Auxiliary function i as defined in RFC
+ */
+ private static function i( x:int, y:int, z:int ):int {
+ return y ^ ( x | (~z) );
+ }
+
+ /**
+ * A generic transformation function. The logic of ff, gg, hh, and
+ * ii are all the same, minus the function used, so pull that logic
+ * out and simplify the method bodies for the transoformation functions.
+ */
+ private static function transform( func:Function, a:int, b:int, c:int, d:int, x:int, s:int, t:int):int {
+ var tmp:int = a + int( func( b, c, d ) ) + x + t;
+ return IntUtil.rol( tmp, s ) + b;
+ }
+
+ /**
+ * ff transformation function
+ */
+ private static function ff ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
+ return transform( f, a, b, c, d, x, s, t );
+ }
+
+ /**
+ * gg transformation function
+ */
+ private static function gg ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
+ return transform( g, a, b, c, d, x, s, t );
+ }
+
+ /**
+ * hh transformation function
+ */
+ private static function hh ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
+ return transform( h, a, b, c, d, x, s, t );
+ }
+
+ /**
+ * ii transformation function
+ */
+ private static function ii ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
+ return transform( i, a, b, c, d, x, s, t );
+ }
+
+ /**
+ * Converts a string to a sequence of 16-word blocks
+ * that we'll do the processing on. Appends padding
+ * and length in the process.
+ *
+ * @param s The string to split into blocks
+ * @return An array containing the blocks that s was
+ * split into.
+ */
+ private static function createBlocks( s:ByteArray ):Array {
+ var blocks:Array = new Array();
+ var len:int = s.length * 8;
+ var mask:int = 0xFF; // ignore hi byte of characters > 0xFF
+ for( var i:int = 0; i < len; i += 8 ) {
+ blocks[ int(i >> 5) ] |= ( s[ i / 8 ] & mask ) << ( i % 32 );
+ }
+
+ // append padding and length
+ blocks[ int(len >> 5) ] |= 0x80 << ( len % 32 );
+ blocks[ int(( ( ( len + 64 ) >>> 9 ) << 4 ) + 14) ] = len;
+ return blocks;
+ }
+
+ }
View
51 monkey/native/flash/com/adobe/net/DynamicURLLoader.as
@@ -0,0 +1,51 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+ import flash.net.URLLoader;
+
+ /**
+ * Class that provides a dynamic implimentation of the URLLoader class.
+ *
+ * This class provides no API implimentations. However, since the class is
+ * declared as dynamic, it can be used in place of URLLoader, and allow
+ * you to dynamically attach properties to it (which URLLoader does not allow).
+ *
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ dynamic class DynamicURLLoader extends URLLoader
+ {
+ public function DynamicURLLoader()
+ {
+ super();
+ }
+ }
View
84 monkey/native/flash/com/adobe/serialization/json/JSON.as
@@ -0,0 +1,84 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+ /**
+ * This class provides encoding and decoding of the JSON format.
+ *
+ * Example usage:
+ * <code>
+ * // create a JSON string from an internal object
+ * JSON.encode( myObject );
+ *
+ * // read a JSON string into an internal object
+ * var myObject:Object = JSON.decode( jsonString );
+ * </code>
+ */
+ class JSON
+ {
+ /**
+ * Encodes a object into a JSON string.
+ *
+ * @param o The object to create a JSON string for
+ * @return the JSON string representing o
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public static function encode( o:Object ):String
+ {
+ return new JSONEncoder( o ).getString();
+ }
+
+ /**
+ * Decodes a JSON string into a native object.
+ *
+ * @param s The JSON string representing the object
+ * @param strict Flag indicating if the decoder should strictly adhere
+ * to the JSON standard or not. The default of <code>true</code>
+ * throws errors if the format does not match the JSON syntax exactly.
+ * Pass <code>false</code> to allow for non-properly-formatted JSON
+ * strings to be decoded with more leniancy.
+ * @return A native object as specified by s
+ * @throw JSONParseError
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public static function decode( s:String, strict:Boolean = true ):*
+ {
+ return new JSONDecoder( s, strict ).getValue();
+ }
+
+ }
+
View
323 monkey/native/flash/com/adobe/serialization/json/JSONDecoder.as
@@ -0,0 +1,323 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ class JSONDecoder
+ {
+
+ /**
+ * Flag indicating if the parser should be strict about the format
+ * of the JSON string it is attempting to decode.
+ */
+ private var strict:Boolean;
+
+ /** The value that will get parsed from the JSON string */
+ private var value:*;
+
+ /** The tokenizer designated to read the JSON string */
+ private var tokenizer:JSONTokenizer;
+
+ /** The current token from the tokenizer */
+ private var token:JSONToken;
+
+ /**
+ * Constructs a new JSONDecoder to parse a JSON string
+ * into a native object.
+ *
+ * @param s The JSON string to be converted
+ * into a native object
+ * @param strict Flag indicating if the JSON string needs to
+ * strictly match the JSON standard or not.
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function JSONDecoder( s:String, strict:Boolean )
+ {
+ this.strict = strict;
+ tokenizer = new JSONTokenizer( s, strict );
+
+ nextToken();
+ value = parseValue();
+
+ // Make sure the input stream is empty
+ if ( strict && nextToken() != null )
+ {
+ tokenizer.parseError( "Unexpected characters left in input stream" );
+ }
+ }
+
+ /**
+ * Gets the internal object that was created by parsing
+ * the JSON string passed to the constructor.
+ *
+ * @return The internal object representation of the JSON
+ * string that was passed to the constructor
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function getValue():*
+ {
+ return value;
+ }
+
+ /**
+ * Returns the next token from the tokenzier reading
+ * the JSON string
+ */
+ private function nextToken():JSONToken
+ {
+ return token = tokenizer.getNextToken();
+ }
+
+ /**
+ * Attempt to parse an array.
+ */
+ private function parseArray():Array
+ {
+ // create an array internally that we're going to attempt
+ // to parse from the tokenizer
+ var a:Array = new Array();
+
+ // grab the next token from the tokenizer to move
+ // past the opening [
+ nextToken();
+
+ // check to see if we have an empty array
+ if ( token.type == JSONTokenType.RIGHT_BRACKET )
+ {
+ // we're done reading the array, so return it
+ return a;
+ }
+ // in non-strict mode an empty array is also a comma
+ // followed by a right bracket
+ else if ( !strict && token.type == JSONTokenType.COMMA )
+ {
+ // move past the comma
+ nextToken();
+
+ // check to see if we're reached the end of the array
+ if ( token.type == JSONTokenType.RIGHT_BRACKET )
+ {
+ return a;
+ }
+ else
+ {
+ tokenizer.parseError( "Leading commas are not supported. Expecting ']' but found " + token.value );
+ }
+ }
+
+ // deal with elements of the array, and use an "infinite"
+ // loop because we could have any amount of elements
+ while ( true )
+ {
+ // read in the value and add it to the array
+ a.push( parseValue() );
+
+ // after the value there should be a ] or a ,
+ nextToken();
+
+ if ( token.type == JSONTokenType.RIGHT_BRACKET )
+ {
+ // we're done reading the array, so return it
+ return a;
+ }
+ else if ( token.type == JSONTokenType.COMMA )
+ {
+ // move past the comma and read another value
+ nextToken();
+
+ // Allow arrays to have a comma after the last element
+ // if the decoder is not in strict mode
+ if ( !strict )
+ {
+ // Reached ",]" as the end of the array, so return it
+ if ( token.type == JSONTokenType.RIGHT_BRACKET )
+ {
+ return a;
+ }
+ }
+ }
+ else
+ {
+ tokenizer.parseError( "Expecting ] or , but found " + token.value );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to parse an object.
+ */
+ private function parseObject():Object
+ {
+ // create the object internally that we're going to
+ // attempt to parse from the tokenizer
+ var o:Object = new Object();
+
+ // store the string part of an object member so
+ // that we can assign it a value in the object
+ var key:String
+
+ // grab the next token from the tokenizer
+ nextToken();
+
+ // check to see if we have an empty object
+ if ( token.type == JSONTokenType.RIGHT_BRACE )
+ {
+ // we're done reading the object, so return it
+ return o;
+ }
+ // in non-strict mode an empty object is also a comma
+ // followed by a right bracket
+ else if ( !strict && token.type == JSONTokenType.COMMA )
+ {
+ // move past the comma
+ nextToken();
+
+ // check to see if we're reached the end of the object
+ if ( token.type == JSONTokenType.RIGHT_BRACE )
+ {
+ return o;
+ }
+ else
+ {
+ tokenizer.parseError( "Leading commas are not supported. Expecting '}' but found " + token.value );
+ }
+ }
+
+ // deal with members of the object, and use an "infinite"
+ // loop because we could have any amount of members
+ while ( true )
+ {
+ if ( token.type == JSONTokenType.STRING )
+ {
+ // the string value we read is the key for the object
+ key = String( token.value );
+
+ // move past the string to see what's next
+ nextToken();
+
+ // after the string there should be a :
+ if ( token.type == JSONTokenType.COLON )
+ {
+ // move past the : and read/assign a value for the key
+ nextToken();
+ o[key] = parseValue();
+
+ // move past the value to see what's next
+ nextToken();
+
+ // after the value there's either a } or a ,
+ if ( token.type == JSONTokenType.RIGHT_BRACE )
+ {
+ // we're done reading the object, so return it
+ return o;
+ }
+ else if ( token.type == JSONTokenType.COMMA )
+ {
+ // skip past the comma and read another member
+ nextToken();
+
+ // Allow objects to have a comma after the last member
+ // if the decoder is not in strict mode
+ if ( !strict )
+ {
+ // Reached ",}" as the end of the object, so return it
+ if ( token.type == JSONTokenType.RIGHT_BRACE )
+ {
+ return o;
+ }
+ }
+ }
+ else
+ {
+ tokenizer.parseError( "Expecting } or , but found " + token.value );
+ }
+ }
+ else
+ {
+ tokenizer.parseError( "Expecting : but found " + token.value );
+ }
+ }
+ else
+ {
+ tokenizer.parseError( "Expecting string but found " + token.value );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to parse a value
+ */
+ private function parseValue():Object
+ {
+ // Catch errors when the input stream ends abruptly
+ if ( token == null )
+ {
+ tokenizer.parseError( "Unexpected end of input" );
+ }
+
+ switch ( token.type )
+ {
+ case JSONTokenType.LEFT_BRACE:
+ return parseObject();
+
+ case JSONTokenType.LEFT_BRACKET:
+ return parseArray();
+
+ case JSONTokenType.STRING:
+ case JSONTokenType.NUMBER:
+ case JSONTokenType.TRUE:
+ case JSONTokenType.FALSE:
+ case JSONTokenType.NULL:
+ return token.value;
+
+ case JSONTokenType.NAN:
+ if ( !strict )
+ {
+ return token.value;
+ }
+ else
+ {
+ tokenizer.parseError( "Unexpected " + token.value );
+ }
+
+ default:
+ tokenizer.parseError( "Unexpected " + token.value );
+
+ }
+
+ return null;
+ }
+ }
View
307 monkey/native/flash/com/adobe/serialization/json/JSONEncoder.as
@@ -0,0 +1,307 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ import flash.utils.describeType;
+
+ class JSONEncoder {
+
+ /** The string that is going to represent the object we're encoding */
+ private var jsonString:String;
+
+ /**
+ * Creates a new JSONEncoder.
+ *
+ * @param o The object to encode as a JSON string
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function JSONEncoder( value:* ) {
+ jsonString = convertToString( value );
+
+ }
+
+ /**
+ * Gets the JSON string from the encoder.
+ *
+ * @return The JSON string representation of the object
+ * that was passed to the constructor
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function getString():String {
+ return jsonString;
+ }
+
+ /**
+ * Converts a value to it's JSON string equivalent.
+ *
+ * @param value The value to convert. Could be any
+ * type (object, number, array, etc)
+ */
+ private function convertToString( value:* ):String {
+
+ // determine what value is and convert it based on it's type
+ if ( value is String ) {
+
+ // escape the string so it's formatted correctly
+ return escapeString( value as String );
+
+ } else if ( value is Number ) {
+
+ // only encode numbers that finate
+ return isFinite( value as Number) ? value.toString() : "null";
+
+ } else if ( value is Boolean ) {
+
+ // convert boolean to string easily
+ return value ? "true" : "false";
+
+ } else if ( value is Array ) {
+
+ // call the helper method to convert an array
+ return arrayToString( value as Array );
+
+ } else if ( value is Object && value != null ) {
+
+ // call the helper method to convert an object
+ return objectToString( value );
+ }
+ return "null";
+ }
+
+ /**
+ * Escapes a string accoding to the JSON specification.
+ *
+ * @param str The string to be escaped
+ * @return The string with escaped special characters
+ * according to the JSON specification
+ */
+ private function escapeString( str:String ):String {
+ // create a string to store the string's jsonstring value
+ var s:String = "";
+ // current character in the string we're processing
+ var ch:String;
+ // store the length in a local variable to reduce lookups
+ var len:Number = str.length;
+
+ // loop over all of the characters in the string
+ for ( var i:int = 0; i < len; i++ ) {
+
+ // examine the character to determine if we have to escape it
+ ch = str.charAt( i );
+ switch ( ch ) {
+
+ case '"': // quotation mark
+ s += "\\\"";
+ break;
+
+ //case '/': // solidus
+ // s += "\\/";
+ // break;
+
+ case '\\': // reverse solidus
+ s += "\\\\";
+ break;
+
+ case '\b': // bell
+ s += "\\b";
+ break;
+
+ case '\f': // form feed
+ s += "\\f";
+ break;
+
+ case '\n': // newline
+ s += "\\n";
+ break;
+
+ case '\r': // carriage return
+ s += "\\r";
+ break;
+
+ case '\t': // horizontal tab
+ s += "\\t";
+ break;
+
+ default: // everything else
+
+ // check for a control character and escape as unicode
+ if ( ch < ' ' ) {
+ // get the hex digit(s) of the character (either 1 or 2 digits)
+ var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
+
+ // ensure that there are 4 digits by adjusting
+ // the # of zeros accordingly.
+ var zeroPad:String = hexCode.length == 2 ? "00" : "000";
+
+ // create the unicode escape sequence with 4 hex digits
+ s += "\\u" + zeroPad + hexCode;
+ } else {
+
+ // no need to do any special encoding, just pass-through
+ s += ch;
+
+ }
+ } // end switch
+
+ } // end for loop
+
+ return "\"" + s + "\"";
+ }
+
+ /**
+ * Converts an array to it's JSON string equivalent
+ *
+ * @param a The array to convert
+ * @return The JSON string representation of <code>a</code>
+ */
+ private function arrayToString( a:Array ):String {
+ // create a string to store the array's jsonstring value
+ var s:String = "";
+
+ // loop over the elements in the array and add their converted
+ // values to the string
+ for ( var i:int = 0; i < a.length; i++ ) {
+ // when the length is 0 we're adding the first element so
+ // no comma is necessary
+ if ( s.length > 0 ) {
+ // we've already added an element, so add the comma separator
+ s += ","
+ }
+
+ // convert the value to a string
+ s += convertToString( a[i] );
+ }
+
+ // KNOWN ISSUE: In ActionScript, Arrays can also be associative
+ // objects and you can put anything in them, ie:
+ // myArray["foo"] = "bar";
+ //
+ // These properties aren't picked up in the for loop above because
+ // the properties don't correspond to indexes. However, we're
+ // sort of out luck because the JSON specification doesn't allow
+ // these types of array properties.
+ //
+ // So, if the array was also used as an associative object, there
+ // may be some values in the array that don't get properly encoded.
+ //
+ // A possible solution is to instead encode the Array as an Object
+ // but then it won't get decoded correctly (and won't be an
+ // Array instance)
+
+ // close the array and return it's string value
+ return "[" + s + "]";
+ }
+
+ /**
+ * Converts an object to it's JSON string equivalent
+ *
+ * @param o The object to convert
+ * @return The JSON string representation of <code>o</code>
+ */
+ private function objectToString( o:Object ):String
+ {
+ // create a string to store the object's jsonstring value
+ var s:String = "";
+
+ // determine if o is a class instance or a plain object
+ var classInfo:XML = describeType( o );
+ if ( classInfo.@name.toString() == "Object" )
+ {
+ // the value of o[key] in the loop below - store this
+ // as a variable so we don't have to keep looking up o[key]
+ // when testing for valid values to convert
+ var value:Object;
+
+ // loop over the keys in the object and add their converted
+ // values to the string
+ for ( var key:String in o )
+ {
+ // assign value to a variable for quick lookup
+ value = o[key];
+
+ // don't add function's to the JSON string
+ if ( value is Function )
+ {
+ // skip this key and try another
+ continue;
+ }
+
+ // when the length is 0 we're adding the first item so
+ // no comma is necessary
+ if ( s.length > 0 ) {
+ // we've already added an item, so add the comma separator
+ s += ","
+ }
+
+ s += escapeString( key ) + ":" + convertToString( value );
+ }
+ }
+ else // o is a class instance
+ {
+ // Loop over all of the variables and accessors in the class and
+ // serialize them along with their values.
+ for each ( var v:XML in classInfo..*.(
+ name() == "variable"
+ ||
+ (
+ name() == "accessor"
+ // Issue #116 - Make sure accessors are readable
+ && attribute( "access" ).charAt( 0 ) == "r" )
+ ) )
+ {
+ // Issue #110 - If [Transient] metadata exists, then we should skip
+ if ( v.metadata && v.metadata.( @name == "Transient" ).length() > 0 )
+ {
+ continue;
+ }
+
+ // When the length is 0 we're adding the first item so
+ // no comma is necessary
+ if ( s.length > 0 ) {
+ // We've already added an item, so add the comma separator
+ s += ","
+ }
+
+ s += escapeString( v.@name.toString() ) + ":"
+ + convertToString( o[ v.@name ] );
+ }
+
+ }
+
+ return "{" + s + "}";
+ }
+
+
+ }
View
83 monkey/native/flash/com/adobe/serialization/json/JSONParseError.as
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ /**
+ *
+ *
+ */
+ class JSONParseError extends Error {
+
+ /** The location in the string where the error occurred */
+ private var _location:int;
+
+ /** The string in which the parse error occurred */
+ private var _text:String;
+
+ /**
+ * Constructs a new JSONParseError.
+ *
+ * @param message The error message that occured during parsing
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function JSONParseError( message:String = "", location:int = 0, text:String = "") {
+ super( message );
+ name = "JSONParseError";
+ _location = location;
+ _text = text;
+ }
+
+ /**
+ * Provides read-only access to the location variable.
+ *
+ * @return The location in the string where the error occurred
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function get location():int {
+ return _location;
+ }
+
+ /**
+ * Provides read-only access to the text variable.
+ *
+ * @return The string in which the error occurred
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function get text():String {
+ return _text;
+ }
+ }
View
100 monkey/native/flash/com/adobe/serialization/json/JSONToken.as
@@ -0,0 +1,100 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ class JSONToken {
+
+ private var _type:int;
+ private var _value:Object;
+
+ /**
+ * Creates a new JSONToken with a specific token type and value.
+ *
+ * @param type The JSONTokenType of the token
+ * @param value The value of the token
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function JSONToken( type:int = -1 /* JSONTokenType.UNKNOWN */, value:Object = null ) {
+ _type = type;
+ _value = value;
+ }
+
+ /**
+ * Returns the type of the token.
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function get type():int {
+ return _type;
+ }
+
+ /**
+ * Sets the type of the token.
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function set type( value:int ):void {
+ _type = value;
+ }
+
+ /**
+ * Gets the value of the token
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function get value():Object {
+ return _value;
+ }
+
+ /**
+ * Sets the value of the token
+ *
+ * @see com.adobe.serialization.json.JSONTokenType
+ * @langversion ActionScript 3.0
+ * @playerversion Flash 9.0
+ * @tiptext
+ */
+ public function set value ( v:Object ):void {
+ _value = v;
+ }
+
+ }
View
65 monkey/native/flash/com/adobe/serialization/json/JSONTokenType.as
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ /**
+ * Class containing constant values for the different types
+ * of tokens in a JSON encoded string.
+ */
+ class JSONTokenType {
+
+ public static const UNKNOWN:int = -1;
+
+ public static const COMMA:int = 0;
+
+ public static const LEFT_BRACE:int = 1;
+
+ public static const RIGHT_BRACE:int = 2;
+
+ public static const LEFT_BRACKET:int = 3;
+
+ public static const RIGHT_BRACKET:int = 4;
+
+ public static const COLON:int = 6;
+
+ public static const TRUE:int = 7;
+
+ public static const FALSE:int = 8;
+
+ public static const NULL:int = 9;
+
+ public static const STRING:int = 10;
+
+ public static const NUMBER:int = 11;
+
+ public static const NAN:int = 12;
+
+ }
View
698 monkey/native/flash/com/adobe/serialization/json/JSONTokenizer.as
@@ -0,0 +1,698 @@
+/*
+ Copyright (c) 2008, Adobe Systems Incorporated
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems Incorporated nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+ class JSONTokenizer {
+
+ /**
+ * Flag indicating if the tokenizer should only recognize
+ * standard JSON tokens. Setting to <code>false</code> allows
+ * tokens such as NaN and allows numbers to be formatted as
+ * hex, etc.
+ */
+ private var strict:Boolean;
+
+ /** The object that will get parsed from the JSON string */
+ private var obj:Object;
+
+ /** The JSON string to be parsed */
+ private var jsonString:String;
+
+ /** The current parsing location in the JSON string */
+ private var loc:int;
+
+ /** The current character in the JSON string during parsing */
+ private var ch:String;
+
+ /**
+ * The regular expression used to make sure the string does not
+ * contain invalid control characters.
+ */
+ private var controlCharsRegExp:RegExp = /[\x00-\x1F]/;
+
+ /**
+ * Constructs a new JSONDecoder to parse a JSON string
+ * into a native object.
+ *
+ * @param s The JSON string to be converted
+ * into a native object
+ */
+ public function JSONTokenizer( s:String, strict:Boolean )
+ {
+ jsonString = s;
+ this.strict = strict;
+ loc = 0;
+
+ // prime the pump by getting the first character
+ nextChar();
+ }
+
+ /**
+ * Gets the next token in the input sting and advances
+ * the character to the next character after the token
+ */
+ public function getNextToken():JSONToken
+ {
+ var token:JSONToken = new JSONToken();
+
+ // skip any whitespace / comments since the last
+ // token was read
+ skipIgnored();
+
+ // examine the new character and see what we have...
+ switch ( ch )
+ {
+ case '{':
+ token.type = JSONTokenType.LEFT_BRACE;
+ token.value = '{';
+ nextChar();
+ break
+
+ case '}':
+ token.type = JSONTokenType.RIGHT_BRACE;
+ token.value = '}';
+ nextChar();
+ break
+
+ case '[':
+ token.type = JSONTokenType.LEFT_BRACKET;
+ token.value = '[';
+ nextChar();
+ break
+
+ case ']':
+ token.type = JSONTokenType.RIGHT_BRACKET;
+ token.value = ']';
+ nextChar();
+ break
+
+ case ',':
+ token.type = JSONTokenType.COMMA;
+ token.value = ',';
+ nextChar();
+ break
+
+ case ':':
+ token.type = JSONTokenType.COLON;
+ token.value = ':';
+ nextChar();
+ break;
+
+ case 't': // attempt to read true
+ var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
+
+ if ( possibleTrue == "true" )
+ {
+ token.type = JSONTokenType.TRUE;
+ token.value = true;
+ nextChar();
+ }
+ else
+ {
+ parseError( "Expecting 'true' but found " + possibleTrue );
+ }
+
+ break;
+
+ case 'f': // attempt to read false
+ var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
+
+ if ( possibleFalse == "false" )
+ {
+ token.type = JSONTokenType.FALSE;
+ token.value = false;
+ nextChar();
+ }
+ else
+ {
+ parseError( "Expecting 'false' but found " + possibleFalse );
+ }
+
+ break;
+
+ case 'n': // attempt to read null
+ var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
+
+ if ( possibleNull == "null" )
+ {
+ token.type = JSONTokenType.NULL;
+ token.value = null;
+ nextChar();
+ }
+ else
+ {
+ parseError( "Expecting 'null' but found " + possibleNull );
+ }
+
+ break;
+
+ case 'N': // attempt to read NaN
+ var possibleNaN:String = "N" + nextChar() + nextChar();
+
+ if ( possibleNaN == "NaN" )
+ {
+ token.type = JSONTokenType.NAN;
+ token.value = NaN;
+ nextChar();
+ }
+ else
+ {
+ parseError( "Expecting 'NaN' but found " + possibleNaN );
+ }
+
+ break;
+
+ case '"': // the start of a string
+ token = readString();
+ break;
+
+ default:
+ // see if we can read a number
+ if ( isDigit( ch ) || ch == '-' )
+ {
+ token = readNumber();
+ }
+ else if ( ch == '' )
+ {
+ // check for reading past the end of the string
+ return null;
+ }
+ else
+ {
+ // not sure what was in the input string - it's not
+ // anything we expected
+ parseError( "Unexpected " + ch + " encountered" );
+ }
+ }
+
+ return token;
+ }
+
+ /**
+ * Attempts to read a string from the input string. Places
+ * the character location at the first character after the
+ * string. It is assumed that ch is " before this method is called.
+ *
+ * @return the JSONToken with the string value if a string could
+ * be read. Throws an error otherwise.
+ */
+ private function readString():JSONToken
+ {
+ // Rather than examine the string character-by-character, it's
+ // faster to use indexOf to try to and find the closing quote character
+ // and then replace escape sequences after the fact.
+
+ // Start at the current input stream position
+ var quoteIndex:int = loc;
+ do
+ {
+ // Find the next quote in the input stream
+ quoteIndex = jsonString.indexOf( "\"", quoteIndex );
+
+ if ( quoteIndex >= 0 )
+ {
+ // We found the next double quote character in the string, but we need
+ // to make sure it is not part of an escape sequence.
+
+ // Keep looping backwards while the previous character is a backslash
+ var backspaceCount:int = 0;
+ var backspaceIndex:int = quoteIndex - 1;
+ while ( jsonString.charAt( backspaceIndex ) == "\\" )
+ {
+ backspaceCount++;
+ backspaceIndex--;
+ }
+
+ // If we have an even number of backslashes, that means this is the ending quote
+ if ( backspaceCount % 2 == 0 )
+ {
+ break;
+ }
+
+ // At this point, the quote was determined to be part of an escape sequence
+ // so we need to move past the quote index to look for the next one
+ quoteIndex++;
+ }
+ else // There are no more quotes in the string and we haven't found the end yet
+ {
+ parseError( "Unterminated string literal" );
+ }
+ } while ( true );
+
+ // Unescape the string
+ // the token for the string we'll try to read
+ var token:JSONToken = new JSONToken();
+ token.type = JSONTokenType.STRING;
+ // Attach resulting string to the token to return it
+ token.value = unescapeString( jsonString.substr( loc, quoteIndex - loc ) );
+
+ // Move past the closing quote in the input string. This updates the next
+ // character in the input stream to be the character one after the closing quote
+ loc = quoteIndex + 1;
+ nextChar();
+
+ return token;
+ }
+
+ /**
+ * Convert all JavaScript escape characters into normal characters
+ *
+ * @param input The input string to convert
+ * @return Original string with escape characters replaced by real characters
+ */
+ public function unescapeString( input:String ):String
+ {
+ // Issue #104 - If the string contains any unescaped control characters, this
+ // is an error in strict mode
+ if ( strict && controlCharsRegExp.test( input ) )
+ {
+ parseError( "String contains unescaped control character (0x00-0x1F)" );
+ }
+
+ var result:String = "";
+ var backslashIndex:int = 0;
+ var nextSubstringStartPosition:int = 0;
+ var len:int = input.length;
+ do
+ {
+ // Find the next backslash in the input
+ backslashIndex = input.indexOf( '\\', nextSubstringStartPosition );
+
+ if ( backslashIndex >= 0 )
+ {
+ result += input.substr( nextSubstringStartPosition, backslashIndex - nextSubstringStartPosition );
+
+ // Move past the backslash and next character (all escape sequences are
+ // two characters, except for \u, which will advance this further)
+ nextSubstringStartPosition = backslashIndex + 2;
+
+ // Check the next character so we know what to escape
+ var afterBackslashIndex:int = backslashIndex + 1;
+ var escapedChar:String = input.charAt( afterBackslashIndex );
+ switch ( escapedChar )
+ {
+ // Try to list the most common expected cases first to improve performance
+
+ case '"': result += '"'; break; // quotation mark
+ case '\\': result += '\\'; break; // reverse solidus
+ case 'n': result += '\n'; break; // newline
+ case 'r': result += '\r'; break; // carriage return
+ case 't': result += '\t'; break; // horizontal tab
+
+ // Convert a unicode escape sequence to it's character value
+ case 'u':
+
+ // Save the characters as a string we'll convert to an int
+ var hexValue:String = "";
+
+ // Make sure there are enough characters in the string leftover
+ if ( nextSubstringStartPosition + 4 > len )
+ {
+ parseError( "Unexpected end of input. Expecting 4 hex digits after \\u." );
+ }
+
+ // Try to find 4 hex characters
+ for ( var i:int = nextSubstringStartPosition; i < nextSubstringStartPosition + 4; i++ )
+ {
+ // get the next character and determine
+ // if it's a valid hex digit or not
+ var possibleHexChar:String = input.charAt( i );
+ if ( !isHexDigit( possibleHexChar ) )
+ {
+ parseError( "Excepted a hex digit, but found: " + possibleHexChar );
+ }
+
+ // Valid hex digit, add it to the value
+ hexValue += possibleHexChar;
+ }
+
+ // Convert hexValue to an integer, and use that
+ // integer value to create a character to add
+ // to our string.
+ result += String.fromCharCode( parseInt( hexValue, 16 ) );
+ // Move past the 4 hex digits that we just read
+ nextSubstringStartPosition += 4;
+ break;
+
+ case 'f': result += '\f'; break; // form feed
+ case '/': result += '/'; break; // solidus
+ case 'b': result += '\b'; break; // bell
+ default: result += '\\' + escapedChar; // Couldn't unescape the sequence, so just pass it through
+ }
+ }
+ else
+ {
+ // No more backslashes to replace, append the rest of the string
+ result += input.substr( nextSubstringStartPosition );
+ break;
+ }
+
+ } while ( nextSubstringStartPosition < len );
+
+ return result;
+ }
+
+ /**
+ * Attempts to read a number from the input string. Places
+ * the character location at the first character after the
+ * number.
+ *
+ * @return The JSONToken with the number value if a number could
+ * be read. Throws an error otherwise.
+ */
+ private function readNumber():JSONToken
+ {
+ // the string to accumulate the number characters
+ // into that we'll convert to a number at the end
+ var input:String = "";
+
+ // check for a negative number
+ if ( ch == '-' )
+ {
+ input += '-';
+ nextChar();
+ }
+
+ // the number must start with a digit
+ if ( !isDigit( ch ) )
+ {
+ parseError( "Expecting a digit" );
+ }
+
+ // 0 can only be the first digit if it
+ // is followed by a decimal point
+ if ( ch == '0' )
+ {
+ input += ch;
+ nextChar();
+
+ // make sure no other digits come after 0
+ if ( isDigit( ch ) )