一个纯JavaScript AMF 协议接口应用

json2.js

 

/*
    http://www.JSON.org/json2.js
    2010-03-20

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html


    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.


    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or ' '),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, strict: false */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (!this.JSON) {
    this.JSON = {};
}

(function () {

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());

 

//----------------------------------------------

 

amf.js

 

 

/*
amf.js - An AMF library in JavaScript

Copyright (c) 2010, James Ward - http://www.jamesward.com/
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY JAMES WARD ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES WARD OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Ward.
*/
function decodeAMF(data)
{
  var bytes = new a3d.ByteArray(data, a3d.Endian.BIG);

  var version = bytes.readUnsignedShort();
  bytes.objectEncoding = a3d.ObjectEncoding.AMF0;

  var response = new a3d.AMFPacket();

  var remainingBytes;

  // Headers
  var headerCount = bytes.readUnsignedShort();
  for (var h = 0; h < headerCount; h++)
  {
    var headerName = bytes.readUTF();
    var mustUnderstand = bytes.readBoolean();
    bytes.readInt(); // Consume header length...

    // Handle AVM+ type marker
    if (version == a3d.ObjectEncoding.AMF3)
    {
      var typeMarker = bytes.readByte();
      if (typeMarker == a3d.Amf0Types.kAvmPlusObjectType)
        bytes.objectEncoding = a3d.ObjectEncoding.AMF3;
      else
        bytes.pos = bytes.pos - 1;
    }

    var headerValue = bytes.readObject();

   /*
     // Read off the remaining bytes to account for the reset of
     // the by-reference index on each header value
     remainingBytes = new a3d.ByteArray();
     remainingBytes.objectEncoding = bytes.objectEncoding;
     bytes.readBytes(remainingBytes, 0, bytes.length - bytes.pos);
     bytes = remainingBytes;
     remainingBytes = null;
     */
   
    var header = new a3d.AMFHeader(headerName, mustUnderstand, headerValue);
    response.headers.push(header);

    // Reset to AMF0 for next header
    bytes.objectEncoding = a3d.ObjectEncoding.AMF0;
  }

  // Message Bodies
  var messageCount = bytes.readUnsignedShort();
  for (var m = 0; m < messageCount; m++)
  {
    var targetURI = bytes.readUTF();
    var responseURI = bytes.readUTF();
    bytes.readInt(); // Consume message body length...

    // Handle AVM+ type marker
    if (version == a3d.ObjectEncoding.AMF3)
    {
      var typeMarker = bytes.readByte();
      if (typeMarker == a3d.Amf0Types.kAvmPlusObjectType)
        bytes.objectEncoding = a3d.ObjectEncoding.AMF3;
      else
        bytes.pos = bytes.pos - 1;
    }

    var messageBody = bytes.readObject();

    var message = new a3d.AMFMessage(targetURI, responseURI, messageBody);
    response.messages.push(message);

    bytes.objectEncoding = a3d.ObjectEncoding.AMF0;
  }

  return response;
}

function dumpHex(bytes)
{
  var s = "";
  var i = 0;
  var chunk = [];

  while (i < bytes.length)
  {

    if (((i % 16) == 0) && (i != 0))
    {
      s += writeChunk(chunk, 16) + "\n";
      chunk = [];
    }

    chunk.push(bytes.readUnsignedByte());

    i++;
  }
  s += writeChunk(chunk, 16);

  bytes.pos = 0;

  return s;
}

function writeChunk(chunk, width)
{
  var s = "";

  for (var i = 0; i < chunk.length; i++)
  {
    if (((i % 4) == 0) && (i != 0))
    {
      s += " ";
    }

    var b = chunk[i];

    var ss = b.toString(16) + " ";
    if (ss.length == 2)
    {
      ss = "0" + ss;
    }

    s += ss;
  }

  for (var i = 0; i < (width - chunk.length); i++)
  {
    s += "   ";
  }

  var j = Math.floor((width - chunk.length) / 4);
  for (var i = 0; i < j; i++)
  {
    s += " ";
  }

  s += "   ";

  for (var i = 0; i < chunk.length; i++)
  {
    var b = chunk[i];

    if ((b <= 126) && (b > 32))
    {
      var ss = String.fromCharCode(b);
      s += ss;
    }
    else
    {
      s += ".";
    }
  }

  return s;
}

 

/**
 * @preserve
 * Adamia 3D Engine v0.1
 * Copyright (c) 2010 Adam R. Smith
 * Licensed under the new BSD License:
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Project home: http://code.google.com/p/adamia-3d/
 *
 * Date: 01/12/2010
 */

if (typeof(a3d) == 'undefined')
{
  /** @namespace */
  a3d = {};
}

// Taken from http://ejohn.org/blog/simple-javascript-inheritance/

// Inspired by base2 and Prototype
(function()
{
  var initializing = false, fnTest = /xyz/.test(function()
  {
    xyz;
  }) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  /** @class */
  this.Class = function()
  {
  };

  // Create a new Class that inherits from this class
  Class.extend = function(prop)
  {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop)
    {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
              typeof _super[name] == "function" && fnTest.test(prop[name]) ?
              (function(name, fn)
              {
                return function()
                {
                  var tmp = this._super;

                  // Add a new ._super() method that is the same method
                  // but on the super-class
                  this._super = _super[name];

                  // The method only need to be bound temporarily, so we
                  // remove it when we're done executing
                  var ret = fn.apply(this, arguments);
                  this._super = tmp;

                  return ret;
                };
              })(name, prop[name]) :
              prop[name];
    }

    // The dummy class constructor
    function Class()
    {
      // All construction is actually done in the init method
      if (!initializing && this.init)
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();
/**
 * Enum for big or little endian.
 * @enum {number}
 */
a3d.Endian = {
  BIG: 0
  , LITTLE: 1
};

a3d.ObjectEncoding = {
  AMF0: 0
  , AMF3: 3
};

a3d.Amf0Types = {
  kNumberType:           0
  , kBooleanType:        1
  , kStringType:         2
  , kObjectType:         3
  , kMovieClipType:      4
  , kNullType:           5
  , kUndefinedType:      6
  , kReferenceType:      7
  , kECMAArrayType:      8
  , kObjectEndType:      9
  , kStrictArrayType:   10
  , kDateType:          11
  , kLongStringType:    12
  , kUnsupportedType:   13
  , kRecordsetType:     14
  , kXMLObjectType:     15
  , kTypedObjectType:   16
  , kAvmPlusObjectType: 17
};

a3d.Amf3Types = {
  kUndefinedType:    0
  , kNullType:       1
  , kFalseType:      2
  , kTrueType:       3
  , kIntegerType:    4
  , kDoubleType:     5
  , kStringType:     6
  , kXMLType:        7
  , kDateType:       8
  , kArrayType:      9
  , kObjectType:     10
  , kAvmPlusXmlType: 11
  , kByteArrayType:  12
};


a3d.AMFMessage = Class.extend({
  targetURL: ""
  , responseURI: ""
  , body: {}

  , init: function(targetURL, responseURI, body)
  {
    this.targetURL = targetURL;
    this.responseURI = responseURI;
    this.body = body;
  }
});

a3d.AMFPacket = Class.extend({
  version:  0
  , headers: []
  , messages: []

  , init: function(version)
  {
    this.version = (version !== undefined) ? version : 0;
    this.headers = new Array();
    this.messages = new Array();
  }
});

a3d.AMFHeader = Class.extend({
  name: ""
  , mustUnderstand: false
  , data: {}

  , init: function(name, mustUnderstand, data)
  {
    this.name = name;
    this.mustUnderstand = (mustUnderstand != undefined) ? mustUnderstand : false;
    this.data = data;
  }
});

/**
 * Attempt to imitate AS3's ByteArray as very high-performance javascript.
 * I aliased the functions to have shorter names, like ReadUInt32 as well as ReadUnsignedInt.
 * I used some code from http://fhtr.blogspot.com/2009/12/3d-models-and-parsing-binary-data-with.html
 * to kick-start it, but I added optimizations and support both big and little endian.
 * Note that you cannot change the endianness after construction.
 * @extends Class
 */
a3d.ByteArray = Class.extend({
  data: ''
  , length: 0
  , pos: 0
  , pow: Math.pow
  , endian: a3d.Endian.BIG
  , TWOeN23: Math.pow(2, -23)
  , TWOeN52: Math.pow(2, -52)
  , objectEncoding: a3d.ObjectEncoding.AMF0
  , stringTable: []
  , objectTable: []
  , traitTable: []

  /** @constructor */
  , init: function(data, endian)
  {
    this.data = (data !== undefined) ? data : '';
    if (endian !== undefined) this.endian = endian;
    this.length = data.length;

    this.stringTable = new Array();
    this.objectTable = new Array();
    this.traitTable = new Array();

    // Cache the function pointers based on endianness.
    // This avoids doing an if-statement in every function call.
    var funcExt = (endian == a3d.Endian.BIG) ? 'BE' : 'LE';
    var funcs = ['readInt32', 'readInt16', 'readUInt30','readUInt32', 'readUInt16', 'readFloat32', 'readFloat64'];
    for (var func in funcs)
    {
      this[funcs[func]] = this[funcs[func] + funcExt];
    }

    // Add redundant members that match actionscript for compatibility
    var funcMap = {readUnsignedByte: 'readByte', readUnsignedInt: 'readUInt32',
      readFloat: 'readFloat32', readDouble: 'readFloat64', readShort: 'readInt16', readUnsignedShort: 'readUInt16',
      readBoolean: 'readBool', readInt: 'readInt32'};
    for (var func in funcMap)
    {
      this[func] = this[funcMap[func]];
    }
  }

  , readByte: function()
  {
    var cc = this.data.charCodeAt(this.pos++);
    return (cc & 0xFF);
  }

  , writeByte: function(byte)
  {
    this.data += byte;
  }

  , readBool: function()
  {
    return (this.data.charCodeAt(this.pos++) & 0xFF) ? true : false;
  }

  , readUInt30BE: function()
  {
    var ch1 = readByte();
    var ch2 = readByte();
    var ch3 = readByte();
    var ch4 = readByte();

    if (ch1 >= 64)
      return undefined;

    return ch4 | (ch3 << 8) | (ch2 << 16) | (ch1 << 24);
  }

  , readUInt32BE: function()
  {
    var data = this.data, pos = (this.pos += 4) - 4;
    return  ((data.charCodeAt(pos) & 0xFF) << 24) |
            ((data.charCodeAt(++pos) & 0xFF) << 16) |
            ((data.charCodeAt(++pos) & 0xFF) << 8) |
            (data.charCodeAt(++pos) & 0xFF);
  }
  , readInt32BE: function()
  {
    var data = this.data, pos = (this.pos += 4) - 4;
    var x = ((data.charCodeAt(pos) & 0xFF) << 24) |
            ((data.charCodeAt(++pos) & 0xFF) << 16) |
            ((data.charCodeAt(++pos) & 0xFF) << 8) |
            (data.charCodeAt(++pos) & 0xFF);
    return (x >= 2147483648) ? x - 4294967296 : x;
  }

  , readUInt16BE: function()
  {
    var data = this.data, pos = (this.pos += 2) - 2;
    return  ((data.charCodeAt(pos) & 0xFF) << 8) |
            (data.charCodeAt(++pos) & 0xFF);
  }
  , readInt16BE: function()
  {
    var data = this.data, pos = (this.pos += 2) - 2;
    var x = ((data.charCodeAt(pos) & 0xFF) << 8) |
            (data.charCodeAt(++pos) & 0xFF);
    return (x >= 32768) ? x - 65536 : x;
  }

  , readFloat32BE: function()
  {
    var data = this.data, pos = (this.pos += 4) - 4;
    var b1 = data.charCodeAt(pos) & 0xFF,
            b2 = data.charCodeAt(++pos) & 0xFF,
            b3 = data.charCodeAt(++pos) & 0xFF,
            b4 = data.charCodeAt(++pos) & 0xFF;
    var sign = 1 - ((b1 >> 7) << 1);                   // sign = bit 0
    var exp = (((b1 << 1) & 0xFF) | (b2 >> 7)) - 127;  // exponent = bits 1..8
    var sig = ((b2 & 0x7F) << 16) | (b3 << 8) | b4;    // significand = bits 9..31
    if (sig == 0 && exp == -127)
      return 0.0;
    return sign * (1 + this.TWOeN23 * sig) * this.pow(2, exp);
  }

  , readFloat64BE: function()
  {
    var b1 = this.readByte();
    var b2 = this.readByte();
    var b3 = this.readByte();
    var b4 = this.readByte();
    var b5 = this.readByte();
    var b6 = this.readByte();
    var b7 = this.readByte();
    var b8 = this.readByte();

    var sign = 1 - ((b1 >> 7) << 1);         // sign = bit 0
    var exp = (((b1 << 4) & 0x7FF) | (b2 >> 4)) - 1023;     // exponent = bits 1..11

    // This crazy toString() stuff works around the fact that js ints are
    // only 32 bits and signed, giving us 31 bits to work with
    var sig = (((b2 & 0xF) << 16) | (b3 << 8) | b4).toString(2) +
            ((b5 >> 7) ? '1' : '0') +
            (((b5 & 0x7F) << 24) | (b6 << 16) | (b7 << 8) | b8).toString(2); // significand = bits 12..63

    sig = parseInt(sig, 2);

    if (sig == 0 && exp == -1023)
   return 0.0;

  return sign*(1.0 + this.TWOeN52*sig)*this.pow(2, exp);
    /*
    var sig = (((b2 & 0xF) << 16) | (b3 << 8) | b4).toString(2) +
              (((b5 & 0xF) << 16) | (b6 << 8) | b7).toString(2) +
              (b8).toString(2);

    // should have 52 bits here
    console.log(sig.length);

    // this doesn't work   sig = parseInt(sig, 2);
   
    var newSig = 0;
    for (var i = 0; i < sig.length; i++)
    {
      var binaryPlace = this.pow(2, sig.length - i - 1);
      var binaryValue = parseInt(sig.charAt(i));
      newSig += binaryPlace * binaryValue;
    }


    if (newSig == 0 && exp == -1023)
      return 0.0;

    var mantissa = this.TWOeN52 * newSig;

    return sign * (1.0 + mantissa) * this.pow(2, exp);
    */
  }

  , readUInt29: function()
  {
    var value;

    // Each byte must be treated as unsigned
    var b = this.readByte() & 0xFF;

    if (b < 128)
      return b;

    value = (b & 0x7F) << 7;
    b = this.readByte() & 0xFF;

    if (b < 128)
      return (value | b);

    value = (value | (b & 0x7F)) << 7;
    b = this.readByte() & 0xFF;

    if (b < 128)
      return (value | b);

    value = (value | (b & 0x7F)) << 8;
    b = this.readByte() & 0xFF;

    return (value | b);
  }

  , readUInt30LE: function()
  {
    var ch1 = readByte();
    var ch2 = readByte();
    var ch3 = readByte();
    var ch4 = readByte();

    if (ch4 >= 64)
      return undefined;

    return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
  }

  , readUInt32LE: function()
  {
    var data = this.data, pos = (this.pos += 4);
    return  ((data.charCodeAt(--pos) & 0xFF) << 24) |
            ((data.charCodeAt(--pos) & 0xFF) << 16) |
            ((data.charCodeAt(--pos) & 0xFF) << 8) |
            (data.charCodeAt(--pos) & 0xFF);
  }
  , readInt32LE: function()
  {
    var data = this.data, pos = (this.pos += 4);
    var x = ((data.charCodeAt(--pos) & 0xFF) << 24) |
            ((data.charCodeAt(--pos) & 0xFF) << 16) |
            ((data.charCodeAt(--pos) & 0xFF) << 8) |
            (data.charCodeAt(--pos) & 0xFF);
    return (x >= 2147483648) ? x - 4294967296 : x;
  }

  , readUInt16LE: function()
  {
    var data = this.data, pos = (this.pos += 2);
    return  ((data.charCodeAt(--pos) & 0xFF) << 8) |
            (data.charCodeAt(--pos) & 0xFF);
  }
  , readInt16LE: function()
  {
    var data = this.data, pos = (this.pos += 2);
    var x = ((data.charCodeAt(--pos) & 0xFF) << 8) |
            (data.charCodeAt(--pos) & 0xFF);
    return (x >= 32768) ? x - 65536 : x;
  }

  , readFloat32LE: function()
  {
    var data = this.data, pos = (this.pos += 4);
    var b1 = data.charCodeAt(--pos) & 0xFF,
            b2 = data.charCodeAt(--pos) & 0xFF,
            b3 = data.charCodeAt(--pos) & 0xFF,
            b4 = data.charCodeAt(--pos) & 0xFF;
    var sign = 1 - ((b1 >> 7) << 1);                   // sign = bit 0
    var exp = (((b1 << 1) & 0xFF) | (b2 >> 7)) - 127;  // exponent = bits 1..8
    var sig = ((b2 & 0x7F) << 16) | (b3 << 8) | b4;    // significand = bits 9..31
    if (sig == 0 && exp == -127)
      return 0.0;
    return sign * (1 + this.TWOeN23 * sig) * this.pow(2, exp);
  }

  , readFloat64LE: function()
  {
    var data = this.data, pos = (this.pos += 8);
    var b1 = data.charCodeAt(--pos) & 0xFF,
            b2 = data.charCodeAt(--pos) & 0xFF,
            b3 = data.charCodeAt(--pos) & 0xFF,
            b4 = data.charCodeAt(--pos) & 0xFF,
            b5 = data.charCodeAt(--pos) & 0xFF,
            b6 = data.charCodeAt(--pos) & 0xFF,
            b7 = data.charCodeAt(--pos) & 0xFF,
            b8 = data.charCodeAt(--pos) & 0xFF;
    var sign = 1 - ((b1 >> 7) << 1);         // sign = bit 0
    var exp = (((b1 << 4) & 0x7FF) | (b2 >> 4)) - 1023;     // exponent = bits 1..11

    // This crazy toString() stuff works around the fact that js ints are
    // only 32 bits and signed, giving us 31 bits to work with
    var sig = (((b2 & 0xF) << 16) | (b3 << 8) | b4).toString(2) +
            ((b5 >> 7) ? '1' : '0') +
            (((b5 & 0x7F) << 24) | (b6 << 16) | (b7 << 8) | b8).toString(2); // significand = bits 12..63

    sig = parseInt(sig, 2);
    if (sig == 0 && exp == -1023)
      return 0.0;
    return sign * (1.0 + this.TWOeN52 * sig) * this.pow(2, exp);
  }

  , readDate: function()
  {
    var time_ms = this.readDouble();
    var tz_min = this.readUInt16();
    return new Date(time_ms + tz_min * 60 * 1000);
  }

  , readString: function(len)
  {
    var str = "";

    while (len > 0)
    {
      str += String.fromCharCode(this.readUnsignedByte());
      len--;
    }
    return str;
  }

  , readUTF: function()
  {
    return this.readString(this.readUnsignedShort());
  }

  , readLongUTF: function()
  {
    return this.readString(this.readUInt30());
  }

  , stringToXML: function(str)
  {
    var xmlDoc;

    if (window.DOMParser)
    {
      var parser = new DOMParser();
      xmlDoc = parser.parseFromString(str, "text/xml");
    }
    else // IE
    {
      xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
      xmlDoc.async = false;
      xmlDoc.loadXML(stc);
    }

    return xmlDoc;
  }

  , readXML: function()
  {
    var xml = this.readLongUTF();

    return this.stringToXML(xml);
  }

  , readStringAMF3: function()
  {
    var ref = this.readUInt29();

    if ((ref & 1) == 0) // This is a reference
      return this.stringTable[(ref >> 1)];

    var len = (ref >> 1);

    if (0 == len)
      return "";

    var str = this.readString(len);

    this.stringTable.push(str);

    return str;
  }

  , readTraits: function(ref)
  {
    var traitInfo = new Object();
    traitInfo.properties = new Array();

    if ((ref & 3) == 1)
      return this.traitTable[(ref >> 2)];

    traitInfo.externalizable = ((ref & 4) == 4);

    traitInfo.dynamic = ((ref & 8) == 8);

    traitInfo.count = (ref >> 4);
    traitInfo.className = this.readStringAMF3();

    this.traitTable.push(traitInfo);

    for (var i = 0; i < traitInfo.count; i++)
    {
      var propName = this.readStringAMF3();
      traitInfo.properties.push(propName);
    }

    return traitInfo;
  }

  , readExternalizable: function(className)
  {
    return this.readObject();
  }

  , readObject: function()
  {
    if (this.objectEncoding == a3d.ObjectEncoding.AMF0)
    {
      return this.readAMF0Object();
    }
    else if (this.objectEncoding == a3d.ObjectEncoding.AMF3)
    {
      return this.readAMF3Object();
    }
  }

  , readAMF0Object: function()
  {
    var marker = this.readByte();

    if (marker == a3d.Amf0Types.kNumberType)
    {
      return this.readDouble();
    }
    else if (marker == a3d.Amf0Types.kBooleanType)
    {
      return this.readBoolean();
    }
    else if (marker == a3d.Amf0Types.kStringType)
    {
      return this.readUTF();
    }
    else if ((marker == a3d.Amf0Types.kObjectType) || (marker == a3d.Amf0Types.kECMAArrayType))
    {
      var o = new Object();

      var ismixed = (marker == 0x08);

      var size = null;
      if (ismixed)
        this.readUInt30();

      while (true)
      {
        var c1 = this.readByte();
        var c2 = this.readByte();
        var name = this.readString((c1 << 8) | c2);
        var k = this.readByte();
        if (k == 0x09)
          break;

        this.pos--;

        o[name] = readObject();
      }

      return o;
    }
    else if (marker == a3d.Amf0Types.kStrictArrayType)
    {
      var size = this.readInt();

      var a = new Array();

      for (var i = 0; i < size; ++i)
      {
        a.push(this.readObject());
      }

      return a;
    }
    else if (marker == a3d.Amf0Types.kTypedObjectType)
    {
      var o = new Object();

      var typeName = this.readUTF();
     
      var propertyName = this.readUTF();
      var type = this.readByte();
      while (type != kObjectEndType)
      {
        var value = this.readObject();
        o[propertyName] = value;

        propertyName = this.readUTF();
        type = this.readByte();
      }

      return o;
    }
    else if (marker == a3d.Amf0Types.kAvmPlusObjectType)
    {
      return this.readAMF3Object();
    }
    else if (marker == a3d.Amf0Types.kNullType)
    {
      return null;
    }
    else if (marker == a3d.Amf0Types.kUndefinedType)
    {
      return undefined;
    }
    else if (marker == a3d.Amf0Types.kReferenceType)
    {
      var refNum = this.readUnsignedShort();

      var value = this.objectTable[refNum];

      return value;
    }
    else if (marker == a3d.Amf0Types.kDateType)
    {
      return this.readDate();
    }
    else if (marker == a3d.Amf0Types.kLongStringType)
    {
      return this.readLongUTF();
    }
    else if (marker == a3d.Amf0Types.kXMLObjectType)
    {
      return this.readXML();
    }
  }

  , readAMF3Object: function()
  {
    var marker = this.readByte();

    if (marker == a3d.Amf3Types.kUndefinedType)
    {
      return undefined;
    }
    else if (marker == a3d.Amf3Types.kNullType)
    {
      return null;
    }
    else if (marker == a3d.Amf3Types.kFalseType)
    {
      return false;
    }
    else if (marker == a3d.Amf3Types.kTrueType)
    {
      return true;
    }
    else if (marker == a3d.Amf3Types.kIntegerType)
    {
      var i = this.readUInt29();

      return i;
    }
    else if (marker == a3d.Amf3Types.kDoubleType)
    {
      return this.readDouble();
    }
    else if (marker == a3d.Amf3Types.kStringType)
    {
      return this.readStringAMF3();
    }
    else if (marker == a3d.Amf3Types.kXMLType)
    {
      return this.readXML();
    }
    else if (marker == a3d.Amf3Types.kDateType)
    {
      var ref = this.readUInt29();

      if ((ref & 1) == 0)
        return this.objectTable[(ref >> 1)];

      var d = this.readDouble();
      var value = new Date(d);
      this.objectTable.push(value);

      return value;
    }
    else if (marker == a3d.Amf3Types.kArrayType)
    {
      var ref = this.readUInt29();

      if ((ref & 1) == 0)
        return this.objectTable[(ref >> 1)];

      var len = (ref >> 1);

      var key = this.readStringAMF3();

      if (key == "")
      {
        var a = new Array();

        for (var i = 0; i < len; i++)
        {
          var value = this.readObject();

          a.push(value);
        }

        return a;
      }

      // mixed array
      var result = {};

      while (key != "")
      {
        result[key] = this.readObject();
        key = this.readStringAMF3();
      }

      for (var i = 0; i < len; i++)
      {
        result[i] = this.readObject();
      }

      return result;
    }
    else if (marker == a3d.Amf3Types.kObjectType)
    {
      var o = new Object();

      var ref = this.readUInt29();

      if ((ref & 1) == 0)
        return this.objectTable[(ref >> 1)];

      var ti = this.readTraits(ref);
      var className = ti.className;
      var externalizable = ti.externalizable;

      if (externalizable)
      {
        o = this.readExternalizable(className);
      }
      else
      {
        var len = ti.properties.length;

        for (var i = 0; i < len; i++)
        {
          var propName = ti.properties[i];

          var value = this.readObject();

          o[propName] = value;
        }

        if (ti.dynamic)
        {
          for (; ;)
          {
            var name = this.readString();
            if (name == null || name.length == 0) break;

            var value = this.readObject();
            o[propName] = value;
          }
        }
      }

      this.objectTable.push(o);

      return o;
    }
    else if (marker == a3d.Amf3Types.kAvmPlusXmlType)
    {
      var ref = this.readUInt29();

      if ((ref & 1) == 0)
        return this.stringToXML(this.objectTable[(ref >> 1)]);

      var len = (ref >> 1);

      if (0 == len)
        return null;


      var str = this.readString(len);

      var xml = this.stringToXML(str);

      this.objectTable.push(xml);

      return xml;
    }
    else if (marker == a3d.Amf3Types.kByteArrayType)
    {
      var ref = this.readUInt29();
      if ((ref & 1) == 0)
        return this.objectTable[(ref >> 1)];

      var len = (ref >> 1);

      var ba = new a3d.ByteArray();

      for (var i = 0; i < len; i++)
      {
        ba.writeByte(this.readByte());
      }

      this.objectTable.push(ba);

      return ba;
    }

  }
});

//-----------------------------------------

应用实例

 

 

<html>
<head>
  <script type="text/javascript" src="json2.js"></script>
  <script type="text/javascript" src="amf.js"></script>
  <script type="text/vbscript">
          Function BinaryToArray(Binary)
            Dim i
            ReDim byteArray(LenB(Binary))
            For i = 1 To LenB(Binary)
              byteArray(i-1) = AscB(MidB(Binary, i, 1))
            Next
            BinaryToArray = byteArray
          End Function
  </script>
  <script type="text/javascript">
    var url = "/census2-tests/servlet/CensusServiceServlet?command=getAMF3&rows=20&clientId=6F5A9CE7-802D-CC17-CA35-A4A5611BF796&testId=flex_amf3&sendCensusResultURL=http://www.jamesward.com/census2/SendCensusResult&gzip=true";

    var req;

    function getAMF()
    {
      document.getElementById("output").innerHTML = "<h1>Fetching AMF encoded data using JavaScript from:</h1>" + url + "<br/>";

      if (window.ActiveXObject)
      {
        req = new ActiveXObject("Microsoft.XMLHTTP");
      }
      else if (window.XMLHttpRequest)
      {
        req = new XMLHttpRequest();
        //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
        req.overrideMimeType('text/plain; charset=x-user-defined');
      }

      req.onreadystatechange = processReqChange;
      req.open("GET", url, true);
      req.send(null);
    }

    function processReqChange()
    {
      if (req.readyState == 4)
      {
        if (req.status == 200)
        {
          var res;
          if (req.responseBody != undefined)
          {
            res = "";
            var a = BinaryToArray(req.responseBody).toArray();
            for (var i = 0; i < a.length; i++)
            {
              res += String.fromCharCode(a[i]);
            }
          }
          else
          {
            res = req.responseText;
          }

          var o = decodeAMF(res).messages[0].body;
         
          var bytes = new a3d.ByteArray(res, a3d.Endian.BIG);

          var tHtml = "<h2>Decoded data in a table:</h2><table width='100%' cellpadding='4' cellspacing='0' border='1'>";
          for (var i = 0; i < o.length; i++)
          {
            tHtml += "<tr>";
            tHtml += "<td>" + o[i].id + "</td>";
            tHtml += "<td>" + o[i].age + "</td>";
            tHtml += "<td>" + o[i].education + "</td>";
            tHtml += "<td>" + o[i].classOfWorker + "</td>";
            tHtml += "<td>" + o[i].maritalStatus + "</td>";
            tHtml += "<td>" + o[i].race + "</td>";
            tHtml += "<td>" + o[i].sex + "</td>";
            tHtml += "</tr>";
          }
          tHtml += "</table>";

          document.getElementById("output").innerHTML += "<br/>" + tHtml;

          document.getElementById("output").innerHTML += "<br/><h2>hexdump of AMF data:</h2><pre>" + dumpHex(bytes) + "</pre>";

          document.getElementById("output").innerHTML += "<br/><h2>JSON Data representation:</h2>" + JSON.stringify(o);
        }
        else
        {
          alert("There was a problem retrieving the data:\n" + req.statusText);
        }
      }
    }
  </script>
</head>
<body οnlοad="getAMF()">

  <div id="output">

  </div>

</body>
</html>

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值