mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
A Cappuccino project has always needed to have a flat file structure to be able to compile from source in the web browser. A new dictionary can be added to the project's Info.plist with the key 'CPFileTranslationDictionary'. It should contain all the path translations in the project to allow the Browser to find out where in the file structure the source files are. Check in the AppKit framework for an example. Also, a include list can be added in the Info.plist for the key 'CPCompileIncludeFileArray'. The file in the list will be included by a '#include' statement for each file that is compiled in this project A last new feature is to add macros in the 'OBJJ_COMPILER_FLAGS' global variable. It is usually declared in the index.html file. It is done with the format '-Dmacroname[=macrodefinition]'. An updated Info.plist file in the AppKit framework will allow the AppKit to compile from the source in the web browser. The following things is needed to get this to work: 1. Replace the AppKit folder in your project's Frameworks folder with a copy of the AppKit folder from the Cappuccino project. 2. Add a copy of the built Aristo2.blend folder to the AppKit's Resource folder. 3. Add "-DPLATFORM_DOM", "-DPLATFORM_BROWSER" and optionally "-DDEBUG" to the global variable 'OBJJ_COMPILER_FLAGS' in the project's index.html file. The app load time will increase (about 3 seconds on a 2018 MacBook Pro) as the AppKit framework is compiled from source. The Foundation framework is not yet tested but it might be working by adding info about the file structure in the Info.plist file.
717 lines
22 KiB
JavaScript
717 lines
22 KiB
JavaScript
/*
|
|
* Executable.js
|
|
* Objective-J
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 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 ExecutableUnloadedFileDependencies = 0,
|
|
ExecutableLoadingFileDependencies = 1,
|
|
ExecutableLoadedFileDependencies = 2,
|
|
AnonymousExecutableCount = 0;
|
|
|
|
function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String*/ aURL, /*Function*/ aFunction, /*ObjJCompiler*/aCompiler, /*Dictionary*/ aFilenameTranslateDictionary, /* Base64 String */ sourceMap)
|
|
{
|
|
if (arguments.length === 0)
|
|
return this;
|
|
|
|
this._code = aCode;
|
|
this._function = aFunction || null;
|
|
this._URL = makeAbsoluteURL(aURL || new CFURL("(Anonymous" + (AnonymousExecutableCount++) + ")"));
|
|
|
|
this._compiler = aCompiler || null;
|
|
|
|
this._fileDependencies = fileDependencies;
|
|
this._filenameTranslateDictionary = aFilenameTranslateDictionary;
|
|
|
|
if (sourceMap)
|
|
this._base64EncodedSourceMap = sourceMap;
|
|
|
|
if (fileDependencies.length)
|
|
{
|
|
this._fileDependencyStatus = ExecutableUnloadedFileDependencies;
|
|
this._fileDependencyCallbacks = [];
|
|
}
|
|
else
|
|
{
|
|
this._fileDependencyStatus = ExecutableLoadedFileDependencies;
|
|
}
|
|
|
|
if (this._function)
|
|
return;
|
|
|
|
if (!aCompiler)
|
|
this.setCode(aCode);
|
|
}
|
|
|
|
exports.Executable = Executable;
|
|
|
|
Executable.prototype.path = function()
|
|
{
|
|
return this.URL().path();
|
|
};
|
|
|
|
Executable.prototype.URL = function()
|
|
{
|
|
return this._URL;
|
|
};
|
|
|
|
DISPLAY_NAME(Executable.prototype.URL);
|
|
|
|
Executable.prototype.functionParameters = function()
|
|
{
|
|
var functionParameters = ["global", "objj_executeFile", "objj_importFile"];
|
|
|
|
//exportedNames().concat("objj_executeFile", "objj_importFile");
|
|
|
|
#ifdef COMMONJS
|
|
functionParameters = functionParameters.concat("require", "exports", "module", "system", "print", "window");
|
|
#endif
|
|
|
|
return functionParameters;
|
|
};
|
|
|
|
DISPLAY_NAME(Executable.prototype.functionParameters);
|
|
|
|
Executable.prototype.functionArguments = function()
|
|
{
|
|
var functionArguments = [global, this.fileExecuter(), this.fileImporter()];
|
|
|
|
#ifdef COMMONJS
|
|
functionArguments = functionArguments.concat(Executable.commonJSArguments());
|
|
#endif
|
|
|
|
return functionArguments;
|
|
};
|
|
|
|
DISPLAY_NAME(Executable.prototype.functionArguments);
|
|
|
|
#ifdef COMMONJS
|
|
Executable.setCommonJSParameters = function()
|
|
{
|
|
this._commonJSParameters = Array.prototype.slice.call(arguments);
|
|
};
|
|
|
|
Executable.commonJSParameters = function()
|
|
{
|
|
return this._commonJSParameters || [];
|
|
};
|
|
|
|
Executable.setCommonJSArguments = function()
|
|
{
|
|
this._commonJSArguments = Array.prototype.slice.call(arguments);
|
|
};
|
|
|
|
Executable.commonJSArguments = function()
|
|
{
|
|
return this._commonJSArguments || [];
|
|
};
|
|
|
|
Executable.setFilenameTranslateDictionary = function(dict)
|
|
{
|
|
this._filenameTranslateDictionary = dict;
|
|
};
|
|
|
|
Executable.filenameTranslateDictionary = function()
|
|
{
|
|
return this._filenameTranslateDictionary || {};
|
|
};
|
|
|
|
|
|
Executable.prototype.toMarkedString = function()
|
|
{
|
|
var markedString = "@STATIC;1.0;",
|
|
dependencies = this.fileDependencies(),
|
|
index = 0,
|
|
count = dependencies.length;
|
|
|
|
for (; index < count; ++index)
|
|
markedString += dependencies[index].toMarkedString();
|
|
|
|
var sourceMap = this._base64EncodedSourceMap;
|
|
|
|
if (sourceMap) {
|
|
markedString += MARKER_SOURCE_MAP + ";" + sourceMap.length + ";" + sourceMap;
|
|
}
|
|
|
|
var code = this.code();
|
|
|
|
return markedString + MARKER_TEXT + ";" + code.length + ";" + code;
|
|
};
|
|
#endif
|
|
|
|
Executable.prototype.execute = function()
|
|
{
|
|
#if EXECUTION_LOGGING
|
|
CPLog("EXECUTION: " + this.URL());
|
|
#endif
|
|
|
|
if (this._compiler)
|
|
{
|
|
var fileDependencies = this.fileDependencies(),
|
|
index = 0,
|
|
count = fileDependencies.length;
|
|
|
|
this._compiler.pushImport(this.URL().lastPathComponent());
|
|
for (; index < count; ++index)
|
|
{
|
|
var fileDependency = fileDependencies[index],
|
|
isQuoted = fileDependency.isLocal(),
|
|
URL = fileDependency.URL();
|
|
|
|
this.fileExecuter()(URL, isQuoted);
|
|
}
|
|
this._compiler.popImport();
|
|
|
|
this.setCode(this._compiler.compilePass2(), this._compiler.map());
|
|
|
|
if (Executable.printWarningsAndErrors(this._compiler, exports.messageOutputFormatInXML))
|
|
throw "Compilation error";
|
|
|
|
// We don't need to save the AST tree and the source code. It will take up a lot of memory.
|
|
this._compiler = null;
|
|
}
|
|
|
|
var oldContextBundle = CONTEXT_BUNDLE;
|
|
|
|
// FIXME: Should we have stored this?
|
|
CONTEXT_BUNDLE = CFBundle.bundleContainingURL(this.URL());
|
|
|
|
var result = this._function.apply(global, this.functionArguments());
|
|
|
|
CONTEXT_BUNDLE = oldContextBundle;
|
|
|
|
return result;
|
|
};
|
|
|
|
DISPLAY_NAME(Executable.prototype.execute);
|
|
|
|
Executable.prototype.code = function()
|
|
{
|
|
return this._code;
|
|
};
|
|
|
|
DISPLAY_NAME(Executable.prototype.code);
|
|
|
|
Executable.prototype.setCode = function(code, sourceMap)
|
|
{
|
|
this._code = code;
|
|
|
|
var parameters = this.functionParameters().join(",");
|
|
var sourceMapBase64;
|
|
|
|
#if COMMONJS
|
|
if (typeof system !== "undefined" && system.engine === "rhino")
|
|
{
|
|
code = "function(" + parameters + "){" + code + "/**/\n}";
|
|
this._function = Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(window, code, this.URL().absoluteString(), 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
// Check if base64 source map is available
|
|
sourceMapBase64 = this._base64EncodedSourceMap;
|
|
|
|
// "//# sourceURL=" at the end lets us name our eval'd files for debuggers, etc.
|
|
// * WebKit: http://pmuellr.blogspot.com/2009/06/debugger-friendly.html
|
|
// * Firebug: http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
|
|
//if (YES) {
|
|
var absoluteString = this.URL().absoluteString();
|
|
|
|
code += "/**/\n//# sourceURL=" + absoluteString + "s";
|
|
|
|
if (sourceMap)
|
|
{
|
|
if (typeof btoa === 'function')
|
|
sourceMapBase64 = btoa(UTF16ToUTF8(sourceMap));
|
|
else if (typeof Buffer === 'function')
|
|
sourceMapBase64 = new Buffer(sourceMap).toString("base64");
|
|
}
|
|
|
|
if (sourceMapBase64) {
|
|
// The new Function constructor will add a function header before the first line
|
|
// The compiler adds two newlines as the first character to the code to get the source
|
|
// mapping correct. We have to remove it here. As Javascript engines adds diffentent
|
|
// amount of lines at the top we need to calculate how many.
|
|
|
|
// '(exports.ObjJCompiler || ObjJCompiler)' is a temporary fix so it can work both in the Narwhal (exports.ObjJCompiler) and Node (ObjJCompiler) world
|
|
code = code.substring((exports.ObjJCompiler || ObjJCompiler).numberOfLinesAtTopOfFunction());
|
|
this._base64EncodedSourceMap = sourceMapBase64;
|
|
code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + sourceMapBase64;
|
|
}
|
|
//} else {
|
|
// // Firebug only does it for "eval()", not "new Function()". Ugh. Slower.
|
|
// var functionText = "(function(){"+GET_CODE(aFragment)+"/**/\n})\n//# sourceURL="+GET_FILE(aFragment).path;
|
|
// compiled = eval(functionText);
|
|
//}
|
|
this._function = new Function(parameters, code);
|
|
this._function.displayName = absoluteString;
|
|
#if COMMONJS
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.setCode);
|
|
|
|
Executable.prototype.fileDependencies = function()
|
|
{
|
|
return this._fileDependencies;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.fileDependencies);
|
|
|
|
Executable.prototype.setFileDependencies = function(newValue)
|
|
{
|
|
this._fileDependencies = newValue;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.setFileDependencies);
|
|
|
|
Executable.prototype.hasLoadedFileDependencies = function()
|
|
{
|
|
return this._fileDependencyStatus === ExecutableLoadedFileDependencies;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.hasLoadedFileDependencies);
|
|
|
|
var fileDependencyLoadCount = 0,
|
|
fileDependencyExecutables = [],
|
|
fileDependencyMarkers = { };
|
|
|
|
Executable.prototype.loadFileDependencies = function(aCallback)
|
|
{
|
|
var status = this._fileDependencyStatus;
|
|
|
|
if (aCallback)
|
|
{
|
|
if (status === ExecutableLoadedFileDependencies)
|
|
return aCallback();
|
|
|
|
this._fileDependencyCallbacks.push(aCallback);
|
|
}
|
|
|
|
if (status === ExecutableUnloadedFileDependencies)
|
|
{
|
|
if (fileDependencyLoadCount) {
|
|
throw "Can't load: " + this.URL().absoluteURL();
|
|
}
|
|
|
|
loadFileDependenciesForExecutable(this);
|
|
}
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.loadFileDependencies);
|
|
|
|
Executable.prototype.hasExecutableUnloadedFileDependencies = function()
|
|
{
|
|
return this._fileDependencyStatus === ExecutableUnloadedFileDependencies;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.hasExecutableUnloadedFileDependencies);
|
|
|
|
function loadFileDependenciesForExecutable(/*Executable*/ anExecutable)
|
|
{
|
|
fileDependencyExecutables.push(anExecutable);
|
|
anExecutable._fileDependencyStatus = ExecutableLoadingFileDependencies;
|
|
|
|
var fileDependencies = anExecutable.fileDependencies(),
|
|
index = 0,
|
|
count = fileDependencies.length,
|
|
referenceURL = anExecutable.referenceURL(),
|
|
referenceURLString = referenceURL.absoluteString(),
|
|
fileExecutableSearcher = anExecutable.fileExecutableSearcher();
|
|
|
|
fileDependencyLoadCount += count;
|
|
|
|
for (; index < count; ++index)
|
|
{
|
|
var fileDependency = fileDependencies[index],
|
|
isQuoted = fileDependency.isLocal(),
|
|
URL = fileDependency.URL(),
|
|
marker = (isQuoted && (referenceURLString + " ") || "") + URL;
|
|
|
|
if (fileDependencyMarkers[marker])
|
|
{
|
|
if (--fileDependencyLoadCount === 0)
|
|
fileExecutableDependencyLoadFinished();
|
|
|
|
continue;
|
|
}
|
|
|
|
fileDependencyMarkers[marker] = YES;
|
|
fileExecutableSearcher(URL, isQuoted, fileExecutableSearchFinished);
|
|
}
|
|
}
|
|
|
|
function fileExecutableSearchFinished(/*FileExecutable*/ aFileExecutable)
|
|
{
|
|
--fileDependencyLoadCount;
|
|
|
|
if (aFileExecutable._fileDependencyStatus === ExecutableUnloadedFileDependencies)
|
|
loadFileDependenciesForExecutable(aFileExecutable);
|
|
|
|
else if (fileDependencyLoadCount === 0)
|
|
fileExecutableDependencyLoadFinished();
|
|
}
|
|
|
|
function fileExecutableDependencyLoadFinished()
|
|
{
|
|
var executables = fileDependencyExecutables,
|
|
index = 0,
|
|
count = executables.length;
|
|
|
|
fileDependencyExecutables = [];
|
|
|
|
for (; index < count; ++index)
|
|
executables[index]._fileDependencyStatus = ExecutableLoadedFileDependencies;
|
|
|
|
for (index = 0; index < count; ++index)
|
|
{
|
|
var executable = executables[index],
|
|
callbacks = executable._fileDependencyCallbacks,
|
|
callbackIndex = 0,
|
|
callbackCount = callbacks.length;
|
|
|
|
for (; callbackIndex < callbackCount; ++callbackIndex)
|
|
callbacks[callbackIndex]();
|
|
|
|
executable._fileDependencyCallbacks = [];
|
|
}
|
|
}
|
|
|
|
Executable.prototype.referenceURL = function()
|
|
{
|
|
if (this._referenceURL === undefined)
|
|
// Removed the filename (if any) from the path to get the directory
|
|
this._referenceURL = new CFURL(".", this.URL());
|
|
|
|
return this._referenceURL;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.referenceURL);
|
|
|
|
Executable.prototype.fileImporter = function()
|
|
{
|
|
return Executable.fileImporterForURL(this.referenceURL());
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.fileImporter);
|
|
|
|
Executable.prototype.fileExecuter = function()
|
|
{
|
|
return Executable.fileExecuterForURL(this.referenceURL());
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.fileExecuter);
|
|
|
|
Executable.prototype.fileExecutableSearcher = function()
|
|
{
|
|
return Executable.fileExecutableSearcherForURL(this.referenceURL());
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.prototype.fileExecutableSearcher);
|
|
|
|
var cachedFileExecuters = { };
|
|
|
|
// All the file dependences must be loaded and executed before this can be executed.
|
|
Executable.fileExecuterForURL = function(/*CFURL|String*/ aURL)
|
|
{
|
|
var referenceURL = makeAbsoluteURL(aURL),
|
|
referenceURLString = referenceURL.absoluteString(),
|
|
cachedFileExecuter = cachedFileExecuters[referenceURLString];
|
|
|
|
if (!cachedFileExecuter)
|
|
{
|
|
cachedFileExecuter = function(/*CFURL*/ aURL, /*BOOL*/ isQuoted, /*BOOL*/ shouldForce)
|
|
{
|
|
Executable.fileExecutableSearcherForURL(referenceURL)(aURL, isQuoted,
|
|
function(/*FileExecutable*/ aFileExecutable)
|
|
{
|
|
if (!aFileExecutable.hasLoadedFileDependencies())
|
|
throw "No executable loaded for file at URL " + aURL;
|
|
|
|
aFileExecutable.execute(shouldForce);
|
|
});
|
|
}
|
|
|
|
cachedFileExecuters[referenceURLString] = cachedFileExecuter;
|
|
}
|
|
|
|
return cachedFileExecuter;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.fileExecuterForURL);
|
|
|
|
var cachedFileImporters = { };
|
|
|
|
Executable.fileImporterForURL = function(/*CFURL|String*/ aURL)
|
|
{
|
|
var referenceURL = makeAbsoluteURL(aURL),
|
|
referenceURLString = referenceURL.absoluteString(),
|
|
cachedFileImporter = cachedFileImporters[referenceURLString];
|
|
|
|
if (!cachedFileImporter)
|
|
{
|
|
cachedFileImporter = function(/*CFURL*/ aURL, /*BOOL*/ isQuoted, /*Function*/ aCallback)
|
|
{
|
|
// We make heavy use of URLs throughout this process, so cache them!
|
|
enableCFURLCaching();
|
|
|
|
Executable.fileExecutableSearcherForURL(referenceURL)(aURL, isQuoted,
|
|
function(/*FileExecutable*/ aFileExecutable)
|
|
{
|
|
aFileExecutable.loadFileDependencies(function()
|
|
{
|
|
aFileExecutable.execute();
|
|
|
|
// No more need to cache these.
|
|
disableCFURLCaching();
|
|
|
|
if (aCallback)
|
|
aCallback();
|
|
});
|
|
});
|
|
}
|
|
|
|
cachedFileImporters[referenceURLString] = cachedFileImporter;
|
|
}
|
|
|
|
return cachedFileImporter;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.fileImporterForURL);
|
|
|
|
var cachedFileExecutableSearchers = { },
|
|
cachedFileExecutableSearchResults = { };
|
|
|
|
Executable.resetCachedFileExecutableSearchers = function()
|
|
{
|
|
cachedFileExecutableSearchers = { };
|
|
cachedFileExecutableSearchResults = { };
|
|
cachedFileImporters = { };
|
|
cachedFileExecuters = { };
|
|
fileDependencyMarkers = { };
|
|
}
|
|
|
|
Executable.fileExecutableSearcherForURL = function(/*CFURL*/ referenceURL)
|
|
{
|
|
var referenceURLString = referenceURL.absoluteString(),
|
|
cachedFileExecutableSearcher = cachedFileExecutableSearchers[referenceURLString];
|
|
|
|
if (!cachedFileExecutableSearcher)
|
|
{
|
|
var aFilenameTranslateDictionary = Executable.filenameTranslateDictionary ? Executable.filenameTranslateDictionary() : null;
|
|
|
|
cachedFileExecutableSearcher = function(/*CFURL*/ aURL, /*BOOL*/ isQuoted, /*Function*/ success)
|
|
{
|
|
var cacheUID = (isQuoted && referenceURL || "") + aURL,
|
|
cachedResult = cachedFileExecutableSearchResults[cacheUID];
|
|
|
|
if (cachedResult)
|
|
return completed(cachedResult);
|
|
|
|
var isAbsoluteURL = (aURL instanceof CFURL) && aURL.scheme();
|
|
|
|
if (isQuoted || isAbsoluteURL)
|
|
{
|
|
if (!isAbsoluteURL)
|
|
aURL = new CFURL(aURL, referenceURL);
|
|
|
|
StaticResource.resolveResourceAtURL(aURL, NO, completed, aFilenameTranslateDictionary);
|
|
}
|
|
else
|
|
StaticResource.resolveResourceAtURLSearchingIncludeURLs(aURL, completed);
|
|
|
|
function completed(/*StaticResource*/ aStaticResource)
|
|
{
|
|
if (!aStaticResource)
|
|
{
|
|
// '(exports.ObjJCompiler || ObjJCompiler)' is a temporary fix so it can work both in the Narwhal (exports.ObjJCompiler) and Node (ObjJCompiler) world
|
|
var compilingFileUrl = (exports.ObjJCompiler || ObjJCompiler) ? (exports.ObjJCompiler || ObjJCompiler).currentCompileFile : null;
|
|
throw new Error("Could not load file at " + aURL + (compilingFileUrl ? " when compiling " + compilingFileUrl : "") + "\nwith includeURLs: " + StaticResource.includeURLs());
|
|
}
|
|
|
|
cachedFileExecutableSearchResults[cacheUID] = aStaticResource;
|
|
|
|
success(new FileExecutable(aStaticResource.URL(), aFilenameTranslateDictionary));
|
|
}
|
|
};
|
|
|
|
cachedFileExecutableSearchers[referenceURLString] = cachedFileExecutableSearcher;
|
|
}
|
|
|
|
return cachedFileExecutableSearcher;
|
|
}
|
|
|
|
DISPLAY_NAME(Executable.fileExecutableSearcherForURL);
|
|
|
|
/*!
|
|
This funtion prints all errors and warnings for the provided compiler. It returns true if there
|
|
are any errors in the list. it will print it in xml format if printXML is 'true'
|
|
*/
|
|
Executable.printWarningsAndErrors = function(/*ObjJCompiler*/ compiler, /*BOOL*/ printXML)
|
|
{
|
|
var warnings = [],
|
|
anyErrors = false;
|
|
|
|
for (var i = 0; i < compiler.warningsAndErrors.length; i++)
|
|
{
|
|
var warning = compiler.warningsAndErrors[i],
|
|
message = compiler.prettifyMessage(warning);
|
|
|
|
// Set anyErrors to 'true' if there are any errors in the list
|
|
anyErrors = anyErrors || warning.messageType === "ERROR";
|
|
#ifdef BROWSER
|
|
console.log(message);
|
|
#else
|
|
if (printXML)
|
|
{
|
|
var dict = new CFMutableDictionary();
|
|
if (warning.messageOnLine != null) dict.addValueForKey('line', warning.messageOnLine)
|
|
if (warning.path != null) dict.addValueForKey('sourcePath', new CFURL(warning.path).path())
|
|
if (message != null) dict.addValueForKey('message', message)
|
|
|
|
warnings.push(dict);
|
|
}
|
|
else
|
|
{
|
|
print(message);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef BROWSER
|
|
if (warnings.length && printXML)
|
|
try {
|
|
print(CFPropertyListCreateXMLData(warnings, kCFPropertyListXMLFormat_v1_0).rawString());
|
|
} catch (e) {
|
|
print ("XML encode error: " + e);
|
|
}
|
|
#endif
|
|
|
|
return anyErrors;
|
|
}
|
|
|
|
/*
|
|
* Adaption to javascript by Malte Tancred 2012 from ConvertUTF.[ch] by Unicode, Inc.
|
|
* Speed improvements by Martin Carlberg 2016
|
|
*
|
|
* Original copyright follows.
|
|
*/
|
|
|
|
/*
|
|
* Copyright 2001-2004 Unicode, Inc.
|
|
*
|
|
* Disclaimer
|
|
*
|
|
* This source code is provided as is by Unicode, Inc. No claims are
|
|
* made as to fitness for any particular purpose. No warranties of any
|
|
* kind are expressed or implied. The recipient agrees to determine
|
|
* applicability of information provided. If this file has been
|
|
* purchased on magnetic or optical media from Unicode, Inc., the
|
|
* sole remedy for any claim will be exchange of defective media
|
|
* within 90 days of receipt.
|
|
*
|
|
* Limitations on Rights to Redistribute This Code
|
|
*
|
|
* Unicode, Inc. hereby grants the right to freely use the information
|
|
* supplied in this file in the creation of products supporting the
|
|
* Unicode Standard, and to make copies of this file in any form
|
|
* for internal or external distribution as long as this notice
|
|
* remains attached.
|
|
*/
|
|
|
|
/* ---------------------------------------------------------------------
|
|
|
|
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
|
|
Author: Mark E. Davis, 1994.
|
|
Rev History: Rick McGowan, fixes & updates May 2001.
|
|
Sept 2001: fixed const & error conditions per
|
|
mods suggested by S. Parent & A. Lillich.
|
|
June 2002: Tim Dodd added detection and handling of incomplete
|
|
source sequences, enhanced error detection, added casts
|
|
to eliminate compiler warnings.
|
|
July 2003: slight mods to back out aggressive FFFE detection.
|
|
Jan 2004: updated switches in from-UTF8 conversions.
|
|
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
|
|
|
|
See the header file "ConvertUTF.h" for complete documentation.
|
|
|
|
------------------------------------------------------------------------ */
|
|
var SURROGATE_HIGH_START = 0xD800;
|
|
var SURROGATE_HIGH_END = 0xDBFF;
|
|
var SURROGATE_LOW_START = 0xDC00;
|
|
var SURROGATE_LOW_END = 0xDFFF;
|
|
var REPLACEMENT_CHAR = 0xFFFD;
|
|
var FIRSTBYTEMARK = [0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC];
|
|
|
|
function UTF16ToUTF8(source) {
|
|
var target = "";
|
|
var currentPos = 0;
|
|
for (var i = 0; i < source.length; i++) {
|
|
var c = source.charCodeAt(i);
|
|
if (c < 0x80) continue;
|
|
|
|
if (i > currentPos)
|
|
target += source.substring(currentPos, i);
|
|
|
|
if (c >= SURROGATE_HIGH_START && c <= SURROGATE_HIGH_END) {
|
|
i++;
|
|
if (i < source.length) {
|
|
var c2 = source.charCodeAt(i);
|
|
if (c2 >= SURROGATE_LOW_START && c2 <= SURROGATE_LOW_END) {
|
|
c = ((c - SURROGATE_HIGH_START) << 10) + (c2 - SURROGATE_LOW_START) + 0x10000;
|
|
} else {
|
|
// illegal second surrogate char
|
|
return null;
|
|
}
|
|
} else {
|
|
// missing second surrogate in pair
|
|
return null;
|
|
}
|
|
} else if (c >= SURROGATE_LOW_START && c <= SURROGATE_LOW_END) {
|
|
// stray surrogate
|
|
return null;
|
|
}
|
|
|
|
currentPos = i + 1;
|
|
enc = [];
|
|
|
|
var cc = c;
|
|
|
|
if (cc >= 0x110000) { cc = 0x800; c = REPLACEMENT_CHAR; }
|
|
if (cc >= 0x10000) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; }
|
|
if (cc >= 0x800) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; }
|
|
if (cc >= 0x80) { enc.unshift(String.fromCharCode((c | 0x80) & 0xBF)); c >>= 6; }
|
|
|
|
enc.unshift(String.fromCharCode( c | FIRSTBYTEMARK[enc.length]));
|
|
|
|
target += enc.join("");
|
|
}
|
|
|
|
if (currentPos === 0) return source;
|
|
|
|
if (i > currentPos)
|
|
target += source.substring(currentPos, i);
|
|
|
|
return target;
|
|
}
|
|
|
|
DISPLAY_NAME(UTF16ToUTF8);
|