objj-runtime
Version:
JavaScript (ECMAScript) and Objective-J runtime
301 lines (240 loc) • 7.42 kB
JavaScript
/*
* CFData.js
* Objective-J
*
* Created by Francisco Tolmasky.
* Copyright 2008-2010, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
GLOBAL(CFData) = function()
{
this._rawString = NULL;
this._propertyList = NULL;
this._propertyListFormat = NULL;
this._JSONObject = NULL;
this._bytes = NULL;
this._base64 = NULL;
};
CFData.prototype.propertyList = function()
{
if (!this._propertyList)
this._propertyList = CFPropertyList.propertyListFromString(this.rawString());
return this._propertyList;
};
CFData.prototype.JSONObject = function()
{
if (!this._JSONObject)
{
try
{
this._JSONObject = JSON.parse(this.rawString());
}
catch (anException)
{
}
}
return this._JSONObject;
};
CFData.prototype.rawString = function()
{
if (this._rawString === NULL)
{
if (this._propertyList)
this._rawString = CFPropertyList.stringFromPropertyList(this._propertyList, this._propertyListFormat);
else if (this._JSONObject)
this._rawString = JSON.stringify(this._JSONObject);
else if (this._bytes)
this._rawString = CFData.bytesToString(this._bytes);
else if (this._base64)
this._rawString = CFData.decodeBase64ToString(this._base64, true);
else
throw new Error("Can't convert data to string.");
}
return this._rawString;
};
CFData.prototype.bytes = function()
{
if (this._bytes === NULL)
{
var bytes = CFData.stringToBytes(this.rawString());
this.setBytes(bytes);
}
return this._bytes;
};
CFData.prototype.base64 = function()
{
if (this._base64 === NULL)
{
var base64;
if (this._bytes)
base64 = CFData.encodeBase64Array(this._bytes);
else
base64 = CFData.encodeBase64String(this.rawString());
this.setBase64String(base64);
}
return this._base64;
};
GLOBAL(CFMutableData) = function()
{
CFData.call(this);
};
CFMutableData.prototype = new CFData();
function clearMutableData(/*CFMutableData*/ aData)
{
this._rawString = NULL;
this._propertyList = NULL;
this._propertyListFormat = NULL;
this._JSONObject = NULL;
this._bytes = NULL;
this._base64 = NULL;
}
CFMutableData.prototype.setPropertyList = function(/*PropertyList*/ aPropertyList, /*Format*/ aFormat)
{
clearMutableData(this);
this._propertyList = aPropertyList;
this._propertyListFormat = aFormat;
};
CFMutableData.prototype.setJSONObject = function(/*Object*/ anObject)
{
clearMutableData(this);
this._JSONObject = anObject;
};
CFMutableData.prototype.setRawString = function(/*String*/ aString)
{
clearMutableData(this);
this._rawString = aString;
};
CFMutableData.prototype.setBytes = function(/*Array*/ bytes)
{
clearMutableData(this);
this._bytes = bytes;
};
CFMutableData.prototype.setBase64String = function(/*String*/ aBase64String)
{
clearMutableData(this);
this._base64 = aBase64String;
};
// Base64 encoding and decoding
var base64_map_to = [
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"0","1","2","3","4","5","6","7","8","9","+","/","="],
base64_map_from = [];
for (var i = 0; i < base64_map_to.length; i++)
base64_map_from[base64_map_to[i].charCodeAt(0)] = i;
CFData.decodeBase64ToArray = function(input, strip)
{
if (strip)
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
var pad = (input[input.length-1] == "=" ? 1 : 0) + (input[input.length-2] == "=" ? 1 : 0),
length = input.length,
output = [];
var i = 0;
while (i < length)
{
var bits = (base64_map_from[input.charCodeAt(i++)] << 18) |
(base64_map_from[input.charCodeAt(i++)] << 12) |
(base64_map_from[input.charCodeAt(i++)] << 6) |
(base64_map_from[input.charCodeAt(i++)]);
output.push((bits & 0xFF0000) >> 16);
output.push((bits & 0xFF00) >> 8);
output.push(bits & 0xFF);
}
// strip "=" padding from end
if (pad > 0)
return output.slice(0, -1 * pad);
return output;
};
CFData.encodeBase64Array = function(input)
{
var pad = (3 - (input.length % 3)) % 3,
length = input.length + pad,
output = [];
// pad with nulls
if (pad > 0) input.push(0);
if (pad > 1) input.push(0);
var i = 0;
while (i < length)
{
var bits = (input[i++] << 16) |
(input[i++] << 8) |
(input[i++]);
output.push(base64_map_to[(bits & 0xFC0000) >> 18]);
output.push(base64_map_to[(bits & 0x3F000) >> 12]);
output.push(base64_map_to[(bits & 0xFC0) >> 6]);
output.push(base64_map_to[bits & 0x3F]);
}
// pad with "=" and revert array to previous state
if (pad > 0)
{
output[output.length - 1] = "=";
input.pop();
}
if (pad > 1)
{
output[output.length - 2] = "=";
input.pop();
}
return output.join("");
};
CFData.decodeBase64ToString = function(input, strip)
{
return CFData.bytesToString(CFData.decodeBase64ToArray(input, strip));
};
CFData.decodeBase64ToUtf16String = function(input, strip)
{
return CFData.bytesToUtf16String(CFData.decodeBase64ToArray(input, strip));
};
CFData.bytesToString = function(bytes)
{
// This is relatively efficient, I think:
return String.fromCharCode.apply(NULL, bytes);
};
CFData.stringToBytes = function(input)
{
var temp = [];
for (var i = 0; i < input.length; i++)
temp.push(input.charCodeAt(i));
return temp;
};
CFData.encodeBase64String = function(input)
{
var temp = [];
for (var i = 0; i < input.length; i++)
temp.push(input.charCodeAt(i));
return CFData.encodeBase64Array(temp);
};
CFData.bytesToUtf16String = function(bytes)
{
// Strings are encoded with 16 bits per character.
var temp = [];
for (var i = 0; i < bytes.length; i += 2)
temp.push(bytes[i + 1] << 8 | bytes[i]);
// This is relatively efficient, I think:
return String.fromCharCode.apply(NULL, temp);
};
CFData.encodeBase64Utf16String = function(input)
{
// charCodeAt returns UTF-16.
var temp = [];
for (var i = 0; i < input.length; i++)
{
var c = input.charCodeAt(i);
temp.push(c & 0xFF);
temp.push((c & 0xFF00) >> 8);
}
return CFData.encodeBase64Array(temp);
};