/*
|
* Copyright (c) 1995-2005 Macromedia, Inc. All rights reserved.
|
*/
|
|
///////////////////////////////////////////////////////////////////////////
|
//
|
// Filename: wddx.js
|
//
|
// Authors: Simeon Simeonov (simeons@allaire.com)
|
// Nate Weiss (nweiss@icesinc.com)
|
//
|
// Last Modified: February 2, 2001
|
//
|
///////////////////////////////////////////////////////////////////////////
|
|
|
///////////////////////////////////////////////////////////////////////////
|
//
|
// WddxSerializer
|
//
|
///////////////////////////////////////////////////////////////////////////
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeValue() serializes any value that can be serialized
|
// returns true/false
|
function wddxSerializer_serializeValue(obj)
|
{
|
var bSuccess = true;
|
var val;
|
|
if (obj == null)
|
{
|
// Null value
|
this.write("<null/>");
|
}
|
else if (typeof(val = obj.valueOf()) == "string")
|
{
|
// String value
|
this.serializeString(val);
|
}
|
else if (typeof(val = obj.valueOf()) == "number")
|
{
|
// Distinguish between numbers and date-time values
|
|
if (
|
typeof(obj.getTimezoneOffset) == "function" &&
|
typeof(obj.toGMTString) == "function")
|
{
|
// Possible Date
|
// Note: getYear() fix is from David Flanagan's
|
// "JS: The Definitive Guide". This code is Y2K safe.
|
this.write("<dateTime>" +
|
(obj.getYear() < 1000 ? 1900+obj.getYear() : obj.getYear()) + "-" + (obj.getMonth() + 1) + "-" + obj.getDate() +
|
"T" + obj.getHours() + ":" + obj.getMinutes() + ":" + obj.getSeconds());
|
if (this.useTimezoneInfo)
|
{
|
this.write(this.timezoneString);
|
}
|
this.write("</dateTime>");
|
}
|
else
|
{
|
// Number value
|
this.write("<number>" + val + "</number>");
|
}
|
}
|
else if (typeof(val = obj.valueOf()) == "boolean")
|
{
|
// Boolean value
|
this.write("<boolean value='" + val + "'/>");
|
}
|
else if (typeof(obj) == "object")
|
{
|
if (typeof(obj.wddxSerialize) == "function")
|
{
|
// Object knows how to serialize itself
|
bSuccess = obj.wddxSerialize(this);
|
}
|
else if (
|
typeof(obj.join) == "function" &&
|
typeof(obj.reverse) == "function" &&
|
typeof(obj.sort) == "function" &&
|
typeof(obj.length) == "number")
|
{
|
// Possible Array
|
this.write("<array length='" + obj.length + "'>");
|
for (var i = 0; bSuccess && i < obj.length; ++i)
|
{
|
bSuccess = this.serializeValue(obj[i]);
|
}
|
this.write("</array>");
|
}
|
else
|
{
|
// Some generic object; treat it as a structure
|
|
// Use the wddxSerializationType property as a guide as to its type
|
if (typeof(obj.wddxSerializationType) == 'string')
|
{
|
this.write('<struct type="'+ obj.wddxSerializationType +'">')
|
}
|
else
|
{
|
this.write("<struct>");
|
}
|
|
for (var prop in obj)
|
{
|
if (prop != 'wddxSerializationType')
|
{
|
bSuccess = this.serializeVariable(prop, obj[prop]);
|
if (! bSuccess)
|
{
|
break;
|
}
|
}
|
}
|
|
this.write("</struct>");
|
}
|
}
|
else
|
{
|
// Error: undefined values or functions
|
bSuccess = false;
|
}
|
|
// Successful serialization
|
return bSuccess;
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeAttr() serializes an attribute (such as a var tag) using JavaScript
|
// functionality available in NS 3.0 and above
|
function wddxSerializer_serializeAttr(s)
|
{
|
for (var i = 0; i < s.length; ++i)
|
{
|
this.write(this.at[s.charAt(i)]);
|
}
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeAttrOld() serializes a string using JavaScript functionality
|
// available in IE 3.0. We don't support special characters for IE3, so
|
// just throw the unencoded text and hope for the best
|
function wddxSerializer_serializeAttrOld(s)
|
{
|
this.write(s);
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeString() serializes a string using JavaScript functionality
|
// available in NS 3.0 and above
|
function wddxSerializer_serializeString(s)
|
{
|
this.write("<string>");
|
for (var i = 0; i < s.length; ++i)
|
{
|
if (s.charCodeAt(i) > 255)
|
this.write(s.charAt(i));
|
else
|
this.write(this.et[s.charAt(i)]);
|
}
|
this.write("</string>");
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeStringOld() serializes a string using JavaScript functionality
|
// available in IE 3.0
|
function wddxSerializer_serializeStringOld(s)
|
{
|
this.write("<string><![CDATA[");
|
|
pos = s.indexOf("]]>");
|
if (pos != -1)
|
{
|
startPos = 0;
|
while (pos != -1)
|
{
|
this.write(s.substring(startPos, pos) + "]]>]]><![CDATA[");
|
|
startPos = pos + 3;
|
if (startPos < s.length)
|
{
|
pos = s.indexOf("]]>", startPos);
|
}
|
else
|
{
|
// Work around bug in indexOf()
|
// "" will be returned instead of -1 if startPos > length
|
pos = -1;
|
}
|
}
|
this.write(s.substring(startPos, s.length));
|
}
|
else
|
{
|
this.write(s);
|
}
|
|
this.write("]]></string>");
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serializeVariable() serializes a property of a structure
|
// returns true/false
|
function wddxSerializer_serializeVariable(name, obj)
|
{
|
var bSuccess = true;
|
|
if (typeof(obj) != "function")
|
{
|
this.write("<var name='");
|
this.preserveVarCase ? this.serializeAttr(name) : this.serializeAttr(name.toLowerCase());
|
this.write("'>");
|
|
bSuccess = this.serializeValue(obj);
|
this.write("</var>");
|
}
|
|
return bSuccess;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// write() appends text to the wddxPacket buffer
|
function wddxSerializer_write(str)
|
{
|
this.wddxPacket[this.wddxPacket.length] = str;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// writeOld() appends text to the wddxPacket buffer using IE 3.0 (JS 1.0)
|
// functionality. Unfortunately, the += operator has quadratic complexity
|
// which will cause slowdowns for large packets.
|
function wddxSerializer_writeOld(str)
|
{
|
this.wddxPacket += str;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// initPacket() initializes the WDDX packet
|
function wddxSerializer_initPacket()
|
{
|
this.wddxPacket = new Array();
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// initPacketOld() initializes the WDDX packet for use with IE 3.0 (JS 1.0)
|
function wddxSerializer_initPacketOld()
|
{
|
this.wddxPacket = "";
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// extractPacket() extracts the WDDX packet as a string
|
function wddxSerializer_extractPacket()
|
{
|
return this.wddxPacket.join("");
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// extractPacketOld() extracts the WDDX packet as a string (IE 3.0/JS 1.0)
|
function wddxSerializer_extractPacketOld()
|
{
|
return this.wddxPacket;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// serialize() creates a WDDX packet for a given object
|
// returns the packet on success or null on failure
|
function wddxSerializer_serialize(rootObj)
|
{
|
this.initPacket();
|
|
this.write("<wddxPacket version='1.0'><header/><data>");
|
var bSuccess = this.serializeValue(rootObj);
|
this.write("</data></wddxPacket>");
|
|
if (bSuccess)
|
{
|
return this.extractPacket();
|
}
|
else
|
{
|
return null;
|
}
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// WddxSerializer() binds the function properties of the object
|
function WddxSerializer()
|
{
|
// Compatibility section
|
if (navigator.appVersion != "" && navigator.appVersion.indexOf("MSIE 3.") == -1)
|
{
|
// Character encoding table
|
|
// Encoding table for strings (CDATA)
|
var et = new Array();
|
|
// Numbers to characters table and
|
// characters to numbers table
|
var n2c = new Array();
|
var c2n = new Array();
|
|
// Encoding table for attributes (i.e. var=str)
|
var at = new Array();
|
|
for (var i = 0; i < 256; ++i)
|
{
|
// Build a character from octal code
|
var d1 = Math.floor(i/64);
|
var d2 = Math.floor((i%64)/8);
|
var d3 = i%8;
|
var c = eval("\"\\" + d1.toString(10) + d2.toString(10) + d3.toString(10) + "\"");
|
|
// Modify character-code conversion tables
|
n2c[i] = c;
|
c2n[c] = i;
|
|
// Modify encoding table
|
if (i < 32 && i != 9 && i != 10 && i != 13)
|
{
|
// Control characters that are not tabs, newlines, and carriage returns
|
|
// Create a two-character hex code representation
|
var hex = i.toString(16);
|
if (hex.length == 1)
|
{
|
hex = "0" + hex;
|
}
|
|
et[n2c[i]] = "<char code='" + hex + "'/>";
|
|
// strip control chars from inside attrs
|
at[n2c[i]] = "";
|
|
}
|
else if (i < 128)
|
{
|
// Low characters that are not special control characters
|
et[n2c[i]] = n2c[i];
|
|
// attr table
|
at[n2c[i]] = n2c[i];
|
}
|
else
|
{
|
// High characters
|
et[n2c[i]] = "&#x" + i.toString(16) + ";";
|
at[n2c[i]] = "&#x" + i.toString(16) + ";";
|
}
|
}
|
|
// Special escapes for CDATA encoding
|
et["<"] = "<";
|
et[">"] = ">";
|
et["&"] = "&";
|
|
// Special escapes for attr encoding
|
at["<"] = "<";
|
at[">"] = ">";
|
at["&"] = "&";
|
at["'"] = "'";
|
at["\""] = """;
|
|
// Store tables
|
this.n2c = n2c;
|
this.c2n = c2n;
|
this.et = et;
|
this.at = at;
|
|
// The browser is not MSIE 3.x
|
this.serializeString = wddxSerializer_serializeString;
|
this.serializeAttr = wddxSerializer_serializeAttr;
|
this.write = wddxSerializer_write;
|
this.initPacket = wddxSerializer_initPacket;
|
this.extractPacket = wddxSerializer_extractPacket;
|
}
|
else
|
{
|
// The browser is most likely MSIE 3.x, it is NS 2.0 compatible
|
this.serializeString = wddxSerializer_serializeStringOld;
|
this.serializeAttr = wddxSerializer_serializeAttrOld;
|
this.write = wddxSerializer_writeOld;
|
this.initPacket = wddxSerializer_initPacketOld;
|
this.extractPacket = wddxSerializer_extractPacketOld;
|
}
|
|
// Setup timezone information
|
|
var tzOffset = (new Date()).getTimezoneOffset();
|
|
// Invert timezone offset to convert local time to UTC time
|
if (tzOffset >= 0)
|
{
|
this.timezoneString = '-';
|
}
|
else
|
{
|
this.timezoneString = '+';
|
}
|
this.timezoneString += Math.floor(Math.abs(tzOffset) / 60) + ":" + (Math.abs(tzOffset) % 60);
|
|
// Common properties
|
this.preserveVarCase = false;
|
this.useTimezoneInfo = true;
|
|
// Common functions
|
this.serialize = wddxSerializer_serialize;
|
this.serializeValue = wddxSerializer_serializeValue;
|
this.serializeVariable = wddxSerializer_serializeVariable;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
//
|
// WddxRecordset
|
//
|
///////////////////////////////////////////////////////////////////////////
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// isColumn(name) returns true/false based on whether this is a column name
|
function wddxRecordset_isColumn(name)
|
{
|
// Columns must be objects
|
// WddxRecordset extensions might use properties prefixed with
|
// _private_ and these will not be treated as columns
|
return (typeof(this[name]) == "object" &&
|
name.indexOf("_private_") == -1);
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// getRowCount() returns the number of rows in the recordset
|
function wddxRecordset_getRowCount()
|
{
|
var nRowCount = 0;
|
for (var col in this)
|
{
|
if (this.isColumn(col))
|
{
|
nRowCount = this[col].length;
|
break;
|
}
|
}
|
return nRowCount;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// addColumn(name) adds a column with that name and length == getRowCount()
|
function wddxRecordset_addColumn(name)
|
{
|
var nLen = this.getRowCount();
|
var colValue = new Array(nLen);
|
for (var i = 0; i < nLen; ++i)
|
{
|
colValue[i] = null;
|
}
|
this[this.preserveFieldCase ? name : name.toLowerCase()] = colValue;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// addRows() adds n rows to all columns of the recordset
|
function wddxRecordset_addRows(n)
|
{
|
for (var col in this)
|
{
|
if (this.isColumn(col))
|
{
|
var nLen = this[col].length;
|
for (var i = nLen; i < nLen + n; ++i)
|
{
|
this[col][i] = null;
|
}
|
}
|
}
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// getField() returns the element in a given (row, col) position
|
function wddxRecordset_getField(row, col)
|
{
|
return this[this.preserveFieldCase ? col : col.toLowerCase()][row];
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// setField() sets the element in a given (row, col) position to value
|
function wddxRecordset_setField(row, col, value)
|
{
|
this[this.preserveFieldCase ? col : col.toLowerCase()][row] = value;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// wddxSerialize() serializes a recordset
|
// returns true/false
|
function wddxRecordset_wddxSerialize(serializer)
|
{
|
// Create an array and a list of column names
|
var colNamesList = "";
|
var colNames = new Array();
|
var i = 0;
|
for (var col in this)
|
{
|
if (this.isColumn(col))
|
{
|
colNames[i++] = col;
|
|
if (colNamesList.length > 0)
|
{
|
colNamesList += ",";
|
}
|
colNamesList += col;
|
}
|
}
|
|
var nRows = this.getRowCount();
|
|
serializer.write("<recordset rowCount='" + nRows + "' fieldNames='" + colNamesList + "'>");
|
|
var bSuccess = true;
|
for (i = 0; bSuccess && i < colNames.length; i++)
|
{
|
var name = colNames[i];
|
serializer.write("<field name='" + name + "'>");
|
|
for (var row = 0; bSuccess && row < nRows; row++)
|
{
|
bSuccess = serializer.serializeValue(this[name][row]);
|
}
|
|
serializer.write("</field>");
|
}
|
|
serializer.write("</recordset>");
|
|
return bSuccess;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// dump(escapeStrings) returns an HTML table with the recordset data
|
// It is a convenient routine for debugging and testing recordsets
|
// The boolean parameter escapeStrings determines whether the <>&
|
// characters in string values are escaped as <>&
|
function wddxRecordset_dump(escapeStrings)
|
{
|
// Get row count
|
var nRows = this.getRowCount();
|
|
// Determine column names
|
var colNames = new Array();
|
var i = 0;
|
for (var col in this)
|
{
|
if (typeof(this[col]) == "object")
|
{
|
colNames[i++] = col;
|
}
|
}
|
|
// Build table headers
|
var o = "<table border=1><tr><td><b>RowNumber</b></td>";
|
for (i = 0; i < colNames.length; ++i)
|
{
|
o += "<td><b>" + colNames[i] + "</b></td>";
|
}
|
o += "</tr>";
|
|
// Build data cells
|
for (var row = 0; row < nRows; ++row)
|
{
|
o += "<tr><td>" + row + "</td>";
|
for (i = 0; i < colNames.length; ++i)
|
{
|
var elem = this.getField(row, colNames[i]);
|
if (escapeStrings && typeof(elem) == "string")
|
{
|
var str = "";
|
for (var j = 0; j < elem.length; ++j)
|
{
|
var ch = elem.charAt(j);
|
if (ch == '<')
|
{
|
str += "<";
|
}
|
else if (ch == '>')
|
{
|
str += ">";
|
}
|
else if (ch == '&')
|
{
|
str += "&";
|
}
|
else
|
{
|
str += ch;
|
}
|
}
|
o += ("<td>" + str + "</td>");
|
}
|
else
|
{
|
o += ("<td>" + elem + "</td>");
|
}
|
}
|
o += "</tr>";
|
}
|
|
// Close table
|
o += "</table>";
|
|
// Return HTML recordset dump
|
return o;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// WddxRecordset([flagPreserveFieldCase]) creates an empty recordset.
|
// WddxRecordset(columns [, flagPreserveFieldCase]) creates a recordset
|
// with a given set of columns provided as an array of strings.
|
// WddxRecordset(columns, rows [, flagPreserveFieldCase]) creates a
|
// recordset with these columns and some number of rows.
|
// In all cases, flagPreserveFieldCase determines whether the exact case
|
// of field names is preserved. If omitted, the default value is false
|
// which means that all field names will be lowercased.
|
function WddxRecordset()
|
{
|
// Add default properties
|
this.preserveFieldCase = false;
|
|
// Add extensions
|
if (typeof(wddxRecordsetExtensions) == "object")
|
{
|
for (var prop in wddxRecordsetExtensions)
|
{
|
// Hook-up method to WddxRecordset object
|
this[prop] = wddxRecordsetExtensions[prop]
|
}
|
}
|
|
// Add built-in methods
|
this.getRowCount = wddxRecordset_getRowCount;
|
this.addColumn = wddxRecordset_addColumn;
|
this.addRows = wddxRecordset_addRows;
|
this.isColumn = wddxRecordset_isColumn;
|
this.getField = wddxRecordset_getField;
|
this.setField = wddxRecordset_setField;
|
this.wddxSerialize = wddxRecordset_wddxSerialize;
|
this.dump = wddxRecordset_dump;
|
|
// Perfom any needed initialization
|
if (WddxRecordset.arguments.length > 0)
|
{
|
if (typeof(val = WddxRecordset.arguments[0].valueOf()) == "boolean")
|
{
|
// Case preservation flag is provided as 1st argument
|
this.preserveFieldCase = WddxRecordset.arguments[0];
|
}
|
else
|
{
|
// First argument is the array of column names
|
var cols = WddxRecordset.arguments[0];
|
|
// Second argument could be the length or the preserve case flag
|
var nLen = 0;
|
if (WddxRecordset.arguments.length > 1)
|
{
|
if (typeof(val = WddxRecordset.arguments[1].valueOf()) == "boolean")
|
{
|
// Case preservation flag is provided as 2nd argument
|
this.preserveFieldCase = WddxRecordset.arguments[1];
|
}
|
else
|
{
|
// Explicitly specified recordset length
|
nLen = WddxRecordset.arguments[1];
|
|
if (WddxRecordset.arguments.length > 2)
|
{
|
// Case preservation flag is provided as 3rd argument
|
this.preserveFieldCase = WddxRecordset.arguments[2];
|
}
|
}
|
}
|
|
for (var i = 0; i < cols.length; ++i)
|
{
|
var colValue = new Array(nLen);
|
for (var j = 0; j < nLen; ++j)
|
{
|
colValue[j] = null;
|
}
|
|
this[this.preserveFieldCase ? cols[i] : cols[i].toLowerCase()] = colValue;
|
}
|
}
|
}
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
//
|
// WddxRecordset extensions
|
//
|
// The WddxRecordset class has been designed with extensibility in mind.
|
//
|
// Developers can add new properties to the object and as long as their
|
// names are prefixed with _private_ the WDDX serialization function of
|
// WddxRecordset will not treat them as recordset columns.
|
//
|
// Developers can create new methods for the class outside this file as
|
// long as they make a call to registerWddxRecordsetExtension() with the
|
// name of the method and the function object that implements the method.
|
// The WddxRecordset constructor will automatically register all these
|
// methods with instances of the class.
|
//
|
// Example:
|
//
|
// If I want to add a new WddxRecordset method called addOneRow() I can
|
// do the following:
|
//
|
// - create the method implementation
|
//
|
// function wddxRecordset_addOneRow()
|
// {
|
// this.addRows(1);
|
// }
|
//
|
// - call registerWddxRecordsetExtension()
|
//
|
// registerWddxRecordsetExtension("addOneRow", wddxRecordset_addOneRow);
|
//
|
// - use the new function
|
//
|
// rs = new WddxRecordset();
|
// rs.addOneRow();
|
//
|
///////////////////////////////////////////////////////////////////////////
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// registerWddxRecordsetExtension(name, func) can be used to extend
|
// functionality by registering functions that should be added as methods
|
// to WddxRecordset instances.
|
function registerWddxRecordsetExtension(name, func)
|
{
|
// Perform simple validation of arguments
|
if (typeof(name) == "string" && typeof(func) == "function")
|
{
|
// Guarantee existence of wddxRecordsetExtensions object
|
if (typeof(wddxRecordsetExtensions) != "object")
|
{
|
// Create wddxRecordsetExtensions instance
|
wddxRecordsetExtensions = new Object();
|
}
|
|
// Register extension; override an existing one
|
wddxRecordsetExtensions[name] = func;
|
}
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
//
|
// WddxBinary
|
//
|
///////////////////////////////////////////////////////////////////////////
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// wddxSerialize() serializes a binary value
|
// returns true/false
|
function wddxBinary_wddxSerialize(serializer)
|
{
|
serializer.write(
|
"<binary encoding='" + this.encoding + "'>" + this.data + "</binary>");
|
return true;
|
}
|
|
|
///////////////////////////////////////////////////////////////////////////
|
// WddxBinary() constructs an empty binary value
|
// WddxBinary(base64Data) constructs a binary value from base64 encoded data
|
// WddxBinary(data, encoding) constructs a binary value from encoded data
|
function WddxBinary(data, encoding)
|
{
|
this.data = data != null ? data : "";
|
this.encoding = encoding != null ? encoding : "base64";
|
|
// Custom serialization mechanism
|
this.wddxSerialize = wddxBinary_wddxSerialize;
|
}
|