objj-runtime
Version:
JavaScript (ECMAScript) and Objective-J runtime
919 lines (701 loc) • 26.1 kB
JavaScript
/*
* CFBundle.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
*/
var CFBundleUnloaded = 0,
CFBundleLoading = 1 << 0,
CFBundleLoadingInfoPlist = 1 << 1,
CFBundleLoadingExecutable = 1 << 2,
CFBundleLoadingSpritedImages = 1 << 3,
CFBundleLoadingLocalizableStrings = 1 << 4,
CFBundleLoaded = 1 << 5;
var CFBundlesForURLStrings = { },
CFBundlesForClasses = { },
CFBundlesWithIdentifiers = { },
CFCacheBuster = new Date().getTime(),
CFTotalBytesLoaded = 0,
CPApplicationSizeInBytes = 0;
var CPBundleDefaultBrowserLanguage = "CPBundleDefaultBrowserLanguage",
CPBundleDefaultLanguage = "CPBundleDefaultLanguage";
GLOBAL(CFBundle) = function(/*CFURL|String*/ aURL)
{
aURL = makeAbsoluteURL(aURL).asDirectoryPathURL();
var URLString = aURL.absoluteString(),
existingBundle = CFBundlesForURLStrings[URLString];
if (existingBundle)
return existingBundle;
CFBundlesForURLStrings[URLString] = this;
this._bundleURL = aURL;
this._resourcesDirectoryURL = new CFURL("Resources/", aURL);
this._staticResource = NULL;
this._isValid = NO;
this._loadStatus = CFBundleUnloaded;
this._loadRequests = [];
this._infoDictionary = new CFDictionary();
this._eventDispatcher = new EventDispatcher(this);
this._localizableStrings = [];
this._loadedLanguage = NULL;
}
DISPLAY_NAME(CFBundle);
CFBundle.environments = function()
{
// Passed in by GCC.
return ENVIRONMENTS;
};
DISPLAY_NAME(CFBundle.environments);
CFBundle.bundleContainingURL = function(/*CFURL|String*/ aURL)
{
aURL = new CFURL(".", makeAbsoluteURL(aURL));
var previousURLString,
URLString = aURL.absoluteString();
while (!previousURLString || previousURLString !== URLString)
{
var bundle = CFBundlesForURLStrings[URLString];
if (bundle && bundle._isValid)
return bundle;
aURL = new CFURL("..", aURL);
previousURLString = URLString;
URLString = aURL.absoluteString();
}
return NULL;
};
DISPLAY_NAME(CFBundle.bundleContainingURL);
CFBundle.mainBundle = function()
{
return new CFBundle(mainBundleURL);
};
DISPLAY_NAME(CFBundle.mainBundle);
function addClassToBundle(aClass, aBundle)
{
if (aBundle)
CFBundlesForClasses[aClass.name] = aBundle;
}
function resetBundle()
{
CFBundlesForURLStrings = { };
CFBundlesForClasses = { };
CFBundlesWithIdentifiers = { };
//CFCacheBuster = new Date().getTime(),
CFTotalBytesLoaded = 0;
CPApplicationSizeInBytes = 0;
}
CFBundle.bundleForClass = function(/*Class*/ aClass)
{
return CFBundlesForClasses[aClass.name] || CFBundle.mainBundle();
};
DISPLAY_NAME(CFBundle.bundleForClass);
CFBundle.bundleWithIdentifier = function(/*String*/ bundleID)
{
return CFBundlesWithIdentifiers[bundleID] || NULL;
};
DISPLAY_NAME(CFBundle.bundleWithIdentifier);
CFBundle.prototype.bundleURL = function()
{
return this._bundleURL.absoluteURL();
};
DISPLAY_NAME(CFBundle.prototype.bundleURL);
CFBundle.prototype.resourcesDirectoryURL = function()
{
return this._resourcesDirectoryURL;
};
DISPLAY_NAME(CFBundle.prototype.resourcesDirectoryURL);
CFBundle.prototype.resourceURL = function(/*String*/ aResourceName, /*String*/ aType, /*String*/ aSubDirectory, /*String*/ localizationName)
{
if (aType)
aResourceName = aResourceName + "." + aType;
if (localizationName)
aResourceName = localizationName + aResourceName;
if (aSubDirectory)
aResourceName = aSubDirectory + "/" + aResourceName;
var resourceURL = (new CFURL(aResourceName, this.resourcesDirectoryURL())).mappedURL();
return resourceURL.absoluteURL();
};
DISPLAY_NAME(CFBundle.prototype.resourceURL);
CFBundle.prototype.mostEligibleEnvironmentURL = function()
{
if (this._mostEligibleEnvironmentURL === undefined)
this._mostEligibleEnvironmentURL = new CFURL(this.mostEligibleEnvironment() + ".environment/", this.bundleURL());
return this._mostEligibleEnvironmentURL;
}
DISPLAY_NAME(CFBundle.prototype.mostEligibleEnvironmentURL);
CFBundle.prototype.executableURL = function()
{
if (this._executableURL === undefined)
{
var executableSubPath = this.valueForInfoDictionaryKey("CPBundleExecutable");
if (!executableSubPath)
this._executableURL = NULL;
else
this._executableURL = new CFURL(executableSubPath, this.mostEligibleEnvironmentURL());
}
return this._executableURL;
};
DISPLAY_NAME(CFBundle.prototype.executableURL);
CFBundle.prototype.infoDictionary = function()
{
return this._infoDictionary;
};
DISPLAY_NAME(CFBundle.prototype.infoDictionary);
CFBundle.prototype.loadedLanguage = function()
{
return this._loadedLanguage;
};
CFBundle.prototype.valueForInfoDictionaryKey = function(/*String*/ aKey)
{
return this._infoDictionary.valueForKey(aKey);
};
DISPLAY_NAME(CFBundle.prototype.valueForInfoDictionaryKey);
CFBundle.prototype.identifier = function()
{
return this._infoDictionary.valueForKey("CPBundleIdentifier");
};
DISPLAY_NAME(CFBundle.prototype.identifier);
CFBundle.prototype.hasSpritedImages = function()
{
var environments = this._infoDictionary.valueForKey("CPBundleEnvironmentsWithImageSprites") || [],
index = environments.length,
mostEligibleEnvironment = this.mostEligibleEnvironment();
while (index--)
if (environments[index] === mostEligibleEnvironment)
return YES;
return NO;
};
DISPLAY_NAME(CFBundle.prototype.hasSpritedImages);
CFBundle.prototype.environments = function()
{
return this._infoDictionary.valueForKey("CPBundleEnvironments") || ["ObjJ"];
};
DISPLAY_NAME(CFBundle.prototype.environments);
CFBundle.prototype.mostEligibleEnvironment = function(/*Array*/ environments)
{
environments = environments || this.environments();
var objj_environments = CFBundle.environments(),
index = 0,
count = objj_environments.length,
innerCount = environments.length;
// Ugh, no indexOf, no objects-in-common.
for(; index < count; ++index)
{
var innerIndex = 0,
environment = objj_environments[index];
for (; innerIndex < innerCount; ++innerIndex)
if(environment === environments[innerIndex])
return environment;
}
return NULL;
};
DISPLAY_NAME(CFBundle.prototype.mostEligibleEnvironment);
CFBundle.prototype.isLoading = function()
{
return this._loadStatus & CFBundleLoading;
};
DISPLAY_NAME(CFBundle.prototype.isLoading);
CFBundle.prototype.isLoaded = function()
{
return !!(this._loadStatus & CFBundleLoaded);
};
DISPLAY_NAME(CFBundle.prototype.isLoaded);
CFBundle.prototype.load = function(/*BOOL*/ shouldExecute)
{
if (this._loadStatus !== CFBundleUnloaded)
return;
this._loadStatus = CFBundleLoading | CFBundleLoadingInfoPlist;
var self = this,
bundleURL = this.bundleURL(),
parentURL = new CFURL("..", bundleURL);
if (parentURL.absoluteString() === bundleURL.absoluteString())
parentURL = parentURL.schemeAndAuthority();
StaticResource.resolveResourceAtURL(parentURL, YES, function(aStaticResource)
{
var resourceName = bundleURL.lastPathComponent();
self._staticResource = aStaticResource._children[resourceName] ||
new StaticResource(bundleURL, aStaticResource, YES, NO);
function onsuccess(/*Event*/ anEvent)
{
self._loadStatus &= ~CFBundleLoadingInfoPlist;
var infoDictionary = anEvent.request.responsePropertyList();
self._isValid = !!infoDictionary || CFBundle.mainBundle() === self;
if (infoDictionary)
{
self._infoDictionary = infoDictionary;
var identifier = self._infoDictionary.valueForKey("CPBundleIdentifier");
if (identifier)
CFBundlesWithIdentifiers[identifier] = self;
}
if (!self._infoDictionary)
{
finishBundleLoadingWithError(self, new Error("Could not load bundle at \"" + path + "\""));
return;
}
if (self === CFBundle.mainBundle() && self.valueForInfoDictionaryKey("CPApplicationSize"))
CPApplicationSizeInBytes = self.valueForInfoDictionaryKey("CPApplicationSize").valueForKey("executable") || 0;
loadLanguageForBundle(self);
loadExecutableAndResources(self, shouldExecute);
}
function onfailure()
{
self._isValid = CFBundle.mainBundle() === self;
self._loadStatus = CFBundleUnloaded;
finishBundleLoadingWithError(self, new Error("Could not load bundle at \"" + self.bundleURL() + "\""));
}
new FileRequest(new CFURL("Info.plist", self.bundleURL()), onsuccess, onfailure);
});
};
DISPLAY_NAME(CFBundle.prototype.load);
function finishBundleLoadingWithError(/*CFBundle*/ aBundle, /*Event*/ anError)
{
resolveStaticResource(aBundle._staticResource);
aBundle._eventDispatcher.dispatchEvent(
{
type:"error",
error:anError,
bundle:aBundle
});
}
function loadExecutableAndResources(/*Bundle*/ aBundle, /*BOOL*/ shouldExecute)
{
if (!aBundle.mostEligibleEnvironment())
return failure();
loadExecutableForBundle(aBundle, success, failure, progress);
loadSpritedImagesForBundle(aBundle, success, failure, progress);
loadLocalizableStringsForBundle(aBundle, success, failure, progress);
if (aBundle._loadStatus === CFBundleLoading)
return success();
function failure(/*Error*/ anError)
{
var loadRequests = aBundle._loadRequests,
count = loadRequests.length;
while (count--)
loadRequests[count].abort();
this._loadRequests = [];
aBundle._loadStatus = CFBundleUnloaded;
finishBundleLoadingWithError(aBundle, anError || new Error("Could not recognize executable code format in Bundle " + aBundle));
}
function progress(bytesLoaded)
{
if ((typeof CPApp === "undefined" || !CPApp || !CPApp._finishedLaunching) &&
typeof OBJJ_PROGRESS_CALLBACK === "function")
{
CFTotalBytesLoaded += bytesLoaded;
var percent = CPApplicationSizeInBytes ? MAX(MIN(1.0, CFTotalBytesLoaded / CPApplicationSizeInBytes), 0.0) : 0;
OBJJ_PROGRESS_CALLBACK(percent, CPApplicationSizeInBytes, aBundle.bundlePath());
}
}
function success()
{
if (aBundle._loadStatus === CFBundleLoading)
aBundle._loadStatus = CFBundleLoaded;
else
return;
// Set resolved to true here in case during evaluation this bundle
// needs to resolve another bundle which in turn needs it to be resolved (cycle).
resolveStaticResource(aBundle._staticResource);
function complete()
{
aBundle._eventDispatcher.dispatchEvent(
{
type:"load",
bundle:aBundle
});
}
if (shouldExecute)
executeBundle(aBundle, complete);
else
complete();
}
}
function loadExecutableForBundle(/*Bundle*/ aBundle, success, failure, progress)
{
var executableURL = aBundle.executableURL();
if (!executableURL)
return;
aBundle._loadStatus |= CFBundleLoadingExecutable;
new FileRequest(executableURL, function(/*Event*/ anEvent)
{
try
{
decompileStaticFile(aBundle, anEvent.request.responseText(), executableURL);
aBundle._loadStatus &= ~CFBundleLoadingExecutable;
success();
}
catch(anException)
{
failure(anException);
}
}, failure, progress);
}
function spritedImagesTestURLStringForBundle(/*Bundle*/ aBundle)
{
return "mhtml:" + new CFURL("MHTMLTest.txt", aBundle.mostEligibleEnvironmentURL());
}
function spritedImagesURLForBundle(/*Bundle*/ aBundle)
{
if (CFBundleSupportedSpriteType === CFBundleDataURLSpriteType)
return new CFURL("dataURLs.txt", aBundle.mostEligibleEnvironmentURL());
if (CFBundleSupportedSpriteType === CFBundleMHTMLSpriteType ||
CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType)
return new CFURL("MHTMLPaths.txt", aBundle.mostEligibleEnvironmentURL());
return NULL;
}
function loadSpritedImagesForBundle(/*Bundle*/ aBundle, success, failure, progress)
{
if (!aBundle.hasSpritedImages())
return;
aBundle._loadStatus |= CFBundleLoadingSpritedImages;
if (!CFBundleHasTestedSpriteSupport())
return CFBundleTestSpriteSupport(spritedImagesTestURLStringForBundle(aBundle), function()
{
loadSpritedImagesForBundle(aBundle, success, failure, progress);
});
var spritedImagesURL = spritedImagesURLForBundle(aBundle);
if (!spritedImagesURL)
{
aBundle._loadStatus &= ~CFBundleLoadingSpritedImages;
return success();
}
new FileRequest(spritedImagesURL, function(/*Event*/ anEvent)
{
try
{
decompileStaticFile(aBundle, anEvent.request.responseText(), spritedImagesURL);
aBundle._loadStatus &= ~CFBundleLoadingSpritedImages;
success();
}
catch(anException)
{
failure(anException);
}
}, failure, progress);
}
function loadLocalizableStringsForBundle(/*Bundle*/ aBundle, success, failure, progress)
{
var language = aBundle._loadedLanguage;
if (!language)
return;
var localizableStrings = aBundle.valueForInfoDictionaryKey("CPBundleLocalizableStrings");
if (!localizableStrings)
return;
var self = aBundle,
length = localizableStrings.length,
languagePathURL = new CFURL(language + ".lproj/", self.resourcesDirectoryURL()),
fileSuccessed = 0;
for (var i = 0; i < length; i++)
{
var localizableString = localizableStrings[i];
function onsuccess(/*Event*/ anEvent)
{
var contentFile = anEvent.request.responseText(),
tableName = new CFURL(anEvent.request._URL).lastPathComponent();
try
{
loadLocalizableContentForFileInBundle(self, contentFile, tableName);
if (++fileSuccessed == length)
{
aBundle._loadStatus &= ~CFBundleLoadingLocalizableStrings;
success();
}
}
catch(e)
{
failure(new Error("Error when parsing the localizable file " + tableName));
}
}
aBundle._loadStatus |= CFBundleLoadingLocalizableStrings;
new FileRequest(new CFURL(localizableString, languagePathURL), onsuccess, failure, progress);
}
}
function loadLocalizableContentForFileInBundle(bundle, contentFile, tableName)
{
var values = {},
lines = contentFile.split("\n"),
currentContext;
bundle._localizableStrings[tableName] = values;
for (var i = 0; i < lines.length; i++)
{
var line = lines[i];
// Context
if (line[0] == "/")
{
currentContext = line.substring(2, line.length - 2).trim();
continue;
}
// Key
if (line[0] == "\"")
{
var split = line.split("\"")
// Here we add twice the translated value, once with the context and once without
// This will help in term of rapidity when asking the value of the key
var key = split[1];
if (!(key in values))
values[key] = split[3];
key += currentContext;
if (!(key in values))
values[key] = split[3];
continue;
}
}
}
function loadLanguageForBundle(aBundle)
{
if (aBundle._loadedLanguage)
return;
var defaultLanguage = aBundle.valueForInfoDictionaryKey(CPBundleDefaultLanguage);
if (defaultLanguage != CPBundleDefaultBrowserLanguage && defaultLanguage)
{
aBundle._loadedLanguage = defaultLanguage;
return;
}
if (typeof navigator == "undefined")
return;
// userLanguage is an IE only property.
var language = (typeof navigator.language !== "undefined") ? navigator.language : navigator.userLanguage;
if (!language)
return;
aBundle._loadedLanguage = language.substring(0, 2);
}
var CFBundleSpriteSupportListeners = [],
CFBundleSupportedSpriteType = -1,
CFBundleNoSpriteType = 0,
CFBundleDataURLSpriteType = 1,
CFBundleMHTMLSpriteType = 2,
CFBundleMHTMLUncachedSpriteType = 3;
function CFBundleHasTestedSpriteSupport()
{
return CFBundleSupportedSpriteType !== -1;
}
function CFBundleTestSpriteSupport(/*String*/ MHTMLPath, /*Function*/ aCallback)
{
if (CFBundleHasTestedSpriteSupport())
return;
CFBundleSpriteSupportListeners.push(aCallback);
if (CFBundleSpriteSupportListeners.length > 1)
return;
CFBundleSpriteSupportListeners.push(function()
{
var size = 0,
sizeDictionary = CFBundle.mainBundle().valueForInfoDictionaryKey("CPApplicationSize");
if (!sizeDictionary)
return;
switch (CFBundleSupportedSpriteType)
{
case CFBundleDataURLSpriteType:
size = sizeDictionary.valueForKey("data");
break;
case CFBundleMHTMLSpriteType:
case CFBundleMHTMLUncachedSpriteType:
size = sizeDictionary.valueForKey("mhtml");
break;
}
CPApplicationSizeInBytes += size;
});
CFBundleTestSpriteTypes([
CFBundleDataURLSpriteType,
"data:image/gif;base64,R0lGODlhAQABAIAAAMc9BQAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==",
CFBundleMHTMLSpriteType,
MHTMLPath+"!test",
CFBundleMHTMLUncachedSpriteType,
MHTMLPath+"?"+CFCacheBuster+"!test"
]);
}
function CFBundleNotifySpriteSupportListeners()
{
var count = CFBundleSpriteSupportListeners.length;
while (count--)
CFBundleSpriteSupportListeners[count]();
}
function CFBundleTestSpriteTypes(/*Array*/ spriteTypes)
{
// If we don't support Images, then clearly we don't support sprites.
if (!("Image" in global) || spriteTypes.length < 2)
{
CFBundleSupportedSpriteType = CFBundleNoSpriteType;
CFBundleNotifySpriteSupportListeners();
return;
}
var image = new Image();
image.onload = function()
{
if (image.width === 1 && image.height === 1)
{
CFBundleSupportedSpriteType = spriteTypes[0];
CFBundleNotifySpriteSupportListeners();
}
else
image.onerror();
};
image.onerror = function()
{
CFBundleTestSpriteTypes(spriteTypes.slice(2));
};
image.src = spriteTypes[1];
}
function executeBundle(/*Bundle*/ aBundle, /*Function*/ aCallback)
{
var staticResources = [aBundle._staticResource];
function executeStaticResources(index)
{
for (; index < staticResources.length; ++index)
{
var staticResource = staticResources[index];
if (staticResource.isNotFound())
continue;
if (staticResource.isFile())
{
var executable = new FileExecutable(staticResource.URL());
if (executable.hasLoadedFileDependencies())
executable.execute();
else
{
executable.loadFileDependencies(function()
{
executeStaticResources(index);
});
return;
}
}
else //if (staticResource.isDirectory())
{
// We don't want to execute resources.
if (staticResource.URL().absoluteString() === aBundle.resourcesDirectoryURL().absoluteString())
continue;
var children = staticResource.children();
for (var name in children)
if (hasOwnProperty.call(children, name))
staticResources.push(children[name]);
}
}
aCallback();
}
executeStaticResources(0);
}
var STATIC_MAGIC_NUMBER = "@STATIC",
MARKER_PATH = "p",
MARKER_URI = "u",
MARKER_CODE = "c",
MARKER_TEXT = "t",
MARKER_IMPORT_STD = 'I',
MARKER_IMPORT_LOCAL = 'i';
MARKER_SOURCE_MAP = 'S';
function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString, /*String*/ aPath)
{
var stream = new MarkedStream(aString);
if (stream.magicNumber() !== STATIC_MAGIC_NUMBER)
throw new Error("Could not read static file: " + aPath);
if (stream.version() !== "1.0")
throw new Error("Could not read static file: " + aPath);
var marker,
bundleURL = aBundle.bundleURL(),
file = NULL;
while (marker = stream.getMarker())
{
var text = stream.getString();
if (marker === MARKER_PATH)
{
var fileURL = new CFURL(text, bundleURL),
parent = StaticResource.resourceAtURL(new CFURL(".", fileURL), YES);
file = new StaticResource(fileURL, parent, NO, YES);
}
else if (marker === MARKER_URI)
{
var URL = new CFURL(text, bundleURL),
mappedURLString = stream.getString();
if (mappedURLString.indexOf("mhtml:") === 0)
{
mappedURLString = "mhtml:" + new CFURL(mappedURLString.substr("mhtml:".length), bundleURL);
if (CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType)
{
var exclamationIndex = mappedURLString.indexOf("!"),
firstPart = mappedURLString.substring(0, exclamationIndex),
lastPart = mappedURLString.substring(exclamationIndex);
mappedURLString = firstPart + "?" + CFCacheBuster + lastPart;
}
}
CFURL.setMappedURLForURL(URL, new CFURL(mappedURLString));
// The unresolved directories must not be bundles.
var parent = StaticResource.resourceAtURL(new CFURL(".", URL), YES);
new StaticResource(URL, parent, NO, YES);
}
else if (marker === MARKER_TEXT)
file.write(text);
}
}
// Event Managament
CFBundle.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
this._eventDispatcher.addEventListener(anEventName, anEventListener);
};
DISPLAY_NAME(CFBundle.prototype.addEventListener);
CFBundle.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
this._eventDispatcher.removeEventListener(anEventName, anEventListener);
};
DISPLAY_NAME(CFBundle.prototype.removeEventListener);
CFBundle.prototype.onerror = function(/*Event*/ anEvent)
{
throw anEvent.error;
};
DISPLAY_NAME(CFBundle.prototype.onerror);
CFBundle.prototype.bundlePath = function()
{
return this.bundleURL().path();
};
CFBundle.prototype.path = function()
{
CPLog.warn("CFBundle.prototype.path is deprecated, use CFBundle.prototype.bundlePath instead.");
return this.bundlePath.apply(this, arguments);
};
CFBundle.prototype.pathForResource = function(aResource, aType, aSubDirectory, localizationName)
{
return this.resourceURL(aResource, aType, aSubDirectory, localizationName).absoluteString();
};
GLOBAL(CFBundleCopyLocalizedString) = function (/*Bundle*/ bundle, key, value, tableName)
{
return CFCopyLocalizedStringWithDefaultValue(key, tableName, bundle, value, "");
}
GLOBAL(CFBundleCopyBundleLocalizations) = function (/*Bundle*/ aBundle)
{
return [this._loadedLanguage];
}
GLOBAL(CFCopyLocalizedString) = function (key, comment)
{
return CFCopyLocalizedStringFromTable(key, "Localizable", comment);
}
GLOBAL(CFCopyLocalizedStringFromTable) = function (key, tableName, comment)
{
return CFCopyLocalizedStringFromTableInBundle(key, tableName, CFBundleGetMainBundle(), comment);
}
GLOBAL(CFCopyLocalizedStringFromTableInBundle) = function (key, tableName, bundle, comment)
{
return CFCopyLocalizedStringWithDefaultValue(key, tableName, bundle, null, comment);
}
GLOBAL(CFCopyLocalizedStringWithDefaultValue) = function (key, tableName, bundle, value, comment)
{
var string;
if (!tableName)
tableName = "Localizable";
tableName += ".strings";
var localizableString = bundle._localizableStrings[tableName];
string = localizableString ? localizableString[key + comment] : null;
return string || (value || key);
}
GLOBAL(CFBundleGetMainBundle) = function ()
{
return CFBundle.mainBundle();
};