├── lib
└── as3game.swc
├── bin
├── SpriteSheet.swf
└── data
│ ├── xml
│ ├── xmlformat.png
│ └── xmlformat.xml
│ ├── json
│ ├── jsonformat.png
│ └── jsonformat.json
│ └── json-array
│ ├── jsonarrayformat.json
│ └── jsonarrayformat.png
├── src
├── com
│ ├── as3game
│ │ └── spritesheet
│ │ │ ├── SpriteFrame.as
│ │ │ ├── vos
│ │ │ ├── DataFormat.as
│ │ │ └── Animation.as
│ │ │ ├── analyze
│ │ │ ├── XmlFormat.as
│ │ │ ├── JsonArrayFormat.as
│ │ │ └── JsonFormat.as
│ │ │ ├── TextureAtlas.as
│ │ │ ├── SpriteSheetButton.as
│ │ │ └── SpriteSheet.as
│ └── adobe
│ │ └── serialization
│ │ └── json
│ │ ├── JSONTokenType.as
│ │ ├── JSONParseError.as
│ │ ├── JSONToken.as
│ │ ├── JSON.as
│ │ ├── JSONEncoder.as
│ │ ├── JSONDecoder.as
│ │ └── JSONTokenizer.as
├── TestSpriteSheet.as
└── SWFProfiler.as
├── SpriteSheet.lxml
├── .gitattributes
├── README.md
├── SpriteSheet.as3proj
└── .gitignore
/lib/as3game.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/lib/as3game.swc
--------------------------------------------------------------------------------
/bin/SpriteSheet.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/SpriteSheet.swf
--------------------------------------------------------------------------------
/bin/data/xml/xmlformat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/xml/xmlformat.png
--------------------------------------------------------------------------------
/bin/data/xml/xmlformat.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/xml/xmlformat.xml
--------------------------------------------------------------------------------
/bin/data/json/jsonformat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/json/jsonformat.png
--------------------------------------------------------------------------------
/bin/data/json/jsonformat.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/json/jsonformat.json
--------------------------------------------------------------------------------
/bin/data/json-array/jsonarrayformat.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/json-array/jsonarrayformat.json
--------------------------------------------------------------------------------
/bin/data/json-array/jsonarrayformat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerzhu/SpriteSheet/HEAD/bin/data/json-array/jsonarrayformat.png
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/SpriteFrame.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet
2 | {
3 | /**
4 | * 动画一帧对应PNG文件中的属性
5 | * @author Tylerzhu
6 | */
7 | public class SpriteFrame
8 | {
9 | public var id:String;
10 | public var x:int;
11 | public var y:int;
12 | public var width:int;
13 | public var height:int;
14 | public var offX:int;
15 | public var offY:int;
16 | public var centerX:Number;
17 | public var centerY:Number;
18 |
19 | public function SpriteFrame()
20 | {
21 |
22 | }
23 |
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/vos/DataFormat.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet.vos
2 | {
3 | /**
4 | * 与Flash cs导出的数据格式对应,目前实现了以下3种
5 | * 1. JSON
6 | * 2. JSON-Array
7 | * 3. XML
8 | * @author Tylerzhu
9 | */
10 | public class DataFormat
11 | {
12 | public static const FORMAT_JSON:String = "format_json";
13 | public static const FORMAT_JSON_ARRAY:String = "format_json_array";
14 | public static const FORMAT_XML:String = "format_xml";
15 |
16 | public function DataFormat()
17 | {
18 |
19 | }
20 |
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/SpriteSheet.lxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 | false
7 | false
8 | true
9 | false
10 | false
11 |
12 |
13 | None
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SpriteSheet
2 | ===========
3 |
4 | ActionScript 3.0 Sprite Sheet render engine
5 |
6 | 实现了SpriteSheet精灵序列图引擎,可以解析Flash Cs6/TexturePacker导出的JSON、JSON-Array、XML格式的SpriteSheet。
7 | 相关介绍:http://www.cnblogs.com/skynet/p/3570421.html
8 |
9 | SpriteSheet调整中心等功能,参考SpriteSheet编辑工具:https://github.com/saylorzhu/SpriteSheetEdit
10 |
11 | Demo
12 | -----------
13 | . 加载SpriteSheet png图片及数据描述文件
14 |
15 | >AssetManager.getInstance().getGroupAssets("spritesheets-json", ["data/json/jsonformat.json", "data/json/jsonformat.png"], onAnimLoaded);
16 |
17 | . 创建SpriteSheet实例:
18 |
19 | >private function onAnimLoaded():void
20 | {
21 | var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData("data/json/jsonformat.png");
22 | var sheets:* = AssetManager.getInstance().getContent("data/json/jsonformat.json");
23 | var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
24 | sp.setAction("呼吸", 14);
25 | sp.play();
26 | addChild(sp);
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/vos/Animation.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet.vos
2 | {
3 | import com.as3game.spritesheet.SpriteFrame;
4 | import flash.geom.Point;
5 | /**
6 | * 动画信息,一张SpriteSheet中经常会包含多个动画序列
7 | * 如一个战斗角色SpriteSheet包含:walk、stand、attack、unattack等等
8 | * @author Tylerzhu
9 | */
10 | public class Animation
11 | {
12 | public var seqName:String;// 动画序列名 (e.g. "walk")
13 | public var delay:Number;// 帧间隔
14 | public var loop:Boolean;// 是否循环播放
15 | public var arFrames:Vector.;// 帧信息数据
16 | public var center:Point; //中心点位置
17 |
18 | public function Animation(name:String, frames:Vector., frameRate:Number=0, looped:Boolean=true)
19 | {
20 | seqName = name;
21 | delay = 0;
22 | if(frameRate > 0)
23 | delay = 1.0/frameRate;
24 |
25 | arFrames = frames;
26 | loop = looped;
27 | center = new Point(frames[1].centerX, frames[1].centerY);
28 | }
29 |
30 | public function destroy():void
31 | {
32 | arFrames = null;
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/analyze/XmlFormat.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet.analyze
2 | {
3 | import com.as3game.spritesheet.SpriteFrame;
4 |
5 | /**
6 | * 解析XML格式的SpriteSheet数据描述文件
7 | * @author Tylerzhu
8 | */
9 | public class XmlFormat
10 | {
11 | private var mMeta:Object;
12 | private var mFrames:Vector.;
13 |
14 | public function XmlFormat(xml:XML)
15 | {
16 | mFrames = new Vector.();
17 | var spriteFrame:SpriteFrame;
18 |
19 | var xmlList:XMLList = xml.SubTexture;
20 | for (var i:int = 0; i < xmlList.length(); i++)
21 | {
22 | spriteFrame = new SpriteFrame();
23 | spriteFrame.id = String(xmlList[i].@name);
24 | spriteFrame.x = int(xmlList[i].@x);
25 | spriteFrame.y = int(xmlList[i].@y);
26 | spriteFrame.width = int(xmlList[i].@width);
27 | spriteFrame.height = int(xmlList[i].@height);
28 | spriteFrame.offX = -1 * int(xmlList[i].@frameX);
29 | spriteFrame.offY = -1 * int(xmlList[i].@frameY);
30 | spriteFrame.centerX = Number(xmlList[i].@centerX);
31 | spriteFrame.centerY = Number(xmlList[i].@centerY);
32 | mFrames.push(spriteFrame);
33 | }
34 | }
35 |
36 | public function getData():Object
37 | {
38 | return {"meta": mMeta, "frames": mFrames};
39 | }
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/analyze/JsonArrayFormat.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet.analyze
2 | {
3 | import com.adobe.serialization.json.JSON;
4 | import com.as3game.spritesheet.SpriteFrame;
5 |
6 | /**
7 | * 解析JSON Array格式的SpriteSheet数据描述文件
8 | * @author Tylerzhu
9 | */
10 | public class JsonArrayFormat
11 | {
12 | private var mMeta:Object;
13 | private var mFrames:Vector.;
14 |
15 | public function JsonArrayFormat(text:String)
16 | {
17 | var data:Object = com.adobe.serialization.json.JSON.decode(text);
18 | mMeta = data.meta;
19 | mFrames = new Vector.();
20 |
21 | var frames:Object = data.frames;
22 | var spriteFrame:SpriteFrame;
23 | for each (var item:Object in frames)
24 | {
25 | spriteFrame = new SpriteFrame();
26 | spriteFrame.id = item.filename;
27 | spriteFrame.x = item.frame.x;
28 | spriteFrame.y = item.frame.y;
29 | spriteFrame.width = item.frame.w;
30 | spriteFrame.height = item.frame.h;
31 | spriteFrame.offX = item.spriteSourceSize.x;
32 | spriteFrame.offY = item.spriteSourceSize.y;
33 | //spriteFrame.centerX = Number(xmlList[i].@centerX);
34 | //spriteFrame.centerY = Number(xmlList[i].@centerY);
35 | mFrames.push(spriteFrame);
36 | }
37 | }
38 |
39 | public function getData():Object
40 | {
41 | return {"meta": mMeta, "frames": mFrames};
42 | }
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/analyze/JsonFormat.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet.analyze
2 | {
3 | import com.adobe.serialization.json.JSON;
4 | import com.as3game.spritesheet.SpriteFrame;
5 |
6 | /**
7 | * 解析JSON格式的SpriteSheet数据描述文件
8 | * @author Tylerzhu
9 | */
10 | public class JsonFormat
11 | {
12 | private var mMeta:Object;
13 | private var mFrames:Vector.;
14 |
15 | public function JsonFormat(text:String)
16 | {
17 | var data:Object = com.adobe.serialization.json.JSON.decode(text);
18 | mMeta = data.meta;
19 | mFrames = new Vector.();
20 |
21 | var frames:Object = data.frames;
22 | var spriteFrame:SpriteFrame;
23 | for (var key:String in frames)
24 | {
25 | var item:Object = frames[key];
26 | spriteFrame = new SpriteFrame();
27 | spriteFrame.id = key;
28 | spriteFrame.x = item.frame.x;
29 | spriteFrame.y = item.frame.y;
30 | spriteFrame.width = item.frame.w;
31 | spriteFrame.height = item.frame.h;
32 | spriteFrame.offX = item.spriteSourceSize.x;
33 | spriteFrame.offY = item.spriteSourceSize.y;
34 | //spriteFrame.centerX = Number(xmlList[i].@centerX);
35 | //spriteFrame.centerY = Number(xmlList[i].@centerY);
36 | mFrames.push(spriteFrame);
37 | }
38 | }
39 |
40 | public function getData():Object
41 | {
42 | return { "meta": mMeta, "frames":mFrames };
43 | }
44 |
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/TextureAtlas.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet
2 | {
3 | import com.as3game.spritesheet.vos.Animation;
4 | import flash.display.BitmapData;
5 | import flash.geom.Point;
6 | import flash.geom.Rectangle;
7 |
8 | /**
9 | * 动画序列帧数据
10 | * @author Tylerzhu
11 | */
12 | public class TextureAtlas
13 | {
14 | private var mTextureRegions:Vector.;
15 | private var mFrameOffsets:Vector.;
16 | private var mTextureSheet:BitmapData;
17 | private var mFrameRect:Rectangle;
18 |
19 | public function TextureAtlas()
20 | {
21 |
22 | }
23 |
24 | /**
25 | * 初始化动画帧序列,构建帧框数组、偏移点数组
26 | * @param sheet
27 | * @param animation
28 | */
29 | public function init(sheet:BitmapData, animation:Animation):void
30 | {
31 | mTextureRegions = new Vector.();
32 | mFrameRect = new Rectangle();
33 | mFrameOffsets = new Vector.();
34 | mTextureSheet = sheet;
35 | var arFrameData:Vector. = animation.arFrames;
36 | var rcFrame:Rectangle;
37 | var regPt:Point;
38 |
39 | for (var i:int = 0; i < arFrameData.length; i++)
40 | {
41 | rcFrame = new Rectangle();
42 |
43 | rcFrame.x = arFrameData[i].x;
44 | rcFrame.y = arFrameData[i].y;
45 | rcFrame.width = arFrameData[i].width;
46 | rcFrame.height = arFrameData[i].height;
47 | mTextureRegions.push(rcFrame);
48 |
49 | regPt = new Point();
50 | regPt.x = arFrameData[i].offX;
51 | regPt.y = arFrameData[i].offY;
52 | mFrameOffsets.push(regPt);
53 |
54 | mFrameRect.width = Math.max(mFrameRect.width, rcFrame.width + regPt.x);
55 | mFrameRect.height = Math.max(mFrameRect.height, rcFrame.height + regPt.y);
56 | }
57 |
58 | }
59 |
60 | /**
61 | *
62 | * @param frame
63 | * @param dstBmp
64 | */
65 | public function drawFrame(frame:uint, dstBmp:BitmapData):void
66 | {
67 | dstBmp.fillRect(dstBmp.rect, 0);
68 | dstBmp.copyPixels(mTextureSheet, mTextureRegions[frame], mFrameOffsets[frame]);
69 | }
70 |
71 | public function get maxRect():Rectangle
72 | {
73 | return mFrameRect;
74 | }
75 |
76 | public function destroy():void
77 | {
78 | mTextureRegions = null;
79 | mFrameRect = null;
80 | mFrameOffsets = null;
81 | mTextureSheet = null;
82 | }
83 |
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONTokenType.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | /**
37 | * Class containing constant values for the different types
38 | * of tokens in a JSON encoded string.
39 | */
40 | public final class JSONTokenType
41 | {
42 | public static const UNKNOWN:int = -1;
43 |
44 | public static const COMMA:int = 0;
45 |
46 | public static const LEFT_BRACE:int = 1;
47 |
48 | public static const RIGHT_BRACE:int = 2;
49 |
50 | public static const LEFT_BRACKET:int = 3;
51 |
52 | public static const RIGHT_BRACKET:int = 4;
53 |
54 | public static const COLON:int = 6;
55 |
56 | public static const TRUE:int = 7;
57 |
58 | public static const FALSE:int = 8;
59 |
60 | public static const NULL:int = 9;
61 |
62 | public static const STRING:int = 10;
63 |
64 | public static const NUMBER:int = 11;
65 |
66 | public static const NAN:int = 12;
67 |
68 | }
69 | }
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONParseError.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | /**
37 | *
38 | *
39 | */
40 | public class JSONParseError extends Error
41 | {
42 |
43 | /** The location in the string where the error occurred */
44 | private var _location:int;
45 |
46 | /** The string in which the parse error occurred */
47 | private var _text:String;
48 |
49 | /**
50 | * Constructs a new JSONParseError.
51 | *
52 | * @param message The error message that occured during parsing
53 | * @langversion ActionScript 3.0
54 | * @playerversion Flash 9.0
55 | * @tiptext
56 | */
57 | public function JSONParseError( message:String = "", location:int = 0, text:String = "" )
58 | {
59 | super( message );
60 | name = "JSONParseError";
61 | _location = location;
62 | _text = text;
63 | }
64 |
65 | /**
66 | * Provides read-only access to the location variable.
67 | *
68 | * @return The location in the string where the error occurred
69 | * @langversion ActionScript 3.0
70 | * @playerversion Flash 9.0
71 | * @tiptext
72 | */
73 | public function get location():int
74 | {
75 | return _location;
76 | }
77 |
78 | /**
79 | * Provides read-only access to the text variable.
80 | *
81 | * @return The string in which the error occurred
82 | * @langversion ActionScript 3.0
83 | * @playerversion Flash 9.0
84 | * @tiptext
85 | */
86 | public function get text():String
87 | {
88 | return _text;
89 | }
90 | }
91 |
92 | }
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONToken.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | public final class JSONToken
37 | {
38 |
39 | /**
40 | * The type of the token.
41 | *
42 | * @langversion ActionScript 3.0
43 | * @playerversion Flash 9.0
44 | * @tiptext
45 | */
46 | public var type:int;
47 |
48 | /**
49 | * The value of the token
50 | *
51 | * @langversion ActionScript 3.0
52 | * @playerversion Flash 9.0
53 | * @tiptext
54 | */
55 | public var value:Object;
56 |
57 | /**
58 | * Creates a new JSONToken with a specific token type and value.
59 | *
60 | * @param type The JSONTokenType of the token
61 | * @param value The value of the token
62 | * @langversion ActionScript 3.0
63 | * @playerversion Flash 9.0
64 | * @tiptext
65 | */
66 | public function JSONToken( type:int = -1 /* JSONTokenType.UNKNOWN */, value:Object = null )
67 | {
68 | this.type = type;
69 | this.value = value;
70 | }
71 |
72 | /**
73 | * Reusable token instance.
74 | *
75 | * @see #create()
76 | */
77 | internal static const token:JSONToken = new JSONToken();
78 |
79 | /**
80 | * Factory method to create instances. Because we don't need more than one instance
81 | * of a token at a time, we can always use the same instance to improve performance
82 | * and reduce memory consumption during decoding.
83 | */
84 | internal static function create( type:int = -1 /* JSONTokenType.UNKNOWN */, value:Object = null ):JSONToken
85 | {
86 | token.type = type;
87 | token.value = value;
88 |
89 | return token;
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/SpriteSheet.as3proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSON.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | /**
37 | * This class provides encoding and decoding of the JSON format.
38 | *
39 | * Example usage:
40 | *
41 | * // create a JSON string from an internal object
42 | * JSON.encode( myObject );
43 | *
44 | * // read a JSON string into an internal object
45 | * var myObject:Object = JSON.decode( jsonString );
46 | *
47 | */
48 | public final class JSON
49 | {
50 | /**
51 | * Encodes a object into a JSON string.
52 | *
53 | * @param o The object to create a JSON string for
54 | * @return the JSON string representing o
55 | * @langversion ActionScript 3.0
56 | * @playerversion Flash 9.0
57 | * @tiptext
58 | */
59 | public static function encode( o:Object ):String
60 | {
61 | return new JSONEncoder( o ).getString();
62 | }
63 |
64 | /**
65 | * Decodes a JSON string into a native object.
66 | *
67 | * @param s The JSON string representing the object
68 | * @param strict Flag indicating if the decoder should strictly adhere
69 | * to the JSON standard or not. The default of true
70 | * throws errors if the format does not match the JSON syntax exactly.
71 | * Pass false to allow for non-properly-formatted JSON
72 | * strings to be decoded with more leniancy.
73 | * @return A native object as specified by s
74 | * @throw JSONParseError
75 | * @langversion ActionScript 3.0
76 | * @playerversion Flash 9.0
77 | * @tiptext
78 | */
79 | public static function decode( s:String, strict:Boolean = true ):*
80 | {
81 | return new JSONDecoder( s, strict ).getValue();
82 | }
83 |
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/SpriteSheetButton.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet
2 | {
3 | import com.as3game.spritesheet.analyze.JsonArrayFormat;
4 | import com.as3game.spritesheet.analyze.JsonFormat;
5 | import com.as3game.spritesheet.analyze.XmlFormat;
6 | import com.as3game.spritesheet.vos.Animation;
7 | import com.as3game.spritesheet.vos.DataFormat;
8 | import flash.display.Bitmap;
9 | import flash.display.BitmapData;
10 | import flash.display.Sprite;
11 | import flash.events.MouseEvent;
12 | import flash.geom.Point;
13 |
14 | /**
15 | * ...
16 | * @author Tylerzhu
17 | */
18 | public class SpriteSheetButton extends Sprite
19 | {
20 | private var mTexture:BitmapData; //SpriteSheet位图
21 | private var mSheets:Object; //SpriteSheet描述数据
22 | private var mTextureAtlas:TextureAtlas;
23 | private var mCanvas:Bitmap; //画布
24 | private var mButtonName:String; //按钮名称(默认为all)
25 | private var mAnimation:Animation; //按钮帧序列
26 |
27 | public function SpriteSheetButton(texture:BitmapData, sheets:*, dataFormat:String = "format_xml", buttonName:String = "all")
28 | {
29 | mButtonName = buttonName;
30 | mTexture = texture;
31 | switch (dataFormat)
32 | {
33 | case DataFormat.FORMAT_JSON:
34 | mSheets = new JsonFormat(sheets as String).getData();
35 | break;
36 | case DataFormat.FORMAT_JSON_ARRAY:
37 | mSheets = new JsonArrayFormat(sheets as String).getData();
38 | break;
39 | case DataFormat.FORMAT_XML:
40 | mSheets = new XmlFormat(sheets as XML).getData();
41 | break;
42 | default:
43 | }
44 |
45 | initButton(buttonName);
46 | }
47 |
48 | public function get sheets():Object
49 | {
50 | return mSheets;
51 | }
52 |
53 | public function get centor():Point
54 | {
55 | return mAnimation.center;
56 | }
57 |
58 | public function destroy():void
59 | {
60 | mCanvas.bitmapData.dispose();
61 | mCanvas = null;
62 | mTexture = null;
63 | mButtonName = null;
64 | mSheets = null;
65 | mAnimation.destroy();
66 | mAnimation = null;
67 | }
68 |
69 | private function initButton(buttonName:String):void
70 | {
71 | var frames:Vector. = new Vector.();
72 | for each (var item:SpriteFrame in mSheets.frames)
73 | {
74 | if (item.id.indexOf(mButtonName) != -1 || mButtonName == "all")
75 | {
76 | frames.push(item);
77 | }
78 | }
79 |
80 | mAnimation = new Animation(mButtonName, frames);
81 | mTextureAtlas = new TextureAtlas();
82 | mTextureAtlas.init(mTexture, mAnimation);
83 |
84 | mCanvas = new Bitmap();
85 | mCanvas.bitmapData = new BitmapData(mTextureAtlas.maxRect.width, mTextureAtlas.maxRect.height);
86 | mCanvas.x = mAnimation.center.x;
87 | mCanvas.y = mAnimation.center.y;
88 | addChild(mCanvas);
89 |
90 | mTextureAtlas.drawFrame(0, mCanvas.bitmapData);
91 |
92 | this.buttonMode = true;
93 | this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
94 | this.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
95 | this.addEventListener(MouseEvent.CLICK, onMouseClick);
96 | }
97 |
98 | private function onMouseClick(e:MouseEvent):void
99 | {
100 | mTextureAtlas.drawFrame(0, mCanvas.bitmapData);
101 | }
102 |
103 | private function onMouseOut(e:MouseEvent):void
104 | {
105 | mTextureAtlas.drawFrame(0, mCanvas.bitmapData);
106 | }
107 |
108 | private function onMouseOver(e:MouseEvent):void
109 | {
110 | mTextureAtlas.drawFrame(1, mCanvas.bitmapData);
111 | }
112 |
113 | }
114 |
115 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | tmp/
9 | *.tmp
10 | *.bak
11 | *.swp
12 | *~.nib
13 | local.properties
14 | .classpath
15 | .settings/
16 | .loadpath
17 |
18 | # External tool builders
19 | .externalToolBuilders/
20 |
21 | # Locally stored "Eclipse launch configurations"
22 | *.launch
23 |
24 | # CDT-specific
25 | .cproject
26 |
27 | # PDT-specific
28 | .buildpath
29 |
30 |
31 | #################
32 | ## Visual Studio
33 | #################
34 |
35 | ## Ignore Visual Studio temporary files, build results, and
36 | ## files generated by popular Visual Studio add-ons.
37 |
38 | # User-specific files
39 | *.suo
40 | *.user
41 | *.sln.docstates
42 |
43 | # Build results
44 |
45 | [Dd]ebug/
46 | [Rr]elease/
47 | x64/
48 | build/
49 | [Oo]bj/
50 |
51 | # MSTest test Results
52 | [Tt]est[Rr]esult*/
53 | [Bb]uild[Ll]og.*
54 |
55 | *_i.c
56 | *_p.c
57 | *.ilk
58 | *.meta
59 | *.obj
60 | *.pch
61 | *.pdb
62 | *.pgc
63 | *.pgd
64 | *.rsp
65 | *.sbr
66 | *.tlb
67 | *.tli
68 | *.tlh
69 | *.tmp
70 | *.tmp_proj
71 | *.log
72 | *.vspscc
73 | *.vssscc
74 | .builds
75 | *.pidb
76 | *.log
77 | *.scc
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opensdf
84 | *.sdf
85 | *.cachefile
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 |
99 | # TeamCity is a build add-in
100 | _TeamCity*
101 |
102 | # DotCover is a Code Coverage Tool
103 | *.dotCover
104 |
105 | # NCrunch
106 | *.ncrunch*
107 | .*crunch*.local.xml
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.Publish.xml
127 | *.pubxml
128 |
129 | # NuGet Packages Directory
130 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
131 | #packages/
132 |
133 | # Windows Azure Build Output
134 | csx
135 | *.build.csdef
136 |
137 | # Windows Store app package directory
138 | AppPackages/
139 |
140 | # Others
141 | sql/
142 | *.Cache
143 | ClientBin/
144 | [Ss]tyle[Cc]op.*
145 | ~$*
146 | *~
147 | *.dbmdl
148 | *.[Pp]ublish.xml
149 | *.pfx
150 | *.publishsettings
151 |
152 | # RIA/Silverlight projects
153 | Generated_Code/
154 |
155 | # Backup & report files from converting an old project file to a newer
156 | # Visual Studio version. Backup files are not needed, because we have git ;-)
157 | _UpgradeReport_Files/
158 | Backup*/
159 | UpgradeLog*.XML
160 | UpgradeLog*.htm
161 |
162 | # SQL Server files
163 | App_Data/*.mdf
164 | App_Data/*.ldf
165 |
166 | #############
167 | ## Windows detritus
168 | #############
169 |
170 | # Windows image file caches
171 | Thumbs.db
172 | ehthumbs.db
173 |
174 | # Folder config file
175 | Desktop.ini
176 |
177 | # Recycle Bin used on file shares
178 | $RECYCLE.BIN/
179 |
180 | # Mac crap
181 | .DS_Store
182 |
183 |
184 | #############
185 | ## Python
186 | #############
187 |
188 | *.py[co]
189 |
190 | # Packages
191 | *.egg
192 | *.egg-info
193 | dist/
194 | build/
195 | eggs/
196 | parts/
197 | var/
198 | sdist/
199 | develop-eggs/
200 | .installed.cfg
201 |
202 | # Installer logs
203 | pip-log.txt
204 |
205 | # Unit test / coverage reports
206 | .coverage
207 | .tox
208 |
209 | #Translations
210 | *.mo
211 |
212 | #Mr Developer
213 | .mr.developer.cfg
214 |
--------------------------------------------------------------------------------
/src/com/as3game/spritesheet/SpriteSheet.as:
--------------------------------------------------------------------------------
1 | package com.as3game.spritesheet
2 | {
3 | import com.as3game.spritesheet.analyze.JsonArrayFormat;
4 | import com.as3game.spritesheet.analyze.JsonFormat;
5 | import com.as3game.spritesheet.analyze.XmlFormat;
6 | import com.as3game.spritesheet.vos.Animation;
7 | import com.as3game.spritesheet.vos.DataFormat;
8 | import com.as3game.time.GameTimer;
9 | import flash.display.Bitmap;
10 | import flash.display.BitmapData;
11 | import flash.display.Sprite;
12 | import flash.geom.Point;
13 | import flash.utils.getTimer;
14 |
15 | /**
16 | * SpriteSheet位图动画类
17 | * @author Tylerzhu
18 | */
19 | public class SpriteSheet extends Sprite
20 | {
21 | private var mTextureAtlas:TextureAtlas;
22 | private var mPlaying:Boolean;
23 | private var mCurFrame:uint;
24 | private var mCanvas:Bitmap;//当前帧位图画布
25 |
26 | private var mTexture:BitmapData; //SpriteSheet位图
27 | private var mSheets:Object; //SpriteSheet描述数据
28 | private var mActionName:String; //当前动作名称(默认为all)
29 | private var mAnimation:Animation;//当前动画(一张SpriteSheet中经常会包含多个动画序列,walk/stand/attack)
30 | private var mObjectId:String;
31 |
32 | public function SpriteSheet(texture:BitmapData, sheets:*, dataFormat:String)
33 | {
34 | mObjectId = "SpriteSheet" + getTimer();
35 | mActionName = "";
36 | mTexture = texture;
37 | switch (dataFormat)
38 | {
39 | case DataFormat.FORMAT_JSON:
40 | mSheets = new JsonFormat(sheets as String).getData();
41 | break;
42 | case DataFormat.FORMAT_JSON_ARRAY:
43 | mSheets = new JsonArrayFormat(sheets as String).getData();
44 | break;
45 | case DataFormat.FORMAT_XML:
46 | mSheets = new XmlFormat(sheets as XML).getData();
47 | break;
48 | default:
49 | }
50 | }
51 |
52 | public function get centor():Point
53 | {
54 | return mAnimation.center;
55 | }
56 |
57 | public function setAction(id:String = "all", frameRate:int = 30, loop:Boolean = true):void
58 | {
59 | if (id == mActionName)
60 | {
61 | return;
62 | }
63 |
64 | // 清理操作
65 | if (mCanvas)
66 | {
67 | removeChild(mCanvas);
68 | mCanvas.bitmapData.dispose();
69 | }
70 | if (mAnimation)
71 | {
72 | mAnimation.destroy();
73 | }
74 | if (mTextureAtlas)
75 | {
76 | mTextureAtlas.destroy();
77 | }
78 |
79 | //
80 | mActionName = id;
81 | mCurFrame = 0;
82 |
83 | var frames:Vector. = new Vector.();
84 | if (mActionName == "all")
85 | {
86 | frames = mSheets.frames;
87 | }
88 | else
89 | {
90 | for each (var item:SpriteFrame in mSheets.frames)
91 | {
92 | if (item.id.indexOf(id) != -1)
93 | {
94 | frames.push(item);
95 | }
96 | }
97 | }
98 |
99 | mAnimation = new Animation(id, frames, frameRate, loop);
100 | mTextureAtlas = new TextureAtlas();
101 | mTextureAtlas.init(mTexture, mAnimation);
102 |
103 | mCanvas = new Bitmap();
104 | mCanvas.bitmapData = new BitmapData(mTextureAtlas.maxRect.width, mTextureAtlas.maxRect.height);
105 | mCanvas.x = mAnimation.center.x;
106 | mCanvas.y = mAnimation.center.y;
107 | addChild(mCanvas);
108 | }
109 |
110 | public function get isPlaying():Boolean
111 | {
112 | return mPlaying;
113 | }
114 |
115 | /**
116 | * 播放SpriteSheet位图动画
117 | */
118 | public function play():void
119 | {
120 | if (mActionName == "")
121 | {
122 | return;
123 | }
124 | mPlaying = true;
125 | mTextureAtlas.drawFrame(mCurFrame, mCanvas.bitmapData);
126 |
127 | GameTimer.getInstance().register(mObjectId, 1000 * mAnimation.delay, 0, update);
128 | }
129 |
130 | /**
131 | * 停止SpriteSheet位图动画
132 | */
133 | public function stop():void
134 | {
135 | mPlaying = false;
136 | GameTimer.getInstance().unregister(mObjectId);
137 | }
138 |
139 | /**
140 | * 将播放头移到SpriteSheet的指定帧并开始播放。
141 | * @param frame 表示播放头转到的帧编号的数字。
142 | */
143 | public function gotoAndPlay(frame:uint):void
144 | {
145 | mCurFrame = frame;
146 | if (mCurFrame >= mAnimation.arFrames.length)
147 | {
148 | mCurFrame = mAnimation.arFrames.length - 1;
149 | }
150 | play();
151 | }
152 |
153 | /**
154 | * 将播放头移到SpriteSheet的指定帧并停在那里。
155 | * @param frame 表示播放头转到的帧编号的数字。
156 | */
157 | public function gotoAndStop(frame:uint):void
158 | {
159 | stop();
160 | mCurFrame = frame;
161 | if (mCurFrame >= mAnimation.arFrames.length)
162 | {
163 | mCurFrame = mAnimation.arFrames.length - 1;
164 | }
165 | mTextureAtlas.drawFrame(mCurFrame, mCanvas.bitmapData);
166 | }
167 |
168 | public function get currenFrame():uint
169 | {
170 | return mCurFrame;
171 | }
172 |
173 | public function get actionName():String
174 | {
175 | return mActionName;
176 | }
177 |
178 | private function update(count:uint):void
179 | {
180 | mCurFrame ++;
181 | if (mCurFrame >= mAnimation.arFrames.length)
182 | {
183 | if (mAnimation.loop)
184 | {
185 | mCurFrame = 0;
186 | }
187 | else
188 | {
189 | GameTimer.getInstance().unregister(mObjectId);
190 | return;
191 | }
192 | }
193 | mTextureAtlas.drawFrame(mCurFrame, mCanvas.bitmapData);
194 |
195 | }
196 | }
197 |
198 | }
--------------------------------------------------------------------------------
/src/TestSpriteSheet.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import com.as3game.asset.AssetManager;
4 | import com.as3game.spritesheet.SpriteSheet;
5 | import com.as3game.spritesheet.vos.DataFormat;
6 | import flash.display.BitmapData;
7 | import flash.display.Sprite;
8 | import flash.events.MouseEvent;
9 | import flash.filters.ColorMatrixFilter;
10 | import flash.text.TextField;
11 |
12 | /**
13 | * ...
14 | * @author Tylerzhu
15 | */
16 | public class TestSpriteSheet extends Sprite
17 | {
18 |
19 | public function TestSpriteSheet()
20 | {
21 | SWFProfiler.init(stage, this);
22 | AssetManager.getInstance().getGroupAssets("spritesheets-json", ["data/json/jsonformat.json", "data/json/jsonformat.png"], onAnimLoaded);
23 | AssetManager.getInstance().getGroupAssets("spritesheets-xml", ["data/xml/xmlformat.xml", "data/xml/xmlformat.png"], onAnimLoadedXML);
24 | AssetManager.getInstance().getGroupAssets("spritesheets-jsonarray", ["data/json-array/jsonarrayformat.json", "data/json-array/jsonarrayformat.png"], onAnimLoadedJsonArray);
25 | }
26 |
27 |
28 | private function onAnimLoaded():void
29 | {
30 | var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData("data/json/jsonformat.png");
31 | var sheets:* = AssetManager.getInstance().getContent("data/json/jsonformat.json");
32 | var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
33 | sp.setAction("呼吸", 14);
34 | //sp.setAction("打击", 14);
35 | sp.play();
36 | addChild(sp);
37 |
38 | var sp1:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
39 | sp1.x = 100;
40 | sp1.setAction("打击", 14);
41 | sp1.play();
42 | addChild(sp1);
43 | //sp1.filters = [new ColorMatrixFilter([
44 | //0.3,0.6,0.082,0,0,
45 | //0.3,0.6,0.082,0,0,
46 | //0.3,0.6,0.082,0,0,
47 | //0,0,0,1,0])];;
48 |
49 | var sp2:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
50 | sp2.x = 250;
51 | sp2.setAction("左行走", 14);
52 | sp2.play();
53 | addChild(sp2);
54 |
55 | var sp3:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
56 | sp3.x = 400;
57 | sp3.setAction("挨打", 14);
58 | sp3.play();
59 | addChild(sp3);
60 |
61 | var sp4:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON);
62 | sp4.x = 550;
63 | sp4.setAction("攻击", 30);
64 | sp4.play();
65 | addChild(sp4);
66 |
67 | var tf:TextField = new TextField();
68 | tf.text = "spritesheets-json";
69 | tf.width = 200;
70 | addChild(tf);
71 | }
72 |
73 | private function onAnimLoadedXML():void
74 | {
75 | var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData("data/xml/xmlformat.png");
76 | var sheets:* = AssetManager.getInstance().getContent("data/xml/xmlformat.xml");
77 | var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);
78 | sp.setAction("呼吸", 15);
79 | sp.play();
80 | sp.y = 150;
81 | addChild(sp);
82 |
83 | var sp1:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);
84 | sp1.x = 100;
85 | sp1.y = 150;
86 | sp1.setAction("打击", 14);
87 | sp1.play();
88 | addChild(sp1);
89 |
90 | var sp2:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);
91 | sp2.x = 250;
92 | sp2.y = 150;
93 | sp2.setAction("左行走", 14);
94 | sp2.play();
95 | addChild(sp2);
96 | sp2.buttonMode = true;
97 | sp2.addEventListener(MouseEvent.CLICK, onClickHandler);
98 |
99 | var sp3:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);
100 | sp3.x = 400;
101 | sp3.y = 150;
102 | sp3.setAction("挨打", 14);
103 | sp3.play();
104 | addChild(sp3);
105 |
106 | var sp4:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_XML);
107 | sp4.x = 550;
108 | sp4.y = 150;
109 | sp4.setAction("攻击", 30);
110 | sp4.play();
111 | addChild(sp4);
112 |
113 | var tf:TextField = new TextField();
114 | tf.width = 200;
115 | tf.text = "spritesheets-xml";
116 | tf.y = 150;
117 | addChild(tf);
118 | }
119 |
120 | private function onClickHandler(e:MouseEvent):void
121 | {
122 | var sp:SpriteSheet = e.currentTarget as SpriteSheet;
123 | trace("~~~~~~~~~~~~~~~~~~~~~~~~~~~", sp.isPlaying);
124 | if (sp.actionName == "打击")
125 | {
126 | sp.stop();
127 | sp.setAction("挨打", 14);
128 | sp.play();
129 | //sp.gotoAndStop(13);
130 | trace("===========================", sp.isPlaying);
131 | }
132 | else
133 | {
134 | sp.stop();
135 | sp.setAction("打击", 14);
136 | sp.play();
137 | //sp.play();
138 | //sp.gotoAndPlay(1);
139 | }
140 | }
141 |
142 | private function onAnimLoadedJsonArray():void
143 | {
144 | var bitmapData:BitmapData = AssetManager.getInstance().bulkLoader.getBitmapData("data/json-array/jsonarrayformat.png");
145 | var sheets:* = AssetManager.getInstance().getContent("data/json-array/jsonarrayformat.json");
146 | var sp:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON_ARRAY);
147 | sp.setAction("呼吸", 15);
148 | sp.play();
149 | sp.y = 300;
150 | addChild(sp);
151 |
152 | var sp1:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON_ARRAY);
153 | sp1.x = 100;
154 | sp1.y = 300;
155 | sp1.setAction("打击", 14);
156 | sp1.play();
157 | addChild(sp1);
158 |
159 | var sp2:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON_ARRAY);
160 | sp2.x = 250;
161 | sp2.y = 300;
162 | sp2.setAction("左行走", 14);
163 | sp2.play();
164 | addChild(sp2);
165 | sp2.buttonMode = true;
166 |
167 | var sp3:SpriteSheet = new SpriteSheet(bitmapData, sheets, DataFormat.FORMAT_JSON_ARRAY);
168 | sp3.x = 400;
169 | sp3.y = 300;
170 | sp3.setAction("挨打", 14);
171 | sp3.play();
172 | addChild(sp3);
173 |
174 | var tf:TextField = new TextField();
175 | tf.width = 200;
176 | tf.text = "spritesheets-jsonarray";
177 | tf.y = 300;
178 | addChild(tf);
179 | }
180 | }
181 |
182 | }
--------------------------------------------------------------------------------
/src/SWFProfiler.as:
--------------------------------------------------------------------------------
1 | package {
2 | import flash.display.*;
3 | import flash.events.*;
4 | import flash.net.LocalConnection;
5 | import flash.net.URLRequest;
6 | import flash.system.System;
7 | import flash.ui.*;
8 | import flash.utils.getTimer;
9 | import flash.net.navigateToURL
10 |
11 | /**
12 | * 右键菜单中加入profiler选项,点击能看到帧频监控
13 | * 用法:SWFProfiler.init(stage, this);
14 | * @author shanem
15 | */
16 | public class SWFProfiler {
17 | private static var itvTime : int;
18 | private static var initTime : int;
19 | private static var currentTime : int;
20 | private static var frameCount : int;
21 | private static var totalCount : int;
22 |
23 | public static var minFps : Number;
24 | public static var maxFps : Number;
25 | public static var minMem : Number;
26 | public static var maxMem : Number;
27 | public static var history : int = 60;
28 | public static var fpsList : Array = [];
29 | public static var memList : Array = [];
30 |
31 | private static var displayed : Boolean = false;
32 | private static var started : Boolean = false;
33 | private static var inited : Boolean = false;
34 | private static var frame : Sprite;
35 | private static var stage : Stage;
36 | private static var content : ProfilerContent;
37 | private static var ci : ContextMenuItem;
38 | private static var ryanMenuItem:ContextMenuItem
39 | private static var cm : ContextMenu
40 |
41 | public static function init(swf : Stage, context : InteractiveObject) : void {
42 | if(inited) return;
43 |
44 | inited = true;
45 | stage = swf;
46 |
47 |
48 | content = new ProfilerContent();
49 | frame = new Sprite();
50 |
51 |
52 | minFps = Number.MAX_VALUE;
53 | maxFps = Number.MIN_VALUE;
54 | minMem = Number.MAX_VALUE;
55 | maxMem = Number.MIN_VALUE;
56 |
57 | cm = new ContextMenu();
58 | cm.hideBuiltInItems();
59 | ci = new ContextMenuItem("Show Profiler");
60 | addEvent(ci, ContextMenuEvent.MENU_ITEM_SELECT, onSelect);
61 | cm.customItems = [ci,ryanMenuItem];
62 | context.contextMenu = cm
63 | start();
64 | }
65 |
66 | public static function start() : void {
67 | if(started) return;
68 |
69 | started = true;
70 | initTime = itvTime = getTimer();
71 | totalCount = frameCount = 0;
72 |
73 | addEvent(frame, Event.ENTER_FRAME, draw);
74 | }
75 |
76 | public static function stop() : void {
77 | if(!started) return;
78 |
79 | started = false;
80 |
81 | removeEvent(frame, Event.ENTER_FRAME, draw);
82 | }
83 |
84 | public static function gc() : void {
85 | try {
86 | new LocalConnection().connect('foo');
87 | new LocalConnection().connect('foo');
88 | } catch (e : Error) {
89 | }
90 | }
91 |
92 | public static function get currentFps() : Number {
93 | return frameCount / intervalTime;
94 | }
95 |
96 | public static function get currentMem() : Number {
97 | return (System.totalMemory / 1024) / 1000;
98 | }
99 |
100 | public static function get averageFps() : Number {
101 | return totalCount / runningTime;
102 | }
103 |
104 | private static function get runningTime() : Number {
105 | return (currentTime - initTime) / 1000;
106 | }
107 |
108 | private static function get intervalTime() : Number {
109 | return (currentTime - itvTime) / 1000;
110 | }
111 |
112 |
113 | private static function onSelect(e : ContextMenuEvent) : void {
114 | if(!displayed) {
115 | show();
116 | } else {
117 | hide();
118 | }
119 | }
120 |
121 | private static function onRyanSelect(e:ContextMenuEvent):void {
122 | navigateToURL(new URLRequest('http://www.ryan-liu.com/blog'),'_blank')
123 | }
124 |
125 | private static function show() : void {
126 | ci.caption = "Hide Profiler";
127 | displayed = true;
128 | addEvent(stage, Event.RESIZE, resize);
129 | stage.addChild(content);
130 | updateDisplay();
131 | }
132 |
133 | private static function hide() : void {
134 | ci.caption = "Show Profiler";
135 | displayed = false;
136 | removeEvent(stage, Event.RESIZE, resize);
137 | stage.removeChild(content);
138 | }
139 |
140 | private static function resize(e:Event) : void {
141 | content.update(runningTime, minFps, maxFps, minMem, maxMem, currentFps, currentMem, averageFps, fpsList, memList, history);
142 | }
143 |
144 | private static function draw(e : Event) : void {
145 | currentTime = getTimer();
146 |
147 | frameCount++;
148 | totalCount++;
149 |
150 | if(intervalTime >= 1) {
151 | if(displayed) {
152 | updateDisplay();
153 | } else {
154 | updateMinMax();
155 | }
156 |
157 | fpsList.unshift(currentFps);
158 | memList.unshift(currentMem);
159 |
160 | if(fpsList.length > history) fpsList.pop();
161 | if(memList.length > history) memList.pop();
162 |
163 | itvTime = currentTime;
164 | frameCount = 0;
165 | }
166 | }
167 |
168 | private static function updateDisplay() : void {
169 | updateMinMax();
170 | content.update(runningTime, minFps, maxFps, minMem, maxMem, currentFps, currentMem, averageFps, fpsList, memList, history);
171 | }
172 |
173 | private static function updateMinMax() : void {
174 | minFps = Math.min(currentFps, minFps);
175 | maxFps = Math.max(currentFps, maxFps);
176 |
177 | minMem = Math.min(currentMem, minMem);
178 | maxMem = Math.max(currentMem, maxMem);
179 | }
180 |
181 | private static function addEvent(item : EventDispatcher, type : String, listener : Function) : void {
182 | item.addEventListener(type, listener, false, 0, true);
183 | }
184 |
185 | private static function removeEvent(item : EventDispatcher, type : String, listener : Function) : void {
186 | item.removeEventListener(type, listener);
187 | }
188 | }
189 | }
190 |
191 | import flash.display.*;
192 | import flash.events.Event;
193 | import flash.text.*;
194 |
195 | internal class ProfilerContent extends Sprite {
196 |
197 | private var minFpsTxtBx : TextField;
198 | private var maxFpsTxtBx : TextField;
199 | private var minMemTxtBx : TextField;
200 | private var maxMemTxtBx : TextField;
201 | private var infoTxtBx : TextField;
202 | private var box : Shape;
203 | private var fps : Shape;
204 | private var mb : Shape;
205 |
206 | public function ProfilerContent() : void {
207 | fps = new Shape();
208 | mb = new Shape();
209 | box = new Shape();
210 |
211 | this.mouseChildren = false;
212 | this.mouseEnabled = false;
213 |
214 | fps.x = 65;
215 | fps.y = 45;
216 | mb.x = 65;
217 | mb.y = 90;
218 |
219 | var tf : TextFormat = new TextFormat("_sans", 9, 0xAAAAAA);
220 |
221 | infoTxtBx = new TextField();
222 | infoTxtBx.autoSize = TextFieldAutoSize.LEFT;
223 | infoTxtBx.defaultTextFormat = new TextFormat("_sans", 11, 0xCCCCCC);
224 | infoTxtBx.y = 98;
225 |
226 | minFpsTxtBx = new TextField();
227 | minFpsTxtBx.autoSize = TextFieldAutoSize.LEFT;
228 | minFpsTxtBx.defaultTextFormat = tf;
229 | minFpsTxtBx.x = 7;
230 | minFpsTxtBx.y = 37;
231 |
232 | maxFpsTxtBx = new TextField();
233 | maxFpsTxtBx.autoSize = TextFieldAutoSize.LEFT;
234 | maxFpsTxtBx.defaultTextFormat = tf;
235 | maxFpsTxtBx.x = 7;
236 | maxFpsTxtBx.y = 5;
237 |
238 | minMemTxtBx = new TextField();
239 | minMemTxtBx.autoSize = TextFieldAutoSize.LEFT;
240 | minMemTxtBx.defaultTextFormat = tf;
241 | minMemTxtBx.x = 7;
242 | minMemTxtBx.y = 83;
243 |
244 | maxMemTxtBx = new TextField();
245 | maxMemTxtBx.autoSize = TextFieldAutoSize.LEFT;
246 | maxMemTxtBx.defaultTextFormat = tf;
247 | maxMemTxtBx.x = 7;
248 | maxMemTxtBx.y = 50;
249 |
250 | addChild(box);
251 | addChild(infoTxtBx);
252 | addChild(minFpsTxtBx);
253 | addChild(maxFpsTxtBx);
254 | addChild(minMemTxtBx);
255 | addChild(maxMemTxtBx);
256 | addChild(fps);
257 | addChild(mb);
258 |
259 | this.addEventListener(Event.ADDED_TO_STAGE, added, false, 0, true);
260 | this.addEventListener(Event.REMOVED_FROM_STAGE, removed, false, 0, true);
261 | }
262 |
263 | public function update(runningTime : Number, minFps : Number, maxFps : Number, minMem : Number, maxMem : Number, currentFps : Number, currentMem : Number, averageFps : Number, fpsList : Array, memList : Array, history : int) : void {
264 | if(runningTime >= 1) {
265 | minFpsTxtBx.text = minFps.toFixed(3) + " Fps";
266 | maxFpsTxtBx.text = maxFps.toFixed(3) + " Fps";
267 | minMemTxtBx.text = minMem.toFixed(3) + " Mb";
268 | maxMemTxtBx.text = maxMem.toFixed(3) + " Mb";
269 | }
270 |
271 | infoTxtBx.text = "Current Fps " + currentFps.toFixed(3) + " | Average Fps " + averageFps.toFixed(3) + " | Memory Used " + currentMem.toFixed(3) + " Mb";
272 | infoTxtBx.x = stage.stageWidth - infoTxtBx.width - 20;
273 |
274 | var vec : Graphics = fps.graphics;
275 | vec.clear();
276 | vec.lineStyle(1, 0x33FF00, 0.7);
277 |
278 | var i : int = 0;
279 | var len : int = fpsList.length;
280 | var height : int = 35;
281 | var width : int = stage.stageWidth - 80;
282 | var inc : Number = width / (history - 1);
283 | var rateRange : Number = maxFps - minFps;
284 | var value : Number;
285 |
286 | for(i = 0;i < len; i++) {
287 | value = (fpsList[i] - minFps) / rateRange;
288 | if(i == 0) {
289 | vec.moveTo(0, -value * height);
290 | } else {
291 | vec.lineTo(i * inc, -value * height);
292 | }
293 | }
294 |
295 | vec = mb.graphics;
296 | vec.clear();
297 | vec.lineStyle(1, 0x0066FF, 0.7);
298 |
299 | i = 0;
300 | len = memList.length;
301 | rateRange = maxMem - minMem;
302 | for(i = 0;i < len; i++) {
303 | value = (memList[i] - minMem) / rateRange;
304 | if(i == 0) {
305 | vec.moveTo(0, -value * height);
306 | } else {
307 | vec.lineTo(i * inc, -value * height);
308 | }
309 | }
310 | }
311 |
312 | private function added(e : Event) : void {
313 | resize();
314 | stage.addEventListener(Event.RESIZE, resize, false, 0, true);
315 | }
316 |
317 | private function removed(e : Event) : void {
318 | stage.removeEventListener(Event.RESIZE, resize);
319 | }
320 |
321 | private function resize(e : Event = null) : void {
322 | var vec : Graphics = box.graphics;
323 | vec.clear();
324 |
325 | vec.beginFill(0x000000, 0.5);
326 | vec.drawRect(0, 0, stage.stageWidth, 120);
327 | vec.lineStyle(1, 0xFFFFFF, 0.2);
328 |
329 | vec.moveTo(65, 45);
330 | vec.lineTo(65, 10);
331 | vec.moveTo(65, 45);
332 | vec.lineTo(stage.stageWidth - 15, 45);
333 |
334 | vec.moveTo(65, 90);
335 | vec.lineTo(65, 55);
336 | vec.moveTo(65, 90);
337 | vec.lineTo(stage.stageWidth - 15, 90);
338 |
339 | vec.endFill();
340 |
341 | infoTxtBx.x = stage.stageWidth - infoTxtBx.width - 20;
342 | }
343 | }
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONEncoder.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | import flash.utils.describeType;
37 |
38 | public class JSONEncoder
39 | {
40 |
41 | /** The string that is going to represent the object we're encoding */
42 | private var jsonString:String;
43 |
44 | /**
45 | * Creates a new JSONEncoder.
46 | *
47 | * @param o The object to encode as a JSON string
48 | * @langversion ActionScript 3.0
49 | * @playerversion Flash 9.0
50 | * @tiptext
51 | */
52 | public function JSONEncoder( value:* )
53 | {
54 | jsonString = convertToString( value );
55 | }
56 |
57 | /**
58 | * Gets the JSON string from the encoder.
59 | *
60 | * @return The JSON string representation of the object
61 | * that was passed to the constructor
62 | * @langversion ActionScript 3.0
63 | * @playerversion Flash 9.0
64 | * @tiptext
65 | */
66 | public function getString():String
67 | {
68 | return jsonString;
69 | }
70 |
71 | /**
72 | * Converts a value to it's JSON string equivalent.
73 | *
74 | * @param value The value to convert. Could be any
75 | * type (object, number, array, etc)
76 | */
77 | private function convertToString( value:* ):String
78 | {
79 | // determine what value is and convert it based on it's type
80 | if ( value is String )
81 | {
82 | // escape the string so it's formatted correctly
83 | return escapeString( value as String );
84 | }
85 | else if ( value is Number )
86 | {
87 | // only encode numbers that finate
88 | return isFinite( value as Number ) ? value.toString() : "null";
89 | }
90 | else if ( value is Boolean )
91 | {
92 | // convert boolean to string easily
93 | return value ? "true" : "false";
94 | }
95 | else if ( value is Array )
96 | {
97 | // call the helper method to convert an array
98 | return arrayToString( value as Array );
99 | }
100 | else if ( value is Object && value != null )
101 | {
102 | // call the helper method to convert an object
103 | return objectToString( value );
104 | }
105 |
106 | return "null";
107 | }
108 |
109 | /**
110 | * Escapes a string accoding to the JSON specification.
111 | *
112 | * @param str The string to be escaped
113 | * @return The string with escaped special characters
114 | * according to the JSON specification
115 | */
116 | private function escapeString( str:String ):String
117 | {
118 | // create a string to store the string's jsonstring value
119 | var s:String = "";
120 | // current character in the string we're processing
121 | var ch:String;
122 | // store the length in a local variable to reduce lookups
123 | var len:Number = str.length;
124 |
125 | // loop over all of the characters in the string
126 | for ( var i:int = 0; i < len; i++ )
127 | {
128 | // examine the character to determine if we have to escape it
129 | ch = str.charAt( i );
130 | switch ( ch )
131 | {
132 | case '"': // quotation mark
133 | s += "\\\"";
134 | break;
135 |
136 | //case '/': // solidus
137 | // s += "\\/";
138 | // break;
139 |
140 | case '\\': // reverse solidus
141 | s += "\\\\";
142 | break;
143 |
144 | case '\b': // bell
145 | s += "\\b";
146 | break;
147 |
148 | case '\f': // form feed
149 | s += "\\f";
150 | break;
151 |
152 | case '\n': // newline
153 | s += "\\n";
154 | break;
155 |
156 | case '\r': // carriage return
157 | s += "\\r";
158 | break;
159 |
160 | case '\t': // horizontal tab
161 | s += "\\t";
162 | break;
163 |
164 | default: // everything else
165 |
166 | // check for a control character and escape as unicode
167 | if ( ch < ' ' )
168 | {
169 | // get the hex digit(s) of the character (either 1 or 2 digits)
170 | var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
171 |
172 | // ensure that there are 4 digits by adjusting
173 | // the # of zeros accordingly.
174 | var zeroPad:String = hexCode.length == 2 ? "00" : "000";
175 |
176 | // create the unicode escape sequence with 4 hex digits
177 | s += "\\u" + zeroPad + hexCode;
178 | }
179 | else
180 | {
181 |
182 | // no need to do any special encoding, just pass-through
183 | s += ch;
184 |
185 | }
186 | } // end switch
187 |
188 | } // end for loop
189 |
190 | return "\"" + s + "\"";
191 | }
192 |
193 | /**
194 | * Converts an array to it's JSON string equivalent
195 | *
196 | * @param a The array to convert
197 | * @return The JSON string representation of a
198 | */
199 | private function arrayToString( a:Array ):String
200 | {
201 | // create a string to store the array's jsonstring value
202 | var s:String = "";
203 |
204 | // loop over the elements in the array and add their converted
205 | // values to the string
206 | var length:int = a.length;
207 | for ( var i:int = 0; i < length; i++ )
208 | {
209 | // when the length is 0 we're adding the first element so
210 | // no comma is necessary
211 | if ( s.length > 0 )
212 | {
213 | // we've already added an element, so add the comma separator
214 | s += ","
215 | }
216 |
217 | // convert the value to a string
218 | s += convertToString( a[ i ] );
219 | }
220 |
221 | // KNOWN ISSUE: In ActionScript, Arrays can also be associative
222 | // objects and you can put anything in them, ie:
223 | // myArray["foo"] = "bar";
224 | //
225 | // These properties aren't picked up in the for loop above because
226 | // the properties don't correspond to indexes. However, we're
227 | // sort of out luck because the JSON specification doesn't allow
228 | // these types of array properties.
229 | //
230 | // So, if the array was also used as an associative object, there
231 | // may be some values in the array that don't get properly encoded.
232 | //
233 | // A possible solution is to instead encode the Array as an Object
234 | // but then it won't get decoded correctly (and won't be an
235 | // Array instance)
236 |
237 | // close the array and return it's string value
238 | return "[" + s + "]";
239 | }
240 |
241 | /**
242 | * Converts an object to it's JSON string equivalent
243 | *
244 | * @param o The object to convert
245 | * @return The JSON string representation of o
246 | */
247 | private function objectToString( o:Object ):String
248 | {
249 | // create a string to store the object's jsonstring value
250 | var s:String = "";
251 |
252 | // determine if o is a class instance or a plain object
253 | var classInfo:XML = describeType( o );
254 | if ( classInfo.@name.toString() == "Object" )
255 | {
256 | // the value of o[key] in the loop below - store this
257 | // as a variable so we don't have to keep looking up o[key]
258 | // when testing for valid values to convert
259 | var value:Object;
260 |
261 | // loop over the keys in the object and add their converted
262 | // values to the string
263 | for ( var key:String in o )
264 | {
265 | // assign value to a variable for quick lookup
266 | value = o[ key ];
267 |
268 | // don't add function's to the JSON string
269 | if ( value is Function )
270 | {
271 | // skip this key and try another
272 | continue;
273 | }
274 |
275 | // when the length is 0 we're adding the first item so
276 | // no comma is necessary
277 | if ( s.length > 0 )
278 | {
279 | // we've already added an item, so add the comma separator
280 | s += ","
281 | }
282 |
283 | s += escapeString( key ) + ":" + convertToString( value );
284 | }
285 | }
286 | else // o is a class instance
287 | {
288 | // Loop over all of the variables and accessors in the class and
289 | // serialize them along with their values.
290 | for each ( var v:XML in classInfo..*.(
291 | name() == "variable"
292 | ||
293 | (
294 | name() == "accessor"
295 | // Issue #116 - Make sure accessors are readable
296 | && attribute( "access" ).charAt( 0 ) == "r" )
297 | ) )
298 | {
299 | // Issue #110 - If [Transient] metadata exists, then we should skip
300 | if ( v.metadata && v.metadata.( @name == "Transient" ).length() > 0 )
301 | {
302 | continue;
303 | }
304 |
305 | // When the length is 0 we're adding the first item so
306 | // no comma is necessary
307 | if ( s.length > 0 )
308 | {
309 | // We've already added an item, so add the comma separator
310 | s += ","
311 | }
312 |
313 | s += escapeString( v.@name.toString() ) + ":"
314 | + convertToString( o[ v.@name ] );
315 | }
316 | }
317 |
318 | return "{" + s + "}";
319 | }
320 |
321 | }
322 |
323 | }
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONDecoder.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | public class JSONDecoder
37 | {
38 |
39 | /**
40 | * Flag indicating if the parser should be strict about the format
41 | * of the JSON string it is attempting to decode.
42 | */
43 | private var strict:Boolean;
44 |
45 | /** The value that will get parsed from the JSON string */
46 | private var value:*;
47 |
48 | /** The tokenizer designated to read the JSON string */
49 | private var tokenizer:JSONTokenizer;
50 |
51 | /** The current token from the tokenizer */
52 | private var token:JSONToken;
53 |
54 | /**
55 | * Constructs a new JSONDecoder to parse a JSON string
56 | * into a native object.
57 | *
58 | * @param s The JSON string to be converted
59 | * into a native object
60 | * @param strict Flag indicating if the JSON string needs to
61 | * strictly match the JSON standard or not.
62 | * @langversion ActionScript 3.0
63 | * @playerversion Flash 9.0
64 | * @tiptext
65 | */
66 | public function JSONDecoder( s:String, strict:Boolean )
67 | {
68 | this.strict = strict;
69 | tokenizer = new JSONTokenizer( s, strict );
70 |
71 | nextToken();
72 | value = parseValue();
73 |
74 | // Make sure the input stream is empty
75 | if ( strict && nextToken() != null )
76 | {
77 | tokenizer.parseError( "Unexpected characters left in input stream" );
78 | }
79 | }
80 |
81 | /**
82 | * Gets the internal object that was created by parsing
83 | * the JSON string passed to the constructor.
84 | *
85 | * @return The internal object representation of the JSON
86 | * string that was passed to the constructor
87 | * @langversion ActionScript 3.0
88 | * @playerversion Flash 9.0
89 | * @tiptext
90 | */
91 | public function getValue():*
92 | {
93 | return value;
94 | }
95 |
96 | /**
97 | * Returns the next token from the tokenzier reading
98 | * the JSON string
99 | */
100 | private final function nextToken():JSONToken
101 | {
102 | return token = tokenizer.getNextToken();
103 | }
104 |
105 | /**
106 | * Returns the next token from the tokenizer reading
107 | * the JSON string and verifies that the token is valid.
108 | */
109 | private final function nextValidToken():JSONToken
110 | {
111 | token = tokenizer.getNextToken();
112 | checkValidToken();
113 |
114 | return token;
115 | }
116 |
117 | /**
118 | * Verifies that the token is valid.
119 | */
120 | private final function checkValidToken():void
121 | {
122 | // Catch errors when the input stream ends abruptly
123 | if ( token == null )
124 | {
125 | tokenizer.parseError( "Unexpected end of input" );
126 | }
127 | }
128 |
129 | /**
130 | * Attempt to parse an array.
131 | */
132 | private final function parseArray():Array
133 | {
134 | // create an array internally that we're going to attempt
135 | // to parse from the tokenizer
136 | var a:Array = new Array();
137 |
138 | // grab the next token from the tokenizer to move
139 | // past the opening [
140 | nextValidToken();
141 |
142 | // check to see if we have an empty array
143 | if ( token.type == JSONTokenType.RIGHT_BRACKET )
144 | {
145 | // we're done reading the array, so return it
146 | return a;
147 | }
148 | // in non-strict mode an empty array is also a comma
149 | // followed by a right bracket
150 | else if ( !strict && token.type == JSONTokenType.COMMA )
151 | {
152 | // move past the comma
153 | nextValidToken();
154 |
155 | // check to see if we're reached the end of the array
156 | if ( token.type == JSONTokenType.RIGHT_BRACKET )
157 | {
158 | return a;
159 | }
160 | else
161 | {
162 | tokenizer.parseError( "Leading commas are not supported. Expecting ']' but found " + token.value );
163 | }
164 | }
165 |
166 | // deal with elements of the array, and use an "infinite"
167 | // loop because we could have any amount of elements
168 | while ( true )
169 | {
170 | // read in the value and add it to the array
171 | a.push( parseValue() );
172 |
173 | // after the value there should be a ] or a ,
174 | nextValidToken();
175 |
176 | if ( token.type == JSONTokenType.RIGHT_BRACKET )
177 | {
178 | // we're done reading the array, so return it
179 | return a;
180 | }
181 | else if ( token.type == JSONTokenType.COMMA )
182 | {
183 | // move past the comma and read another value
184 | nextToken();
185 |
186 | // Allow arrays to have a comma after the last element
187 | // if the decoder is not in strict mode
188 | if ( !strict )
189 | {
190 | checkValidToken();
191 |
192 | // Reached ",]" as the end of the array, so return it
193 | if ( token.type == JSONTokenType.RIGHT_BRACKET )
194 | {
195 | return a;
196 | }
197 | }
198 | }
199 | else
200 | {
201 | tokenizer.parseError( "Expecting ] or , but found " + token.value );
202 | }
203 | }
204 |
205 | return null;
206 | }
207 |
208 | /**
209 | * Attempt to parse an object.
210 | */
211 | private final function parseObject():Object
212 | {
213 | // create the object internally that we're going to
214 | // attempt to parse from the tokenizer
215 | var o:Object = new Object();
216 |
217 | // store the string part of an object member so
218 | // that we can assign it a value in the object
219 | var key:String
220 |
221 | // grab the next token from the tokenizer
222 | nextValidToken();
223 |
224 | // check to see if we have an empty object
225 | if ( token.type == JSONTokenType.RIGHT_BRACE )
226 | {
227 | // we're done reading the object, so return it
228 | return o;
229 | }
230 | // in non-strict mode an empty object is also a comma
231 | // followed by a right bracket
232 | else if ( !strict && token.type == JSONTokenType.COMMA )
233 | {
234 | // move past the comma
235 | nextValidToken();
236 |
237 | // check to see if we're reached the end of the object
238 | if ( token.type == JSONTokenType.RIGHT_BRACE )
239 | {
240 | return o;
241 | }
242 | else
243 | {
244 | tokenizer.parseError( "Leading commas are not supported. Expecting '}' but found " + token.value );
245 | }
246 | }
247 |
248 | // deal with members of the object, and use an "infinite"
249 | // loop because we could have any amount of members
250 | while ( true )
251 | {
252 | if ( token.type == JSONTokenType.STRING )
253 | {
254 | // the string value we read is the key for the object
255 | key = String( token.value );
256 |
257 | // move past the string to see what's next
258 | nextValidToken();
259 |
260 | // after the string there should be a :
261 | if ( token.type == JSONTokenType.COLON )
262 | {
263 | // move past the : and read/assign a value for the key
264 | nextToken();
265 | o[ key ] = parseValue();
266 |
267 | // move past the value to see what's next
268 | nextValidToken();
269 |
270 | // after the value there's either a } or a ,
271 | if ( token.type == JSONTokenType.RIGHT_BRACE )
272 | {
273 | // we're done reading the object, so return it
274 | return o;
275 | }
276 | else if ( token.type == JSONTokenType.COMMA )
277 | {
278 | // skip past the comma and read another member
279 | nextToken();
280 |
281 | // Allow objects to have a comma after the last member
282 | // if the decoder is not in strict mode
283 | if ( !strict )
284 | {
285 | checkValidToken();
286 |
287 | // Reached ",}" as the end of the object, so return it
288 | if ( token.type == JSONTokenType.RIGHT_BRACE )
289 | {
290 | return o;
291 | }
292 | }
293 | }
294 | else
295 | {
296 | tokenizer.parseError( "Expecting } or , but found " + token.value );
297 | }
298 | }
299 | else
300 | {
301 | tokenizer.parseError( "Expecting : but found " + token.value );
302 | }
303 | }
304 | else
305 | {
306 | tokenizer.parseError( "Expecting string but found " + token.value );
307 | }
308 | }
309 | return null;
310 | }
311 |
312 | /**
313 | * Attempt to parse a value
314 | */
315 | private final function parseValue():Object
316 | {
317 | checkValidToken();
318 |
319 | switch ( token.type )
320 | {
321 | case JSONTokenType.LEFT_BRACE:
322 | return parseObject();
323 |
324 | case JSONTokenType.LEFT_BRACKET:
325 | return parseArray();
326 |
327 | case JSONTokenType.STRING:
328 | case JSONTokenType.NUMBER:
329 | case JSONTokenType.TRUE:
330 | case JSONTokenType.FALSE:
331 | case JSONTokenType.NULL:
332 | return token.value;
333 |
334 | case JSONTokenType.NAN:
335 | if ( !strict )
336 | {
337 | return token.value;
338 | }
339 | else
340 | {
341 | tokenizer.parseError( "Unexpected " + token.value );
342 | }
343 |
344 | default:
345 | tokenizer.parseError( "Unexpected " + token.value );
346 |
347 | }
348 |
349 | return null;
350 | }
351 | }
352 | }
353 |
--------------------------------------------------------------------------------
/src/com/adobe/serialization/json/JSONTokenizer.as:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008, Adobe Systems Incorporated
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright notice,
10 | this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Adobe Systems Incorporated nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 |
33 | package com.adobe.serialization.json
34 | {
35 |
36 | public class JSONTokenizer
37 | {
38 |
39 | /**
40 | * Flag indicating if the tokenizer should only recognize
41 | * standard JSON tokens. Setting to false allows
42 | * tokens such as NaN and allows numbers to be formatted as
43 | * hex, etc.
44 | */
45 | private var strict:Boolean;
46 |
47 | /** The object that will get parsed from the JSON string */
48 | private var obj:Object;
49 |
50 | /** The JSON string to be parsed */
51 | private var jsonString:String;
52 |
53 | /** The current parsing location in the JSON string */
54 | private var loc:int;
55 |
56 | /** The current character in the JSON string during parsing */
57 | private var ch:String;
58 |
59 | /**
60 | * The regular expression used to make sure the string does not
61 | * contain invalid control characters.
62 | */
63 | private const controlCharsRegExp:RegExp = /[\x00-\x1F]/;
64 |
65 | /**
66 | * Constructs a new JSONDecoder to parse a JSON string
67 | * into a native object.
68 | *
69 | * @param s The JSON string to be converted
70 | * into a native object
71 | */
72 | public function JSONTokenizer( s:String, strict:Boolean )
73 | {
74 | jsonString = s;
75 | this.strict = strict;
76 | loc = 0;
77 |
78 | // prime the pump by getting the first character
79 | nextChar();
80 | }
81 |
82 | /**
83 | * Gets the next token in the input sting and advances
84 | * the character to the next character after the token
85 | */
86 | public function getNextToken():JSONToken
87 | {
88 | var token:JSONToken = null;
89 |
90 | // skip any whitespace / comments since the last
91 | // token was read
92 | skipIgnored();
93 |
94 | // examine the new character and see what we have...
95 | switch ( ch )
96 | {
97 | case '{':
98 | token = JSONToken.create( JSONTokenType.LEFT_BRACE, ch );
99 | nextChar();
100 | break
101 |
102 | case '}':
103 | token = JSONToken.create( JSONTokenType.RIGHT_BRACE, ch );
104 | nextChar();
105 | break
106 |
107 | case '[':
108 | token = JSONToken.create( JSONTokenType.LEFT_BRACKET, ch );
109 | nextChar();
110 | break
111 |
112 | case ']':
113 | token = JSONToken.create( JSONTokenType.RIGHT_BRACKET, ch );
114 | nextChar();
115 | break
116 |
117 | case ',':
118 | token = JSONToken.create( JSONTokenType.COMMA, ch );
119 | nextChar();
120 | break
121 |
122 | case ':':
123 | token = JSONToken.create( JSONTokenType.COLON, ch );
124 | nextChar();
125 | break;
126 |
127 | case 't': // attempt to read true
128 | var possibleTrue:String = "t" + nextChar() + nextChar() + nextChar();
129 |
130 | if ( possibleTrue == "true" )
131 | {
132 | token = JSONToken.create( JSONTokenType.TRUE, true );
133 | nextChar();
134 | }
135 | else
136 | {
137 | parseError( "Expecting 'true' but found " + possibleTrue );
138 | }
139 |
140 | break;
141 |
142 | case 'f': // attempt to read false
143 | var possibleFalse:String = "f" + nextChar() + nextChar() + nextChar() + nextChar();
144 |
145 | if ( possibleFalse == "false" )
146 | {
147 | token = JSONToken.create( JSONTokenType.FALSE, false );
148 | nextChar();
149 | }
150 | else
151 | {
152 | parseError( "Expecting 'false' but found " + possibleFalse );
153 | }
154 |
155 | break;
156 |
157 | case 'n': // attempt to read null
158 | var possibleNull:String = "n" + nextChar() + nextChar() + nextChar();
159 |
160 | if ( possibleNull == "null" )
161 | {
162 | token = JSONToken.create( JSONTokenType.NULL, null );
163 | nextChar();
164 | }
165 | else
166 | {
167 | parseError( "Expecting 'null' but found " + possibleNull );
168 | }
169 |
170 | break;
171 |
172 | case 'N': // attempt to read NaN
173 | var possibleNaN:String = "N" + nextChar() + nextChar();
174 |
175 | if ( possibleNaN == "NaN" )
176 | {
177 | token = JSONToken.create( JSONTokenType.NAN, NaN );
178 | nextChar();
179 | }
180 | else
181 | {
182 | parseError( "Expecting 'NaN' but found " + possibleNaN );
183 | }
184 |
185 | break;
186 |
187 | case '"': // the start of a string
188 | token = readString();
189 | break;
190 |
191 | default:
192 | // see if we can read a number
193 | if ( isDigit( ch ) || ch == '-' )
194 | {
195 | token = readNumber();
196 | }
197 | else if ( ch == '' )
198 | {
199 | // check for reading past the end of the string
200 | token = null;
201 | }
202 | else
203 | {
204 | // not sure what was in the input string - it's not
205 | // anything we expected
206 | parseError( "Unexpected " + ch + " encountered" );
207 | }
208 | }
209 |
210 | return token;
211 | }
212 |
213 | /**
214 | * Attempts to read a string from the input string. Places
215 | * the character location at the first character after the
216 | * string. It is assumed that ch is " before this method is called.
217 | *
218 | * @return the JSONToken with the string value if a string could
219 | * be read. Throws an error otherwise.
220 | */
221 | private final function readString():JSONToken
222 | {
223 | // Rather than examine the string character-by-character, it's
224 | // faster to use indexOf to try to and find the closing quote character
225 | // and then replace escape sequences after the fact.
226 |
227 | // Start at the current input stream position
228 | var quoteIndex:int = loc;
229 | do
230 | {
231 | // Find the next quote in the input stream
232 | quoteIndex = jsonString.indexOf( "\"", quoteIndex );
233 |
234 | if ( quoteIndex >= 0 )
235 | {
236 | // We found the next double quote character in the string, but we need
237 | // to make sure it is not part of an escape sequence.
238 |
239 | // Keep looping backwards while the previous character is a backslash
240 | var backspaceCount:int = 0;
241 | var backspaceIndex:int = quoteIndex - 1;
242 | while ( jsonString.charAt( backspaceIndex ) == "\\" )
243 | {
244 | backspaceCount++;
245 | backspaceIndex--;
246 | }
247 |
248 | // If we have an even number of backslashes, that means this is the ending quote
249 | if ( ( backspaceCount & 1 ) == 0 )
250 | {
251 | break;
252 | }
253 |
254 | // At this point, the quote was determined to be part of an escape sequence
255 | // so we need to move past the quote index to look for the next one
256 | quoteIndex++;
257 | }
258 | else // There are no more quotes in the string and we haven't found the end yet
259 | {
260 | parseError( "Unterminated string literal" );
261 | }
262 | } while ( true );
263 |
264 | // Unescape the string
265 | // the token for the string we'll try to read
266 | var token:JSONToken = JSONToken.create(
267 | JSONTokenType.STRING,
268 | // Attach resulting string to the token to return it
269 | unescapeString( jsonString.substr( loc, quoteIndex - loc ) ) );
270 |
271 | // Move past the closing quote in the input string. This updates the next
272 | // character in the input stream to be the character one after the closing quote
273 | loc = quoteIndex + 1;
274 | nextChar();
275 |
276 | return token;
277 | }
278 |
279 | /**
280 | * Convert all JavaScript escape characters into normal characters
281 | *
282 | * @param input The input string to convert
283 | * @return Original string with escape characters replaced by real characters
284 | */
285 | public function unescapeString( input:String ):String
286 | {
287 | // Issue #104 - If the string contains any unescaped control characters, this
288 | // is an error in strict mode
289 | if ( strict && controlCharsRegExp.test( input ) )
290 | {
291 | parseError( "String contains unescaped control character (0x00-0x1F)" );
292 | }
293 |
294 | var result:String = "";
295 | var backslashIndex:int = 0;
296 | var nextSubstringStartPosition:int = 0;
297 | var len:int = input.length;
298 | do
299 | {
300 | // Find the next backslash in the input
301 | backslashIndex = input.indexOf( '\\', nextSubstringStartPosition );
302 |
303 | if ( backslashIndex >= 0 )
304 | {
305 | result += input.substr( nextSubstringStartPosition, backslashIndex - nextSubstringStartPosition );
306 |
307 | // Move past the backslash and next character (all escape sequences are
308 | // two characters, except for \u, which will advance this further)
309 | nextSubstringStartPosition = backslashIndex + 2;
310 |
311 | // Check the next character so we know what to escape
312 | var escapedChar:String = input.charAt( backslashIndex + 1 );
313 | switch ( escapedChar )
314 | {
315 | // Try to list the most common expected cases first to improve performance
316 |
317 | case '"':
318 | result += escapedChar;
319 | break; // quotation mark
320 | case '\\':
321 | result += escapedChar;
322 | break; // reverse solidus
323 | case 'n':
324 | result += '\n';
325 | break; // newline
326 | case 'r':
327 | result += '\r';
328 | break; // carriage return
329 | case 't':
330 | result += '\t';
331 | break; // horizontal tab
332 |
333 | // Convert a unicode escape sequence to it's character value
334 | case 'u':
335 |
336 | // Save the characters as a string we'll convert to an int
337 | var hexValue:String = "";
338 |
339 | var unicodeEndPosition:int = nextSubstringStartPosition + 4;
340 |
341 | // Make sure there are enough characters in the string leftover
342 | if ( unicodeEndPosition > len )
343 | {
344 | parseError( "Unexpected end of input. Expecting 4 hex digits after \\u." );
345 | }
346 |
347 | // Try to find 4 hex characters
348 | for ( var i:int = nextSubstringStartPosition; i < unicodeEndPosition; i++ )
349 | {
350 | // get the next character and determine
351 | // if it's a valid hex digit or not
352 | var possibleHexChar:String = input.charAt( i );
353 | if ( !isHexDigit( possibleHexChar ) )
354 | {
355 | parseError( "Excepted a hex digit, but found: " + possibleHexChar );
356 | }
357 |
358 | // Valid hex digit, add it to the value
359 | hexValue += possibleHexChar;
360 | }
361 |
362 | // Convert hexValue to an integer, and use that
363 | // integer value to create a character to add
364 | // to our string.
365 | result += String.fromCharCode( parseInt( hexValue, 16 ) );
366 |
367 | // Move past the 4 hex digits that we just read
368 | nextSubstringStartPosition = unicodeEndPosition;
369 | break;
370 |
371 | case 'f':
372 | result += '\f';
373 | break; // form feed
374 | case '/':
375 | result += '/';
376 | break; // solidus
377 | case 'b':
378 | result += '\b';
379 | break; // bell
380 | default:
381 | result += '\\' + escapedChar; // Couldn't unescape the sequence, so just pass it through
382 | }
383 | }
384 | else
385 | {
386 | // No more backslashes to replace, append the rest of the string
387 | result += input.substr( nextSubstringStartPosition );
388 | break;
389 | }
390 |
391 | } while ( nextSubstringStartPosition < len );
392 |
393 | return result;
394 | }
395 |
396 | /**
397 | * Attempts to read a number from the input string. Places
398 | * the character location at the first character after the
399 | * number.
400 | *
401 | * @return The JSONToken with the number value if a number could
402 | * be read. Throws an error otherwise.
403 | */
404 | private final function readNumber():JSONToken
405 | {
406 | // the string to accumulate the number characters
407 | // into that we'll convert to a number at the end
408 | var input:String = "";
409 |
410 | // check for a negative number
411 | if ( ch == '-' )
412 | {
413 | input += '-';
414 | nextChar();
415 | }
416 |
417 | // the number must start with a digit
418 | if ( !isDigit( ch ) )
419 | {
420 | parseError( "Expecting a digit" );
421 | }
422 |
423 | // 0 can only be the first digit if it
424 | // is followed by a decimal point
425 | if ( ch == '0' )
426 | {
427 | input += ch;
428 | nextChar();
429 |
430 | // make sure no other digits come after 0
431 | if ( isDigit( ch ) )
432 | {
433 | parseError( "A digit cannot immediately follow 0" );
434 | }
435 | // unless we have 0x which starts a hex number, but this
436 | // doesn't match JSON spec so check for not strict mode.
437 | else if ( !strict && ch == 'x' )
438 | {
439 | // include the x in the input
440 | input += ch;
441 | nextChar();
442 |
443 | // need at least one hex digit after 0x to
444 | // be valid
445 | if ( isHexDigit( ch ) )
446 | {
447 | input += ch;
448 | nextChar();
449 | }
450 | else
451 | {
452 | parseError( "Number in hex format require at least one hex digit after \"0x\"" );
453 | }
454 |
455 | // consume all of the hex values
456 | while ( isHexDigit( ch ) )
457 | {
458 | input += ch;
459 | nextChar();
460 | }
461 | }
462 | }
463 | else
464 | {
465 | // read numbers while we can
466 | while ( isDigit( ch ) )
467 | {
468 | input += ch;
469 | nextChar();
470 | }
471 | }
472 |
473 | // check for a decimal value
474 | if ( ch == '.' )
475 | {
476 | input += '.';
477 | nextChar();
478 |
479 | // after the decimal there has to be a digit
480 | if ( !isDigit( ch ) )
481 | {
482 | parseError( "Expecting a digit" );
483 | }
484 |
485 | // read more numbers to get the decimal value
486 | while ( isDigit( ch ) )
487 | {
488 | input += ch;
489 | nextChar();
490 | }
491 | }
492 |
493 | // check for scientific notation
494 | if ( ch == 'e' || ch == 'E' )
495 | {
496 | input += "e"
497 | nextChar();
498 | // check for sign
499 | if ( ch == '+' || ch == '-' )
500 | {
501 | input += ch;
502 | nextChar();
503 | }
504 |
505 | // require at least one number for the exponent
506 | // in this case
507 | if ( !isDigit( ch ) )
508 | {
509 | parseError( "Scientific notation number needs exponent value" );
510 | }
511 |
512 | // read in the exponent
513 | while ( isDigit( ch ) )
514 | {
515 | input += ch;
516 | nextChar();
517 | }
518 | }
519 |
520 | // convert the string to a number value
521 | var num:Number = Number( input );
522 |
523 | if ( isFinite( num ) && !isNaN( num ) )
524 | {
525 | // the token for the number that we've read
526 | return JSONToken.create( JSONTokenType.NUMBER, num );
527 | }
528 | else
529 | {
530 | parseError( "Number " + num + " is not valid!" );
531 | }
532 |
533 | return null;
534 | }
535 |
536 | /**
537 | * Reads the next character in the input
538 | * string and advances the character location.
539 | *
540 | * @return The next character in the input string, or
541 | * null if we've read past the end.
542 | */
543 | private final function nextChar():String
544 | {
545 | return ch = jsonString.charAt( loc++ );
546 | }
547 |
548 | /**
549 | * Advances the character location past any
550 | * sort of white space and comments
551 | */
552 | private final function skipIgnored():void
553 | {
554 | var originalLoc:int;
555 |
556 | // keep trying to skip whitespace and comments as long
557 | // as we keep advancing past the original location
558 | do
559 | {
560 | originalLoc = loc;
561 | skipWhite();
562 | skipComments();
563 | } while ( originalLoc != loc );
564 | }
565 |
566 | /**
567 | * Skips comments in the input string, either
568 | * single-line or multi-line. Advances the character
569 | * to the first position after the end of the comment.
570 | */
571 | private function skipComments():void
572 | {
573 | if ( ch == '/' )
574 | {
575 | // Advance past the first / to find out what type of comment
576 | nextChar();
577 | switch ( ch )
578 | {
579 | case '/': // single-line comment, read through end of line
580 |
581 | // Loop over the characters until we find
582 | // a newline or until there's no more characters left
583 | do
584 | {
585 | nextChar();
586 | } while ( ch != '\n' && ch != '' )
587 |
588 | // move past the \n
589 | nextChar();
590 |
591 | break;
592 |
593 | case '*': // multi-line comment, read until closing */
594 |
595 | // move past the opening *
596 | nextChar();
597 |
598 | // try to find a trailing */
599 | while ( true )
600 | {
601 | if ( ch == '*' )
602 | {
603 | // check to see if we have a closing /
604 | nextChar();
605 | if ( ch == '/' )
606 | {
607 | // move past the end of the closing */
608 | nextChar();
609 | break;
610 | }
611 | }
612 | else
613 | {
614 | // move along, looking if the next character is a *
615 | nextChar();
616 | }
617 |
618 | // when we're here we've read past the end of
619 | // the string without finding a closing */, so error
620 | if ( ch == '' )
621 | {
622 | parseError( "Multi-line comment not closed" );
623 | }
624 | }
625 |
626 | break;
627 |
628 | // Can't match a comment after a /, so it's a parsing error
629 | default:
630 | parseError( "Unexpected " + ch + " encountered (expecting '/' or '*' )" );
631 | }
632 | }
633 |
634 | }
635 |
636 |
637 | /**
638 | * Skip any whitespace in the input string and advances
639 | * the character to the first character after any possible
640 | * whitespace.
641 | */
642 | private final function skipWhite():void
643 | {
644 | // As long as there are spaces in the input
645 | // stream, advance the current location pointer
646 | // past them
647 | while ( isWhiteSpace( ch ) )
648 | {
649 | nextChar();
650 | }
651 |
652 | }
653 |
654 | /**
655 | * Determines if a character is whitespace or not.
656 | *
657 | * @return True if the character passed in is a whitespace
658 | * character
659 | */
660 | private final function isWhiteSpace( ch:String ):Boolean
661 | {
662 | // Check for the whitespace defined in the spec
663 | if ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' )
664 | {
665 | return true;
666 | }
667 | // If we're not in strict mode, we also accept non-breaking space
668 | else if ( !strict && ch.charCodeAt( 0 ) == 160 )
669 | {
670 | return true;
671 | }
672 |
673 | return false;
674 | }
675 |
676 | /**
677 | * Determines if a character is a digit [0-9].
678 | *
679 | * @return True if the character passed in is a digit
680 | */
681 | private final function isDigit( ch:String ):Boolean
682 | {
683 | return ( ch >= '0' && ch <= '9' );
684 | }
685 |
686 | /**
687 | * Determines if a character is a hex digit [0-9A-Fa-f].
688 | *
689 | * @return True if the character passed in is a hex digit
690 | */
691 | private final function isHexDigit( ch:String ):Boolean
692 | {
693 | return ( isDigit( ch ) || ( ch >= 'A' && ch <= 'F' ) || ( ch >= 'a' && ch <= 'f' ) );
694 | }
695 |
696 | /**
697 | * Raises a parsing error with a specified message, tacking
698 | * on the error location and the original string.
699 | *
700 | * @param message The message indicating why the error occurred
701 | */
702 | public final function parseError( message:String ):void
703 | {
704 | throw new JSONParseError( message, loc, jsonString );
705 | }
706 | }
707 |
708 | }
709 |
--------------------------------------------------------------------------------