941 lines
25 KiB
JavaScript
941 lines
25 KiB
JavaScript
if (! this['plt']) { this['plt'] = {}; }
|
|
|
|
// Helpers library: includes a bunch of helper functions that will be used
|
|
//
|
|
//
|
|
// FIXME: there's a circularity between this module and types, and that circularly
|
|
// should not be there!
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// File of helper functions for primitives and world.
|
|
|
|
|
|
(function(scope) {
|
|
var helpers = {};
|
|
scope.helpers = helpers;
|
|
|
|
|
|
// types refers to plt.types, and will be initialized later.
|
|
var types = scope['types'];
|
|
scope.link.ready('types',
|
|
function() {
|
|
types = scope['types'];
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var format = function(formatStr, args, functionName) {
|
|
var throwFormatError = function() {
|
|
functionName = functionName || '#<procedure>';
|
|
var matches = formatStr.match(new RegExp('~[sSaA]', 'g'));
|
|
var expectedNumberOfArgs = matches == null ? 0 : matches.length;
|
|
var errorStrBuffer = [functionName + ': format string requires ' + expectedNumberOfArgs
|
|
+ ' arguments, given ' + args.length + '; arguments were:',
|
|
toWrittenString(formatStr)];
|
|
for (var i = 0; i < args.length; i++) {
|
|
errorStrBuffer.push( toWrittenString(args[i]) );
|
|
}
|
|
|
|
raise( types.incompleteExn(types.exnFailContract, errorStrBuffer.join(' '), []) );
|
|
}
|
|
|
|
var pattern = new RegExp("~[sSaAneE%~]", "g");
|
|
var buffer = args.slice(0);;
|
|
function f(s) {
|
|
if (s == "~~") {
|
|
return "~";
|
|
} else if (s == '~n' || s == '~%') {
|
|
return "\n";
|
|
} else if (s == '~s' || s == "~S") {
|
|
if (buffer.length == 0) {
|
|
throwFormatError();
|
|
}
|
|
return toWrittenString(buffer.shift());
|
|
} else if (s == '~e' || s == "~E") {
|
|
// FIXME: we don't yet have support for the error-print
|
|
// handler, and currently treat ~e just like ~s.
|
|
if (buffer.length == 0) {
|
|
throwFormatError();
|
|
}
|
|
return toWrittenString(buffer.shift());
|
|
} else if (s == '~a' || s == "~A") {
|
|
if (buffer.length == 0) {
|
|
throwFormatError();
|
|
}
|
|
return toDisplayedString(buffer.shift());
|
|
} else {
|
|
throw types.internalError('format: string.replace matched invalid regexp', false);
|
|
}
|
|
}
|
|
var result = formatStr.replace(pattern, f);
|
|
if (buffer.length > 0) {
|
|
throwFormatError();
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
// forEachK: CPS( array CPS(array -> void) (error -> void) -> void )
|
|
// Iterates through an array and applies f to each element using CPS
|
|
// If an error is thrown, it catches the error and calls f_error on it
|
|
var forEachK = function(a, f, f_error, k) {
|
|
var forEachHelp = function(i) {
|
|
if( i >= a.length ) {
|
|
if (k) {
|
|
return k();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
return f(a[i], function() { return forEachHelp(i+1); });
|
|
} catch (e) {
|
|
f_error(e);
|
|
}
|
|
};
|
|
return forEachHelp(0);
|
|
};
|
|
|
|
|
|
// reportError: (or exception string) -> void
|
|
// Reports an error to the user, either at the console
|
|
// if the console exists, or as alerts otherwise.
|
|
var reportError = function(e) {
|
|
var reporter;
|
|
if (typeof(console) != 'undefined' &&
|
|
typeof(console.log) != 'undefined') {
|
|
reporter = (function(x) { console.log(x); });
|
|
} else {
|
|
reporter = (function(x) { alert(x); });
|
|
}
|
|
if (typeof e == 'string') {
|
|
reporter(e);
|
|
} else if ( types.isSchemeError(e) ) {
|
|
if ( types.isExn(e.val) ) {
|
|
reporter( types.exnMessage(e.val) );
|
|
}
|
|
else {
|
|
reporter(e.val);
|
|
}
|
|
} else if ( types.isInternalError(e) ) {
|
|
reporter(e.val);
|
|
} else if (e.message) {
|
|
reporter(e.message);
|
|
} else {
|
|
reporter(e.toString());
|
|
}
|
|
// if (plt.Kernel.lastLoc) {
|
|
// var loc = plt.Kernel.lastLoc;
|
|
// if (typeof(loc) === 'string') {
|
|
// reporter("Error was raised around " + loc);
|
|
// } else if (typeof(loc) !== 'undefined' &&
|
|
// typeof(loc.line) !== 'undefined') {
|
|
// reporter("Error was raised around: "
|
|
// + plt.Kernel.locToString(loc));
|
|
// }
|
|
// }
|
|
};
|
|
|
|
|
|
var raise = function(v) {
|
|
throw types.schemeError(v);
|
|
};
|
|
|
|
|
|
var procArityContains = function(n) {
|
|
return function(proc) {
|
|
var singleCase = function(aCase) {
|
|
if ( aCase instanceof types.ContinuationClosureValue ) {
|
|
return true;
|
|
}
|
|
return (aCase.numParams == n ||
|
|
(aCase.isRest && aCase.numParams <= n));
|
|
};
|
|
|
|
var cases = [];
|
|
if ( proc instanceof types.ContinuationClosureValue ||
|
|
proc instanceof types.ClosureValue ||
|
|
proc instanceof types.PrimProc ) {
|
|
return singleCase(proc);
|
|
}
|
|
else if (proc instanceof types.CasePrimitive) {
|
|
cases = proc.cases;
|
|
}
|
|
else if (proc instanceof types.CaseLambdaValue) {
|
|
cases = proc.closures;
|
|
}
|
|
|
|
for (var i = 0; i < cases.length; i++) {
|
|
if ( singleCase(cases[i]) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
var throwCheckError = function(details, pos, args) {
|
|
var errorFormatStr;
|
|
if (args && args.length > 1) {
|
|
var errorFormatStrBuffer = ['~a: expects type <~a> as ~a arguments, given: ~s; other arguments were:'];
|
|
for (var i = 0; i < args.length; i++) {
|
|
if ( i != pos-1 ) {
|
|
errorFormatStrBuffer.push(toWrittenString(args[i]));
|
|
}
|
|
}
|
|
errorFormatStr = errorFormatStrBuffer.join(' ');
|
|
}
|
|
else {
|
|
errorFormatStr = "~a: expects argument of type <~a>, given: ~s";
|
|
details.splice(2, 1);
|
|
}
|
|
|
|
raise( types.incompleteExn(types.exnFailContract,
|
|
helpers.format(errorFormatStr, details),
|
|
[]) );
|
|
};
|
|
|
|
var check = function(x, f, functionName, typeName, position, args) {
|
|
if ( !f(x) ) {
|
|
throwCheckError([functionName,
|
|
typeName,
|
|
helpers.ordinalize(position),
|
|
x],
|
|
position,
|
|
args);
|
|
}
|
|
};
|
|
|
|
var isList = function(x) {
|
|
var seenPairs = makeLowLevelEqHash();
|
|
while (true) {
|
|
if (seenPairs.containsKey(x)) {
|
|
return true;
|
|
} else if (x === types.EMPTY) {
|
|
return true;
|
|
} else if (types.isPair(x)) {
|
|
seenPairs.put(x, true);
|
|
x = x.rest();
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
var isListOf = function(x, f) {
|
|
var seenPairs = makeLowLevelEqHash();
|
|
while (true) {
|
|
if (seenPairs.containsKey(x)) {
|
|
return true;
|
|
} else if (x === types.EMPTY) {
|
|
return true;
|
|
} else if (types.isPair(x)) {
|
|
seenPairs.put(x, true);
|
|
if (f(x.first())) {
|
|
x = x.rest();
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
var checkListOf = function(lst, f, functionName, typeName, position, args) {
|
|
if ( !isListOf(lst, f) ) {
|
|
helpers.throwCheckError([functionName,
|
|
'list of ' + typeName,
|
|
helpers.ordinalize(position),
|
|
lst],
|
|
position,
|
|
args);
|
|
}
|
|
};
|
|
|
|
|
|
// // remove: array any -> array
|
|
// // removes the first instance of v in a
|
|
// // or returns a copy of a if v does not exist
|
|
// var remove = function(a, v) {
|
|
// for (var i = 0; i < a.length; i++) {
|
|
// if (a[i] === v) {
|
|
// return a.slice(0, i).concat( a.slice(i+1, a.length) );
|
|
// }
|
|
// }
|
|
// return a.slice(0);
|
|
// };
|
|
|
|
// map: array (any -> any) -> array
|
|
// applies f to each element of a and returns the result
|
|
// as a new array
|
|
var map = function(f, a) {
|
|
var b = new Array(a.length);
|
|
for (var i = 0; i < a.length; i++) {
|
|
b[i] = f(a[i]);
|
|
}
|
|
return b;
|
|
};
|
|
|
|
|
|
var concatMap = function(f, a) {
|
|
var b = [];
|
|
for (var i = 0; i < a.length; i++) {
|
|
b = b.concat( f(a[i]) );
|
|
}
|
|
return b;
|
|
};
|
|
|
|
|
|
var schemeListToArray = function(lst) {
|
|
var result = [];
|
|
while ( !lst.isEmpty() ) {
|
|
result.push(lst.first());
|
|
lst = lst.rest();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// deepListToArray: any -> any
|
|
// Converts list structure to array structure.
|
|
var deepListToArray = function(x) {
|
|
var thing = x;
|
|
if (thing === types.EMPTY) {
|
|
return [];
|
|
} else if (types.isPair(thing)) {
|
|
var result = [];
|
|
while (!thing.isEmpty()) {
|
|
result.push(deepListToArray(thing.first()));
|
|
thing = thing.rest();
|
|
}
|
|
return result;
|
|
} else {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
|
|
var flattenSchemeListToArray = function(x) {
|
|
if ( !isList(x) ) {
|
|
return [x];
|
|
}
|
|
|
|
var ret = [];
|
|
while ( !x.isEmpty() ) {
|
|
ret = ret.concat( flattenSchemeListToArray(x.first()) );
|
|
x = x.rest();
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
|
|
// assocListToHash: (listof (list X Y)) -> (hashof X Y)
|
|
var assocListToHash = function(lst) {
|
|
var result = {};
|
|
while ( !lst.isEmpty() ) {
|
|
var key = lst.first().first();
|
|
var val = lst.first().rest().first();
|
|
result[key] = val;
|
|
lst = lst.rest();
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
var ordinalize = function(n) {
|
|
// special case for 11th:
|
|
if ( n % 100 == 11 ) {
|
|
return n + 'th';
|
|
}
|
|
var res = n;
|
|
switch( n % 10 ) {
|
|
case 1: res += 'st'; break;
|
|
case 2: res += 'nd'; break;
|
|
case 3: res += 'rd'; break;
|
|
default: res += 'th'; break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
var wrapJsValue = function(x) {
|
|
if (x === undefined) {
|
|
return types.jsValue('undefined', x);
|
|
}
|
|
else if (x === null) {
|
|
return types.jsValue('null', x);
|
|
}
|
|
else if (typeof(x) == 'function') {
|
|
return types.jsValue('function', x);
|
|
}
|
|
else if ( x instanceof Array ) {
|
|
return types.jsValue('array', x);
|
|
}
|
|
else if ( typeof(x) == 'string' ) {
|
|
return types.jsValue("'" + x.toString() + "'", x);
|
|
}
|
|
else {
|
|
return types.jsValue(x.toString(), x);
|
|
}
|
|
};
|
|
|
|
|
|
var getKeyCodeName = function(e) {
|
|
var code = e.charCode || e.keyCode;
|
|
var keyname;
|
|
switch(code) {
|
|
case 16: keyname = "shift"; break;
|
|
case 17: keyname = "control"; break;
|
|
case 19: keyname = "pause"; break;
|
|
case 27: keyname = "escape"; break;
|
|
case 33: keyname = "prior"; break;
|
|
case 34: keyname = "next"; break;
|
|
case 35: keyname = "end"; break;
|
|
case 36: keyname = "home"; break;
|
|
case 37: keyname = "left"; break;
|
|
case 38: keyname = "up"; break;
|
|
case 39: keyname = "right"; break;
|
|
case 40: keyname = "down"; break;
|
|
case 42: keyname = "print"; break;
|
|
case 45: keyname = "insert"; break;
|
|
case 46: keyname = String.fromCharCode(127); break;
|
|
case 106: keyname = "*"; break;
|
|
case 107: keyname = "+"; break;
|
|
case 109: keyname = "-"; break;
|
|
case 110: keyname = "."; break;
|
|
case 111: keyname = "/"; break;
|
|
case 144: keyname = "numlock"; break;
|
|
case 145: keyname = "scroll"; break;
|
|
case 186: keyname = ";"; break;
|
|
case 187: keyname = "="; break;
|
|
case 188: keyname = ","; break;
|
|
case 189: keyname = "-"; break;
|
|
case 190: keyname = "."; break;
|
|
case 191: keyname = "/"; break;
|
|
case 192: keyname = "`"; break;
|
|
case 219: keyname = "["; break;
|
|
case 220: keyname = "\\"; break;
|
|
case 221: keyname = "]"; break;
|
|
case 222: keyname = "'"; break;
|
|
default: if (code >= 96 && code <= 105) {
|
|
keyname = (code - 96).toString();
|
|
}
|
|
else if (code >= 112 && code <= 123) {
|
|
keyname = "f" + (code - 111);
|
|
}
|
|
else {
|
|
keyname = String.fromCharCode(code).toLowerCase();
|
|
}
|
|
break;
|
|
}
|
|
return keyname;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// maybeCallAfterAttach: dom-node -> void
|
|
// walk the tree rooted at aNode, and call afterAttach if the element has
|
|
// such a method.
|
|
var maybeCallAfterAttach = function(aNode) {
|
|
var stack = [aNode];
|
|
while (stack.length !== 0) {
|
|
var nextNode = stack.pop();
|
|
if (nextNode.afterAttach) {
|
|
nextNode.afterAttach(nextNode);
|
|
}
|
|
if (nextNode.hasChildNodes && nextNode.hasChildNodes()) {
|
|
var children = nextNode.childNodes;
|
|
for (var i = 0; i < children.length; i++) {
|
|
stack.push(children[i]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// makeLocationDom: location -> dom
|
|
// Dom type that has special support in the editor through the print hook.
|
|
// The print hook is expected to look at the printing of dom values with
|
|
// this particular structure. In the context of WeScheme, the environment
|
|
// will rewrite these to be clickable links.
|
|
var makeLocationDom = function(aLocation) {
|
|
var locationSpan = document.createElement("span");
|
|
var idSpan = document.createElement("span");
|
|
var offsetSpan = document.createElement("span");
|
|
var lineSpan = document.createElement("span");
|
|
var columnSpan = document.createElement("span");
|
|
var spanSpan = document.createElement("span");
|
|
|
|
locationSpan['className'] = 'location-reference';
|
|
idSpan['className'] = 'location-id';
|
|
offsetSpan['className'] = 'location-offset';
|
|
lineSpan['className'] = 'location-line';
|
|
columnSpan['className'] = 'location-column';
|
|
spanSpan['className'] = 'location-span';
|
|
|
|
idSpan.appendChild(document.createTextNode(String(aLocation.id)));
|
|
offsetSpan.appendChild(document.createTextNode(String(aLocation.offset)));
|
|
lineSpan.appendChild(document.createTextNode(String(aLocation.line)));
|
|
columnSpan.appendChild(document.createTextNode(String(aLocation.column)));
|
|
spanSpan.appendChild(document.createTextNode(String(aLocation.span)));
|
|
|
|
locationSpan.appendChild(idSpan);
|
|
locationSpan.appendChild(offsetSpan);
|
|
locationSpan.appendChild(lineSpan);
|
|
locationSpan.appendChild(columnSpan);
|
|
locationSpan.appendChild(spanSpan);
|
|
|
|
return locationSpan;
|
|
};
|
|
|
|
|
|
var isLocationDom = function(thing) {
|
|
return (thing
|
|
&&
|
|
(thing.nodeType === Node.TEXT_NODE ||
|
|
thing.nodeType === Node.ELEMENT_NODE)
|
|
&&
|
|
thing['className'] === 'location-reference');
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _eqHashCodeCounter = 0;
|
|
makeEqHashCode = function() {
|
|
_eqHashCodeCounter++;
|
|
return _eqHashCodeCounter;
|
|
};
|
|
|
|
|
|
// getHashCode: any -> (or fixnum string)
|
|
// Produces a hashcode appropriate for eq.
|
|
getEqHashCode = function(x) {
|
|
if (typeof(x) === 'string') {
|
|
return x;
|
|
}
|
|
if (typeof(x) === 'number') {
|
|
return String(x);
|
|
}
|
|
if (x && !x._eqHashCode) {
|
|
x._eqHashCode = makeEqHashCode();
|
|
}
|
|
if (x && x._eqHashCode) {
|
|
return x._eqHashCode;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
|
|
|
|
|
|
var makeLowLevelEqHash = function() {
|
|
return new Hashtable(function(x) { return getEqHashCode(x); },
|
|
function(x, y) { return x === y; });
|
|
};
|
|
|
|
|
|
|
|
// Inheritance.
|
|
var heir = function(parentPrototype) {
|
|
var f = function() {}
|
|
f.prototype = parentPrototype;
|
|
return new f();
|
|
};
|
|
|
|
|
|
|
|
|
|
// toWrittenString: Any Hashtable -> String
|
|
var toWrittenString = function(x, cache) {
|
|
if (! cache) {
|
|
cache = makeLowLevelEqHash();
|
|
}
|
|
|
|
if (typeof(x) === 'object') {
|
|
if (cache.containsKey(x)) {
|
|
return "...";
|
|
}
|
|
}
|
|
|
|
if (x == undefined || x == null) {
|
|
return "#<undefined>";
|
|
}
|
|
if (typeof(x) == 'string') {
|
|
return escapeString(x.toString());
|
|
}
|
|
if (typeof(x) != 'object' && typeof(x) != 'function') {
|
|
return x.toString();
|
|
}
|
|
|
|
var returnVal;
|
|
if (typeof(x.toWrittenString) !== 'undefined') {
|
|
returnVal = x.toWrittenString(cache);
|
|
} else if (typeof(x.toDisplayedString) !== 'undefined') {
|
|
returnVal = x.toDisplayedString(cache);
|
|
} else {
|
|
returnVal = x.toString();
|
|
}
|
|
cache.remove(x);
|
|
return returnVal;
|
|
};
|
|
|
|
|
|
|
|
// toDisplayedString: Any Hashtable -> String
|
|
var toDisplayedString = function(x, cache) {
|
|
if (! cache) {
|
|
cache = makeLowLevelEqHash();
|
|
}
|
|
if (typeof(x) === 'object') {
|
|
if (cache.containsKey(x)) {
|
|
return "...";
|
|
}
|
|
}
|
|
|
|
if (x == undefined || x == null) {
|
|
return "#<undefined>";
|
|
}
|
|
if (typeof(x) == 'string') {
|
|
return x;
|
|
}
|
|
if (typeof(x) != 'object' && typeof(x) != 'function') {
|
|
return x.toString();
|
|
}
|
|
|
|
var returnVal;
|
|
if (typeof(x.toDisplayedString) !== 'undefined') {
|
|
returnVal = x.toDisplayedString(cache);
|
|
} else if (typeof(x.toWrittenString) !== 'undefined') {
|
|
returnVal = x.toWrittenString(cache);
|
|
} else {
|
|
returnVal = x.toString();
|
|
}
|
|
cache.remove(x);
|
|
return returnVal;
|
|
};
|
|
|
|
|
|
|
|
|
|
var ToDomNodeParameters = function(params) {
|
|
if (! params) { params = {}; }
|
|
this.cache = makeLowLevelEqHash();
|
|
for (var k in params) {
|
|
if (params.hasOwnProperty(k)) {
|
|
this[k] = params[k];
|
|
}
|
|
}
|
|
this.objectCounter = 0;
|
|
};
|
|
|
|
// getMode: -> (U "print" "display" "write")
|
|
ToDomNodeParameters.prototype.getMode = function() {
|
|
if (this.mode) {
|
|
return this.mode;
|
|
}
|
|
return 'print';
|
|
};
|
|
|
|
ToDomNodeParameters.prototype.containsKey = function(x) {
|
|
return this.cache.containsKey(x);
|
|
};
|
|
|
|
ToDomNodeParameters.prototype.get = function(x) {
|
|
return this.cache.get(x);
|
|
};
|
|
|
|
ToDomNodeParameters.prototype.remove = function(x) {
|
|
return this.cache.remove(x);
|
|
};
|
|
|
|
ToDomNodeParameters.prototype.put = function(x) {
|
|
this.objectCounter++;
|
|
return this.cache.put(x, this.objectCounter);
|
|
};
|
|
|
|
|
|
// toDomNode: scheme-value -> dom-node
|
|
var toDomNode = function(x, params) {
|
|
if (params === 'write') {
|
|
params = new ToDomNodeParameters({'mode' : 'write'});
|
|
} else if (params === 'print') {
|
|
params = new ToDomNodeParameters({'mode' : 'print'});
|
|
} else if (params === 'display') {
|
|
params = new ToDomNodeParameters({'mode' : 'display'});
|
|
} else {
|
|
params = params || new DomNodeParameters({'mode' : 'display'});
|
|
}
|
|
|
|
if (jsnums.isSchemeNumber(x)) {
|
|
return numberToDomNode(x, params);
|
|
}
|
|
|
|
if (typeof(x) == 'object') {
|
|
if (params.containsKey(x)) {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode("#" + params.get(x)));
|
|
return node;
|
|
}
|
|
}
|
|
if (x == undefined || x == null) {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode("#<undefined>"));
|
|
return node;
|
|
}
|
|
|
|
if (typeof(x) == 'string') {
|
|
var wrapper = document.createElement("span");
|
|
wrapper.style["white-space"] = "pre";
|
|
var node;
|
|
if (params.getMode() === 'write' || params.getMode() === 'print') {
|
|
node = document.createTextNode(toWrittenString(x));
|
|
} else {
|
|
node = document.createTextNode(toDisplayedString(x));
|
|
}
|
|
wrapper.appendChild(node);
|
|
return wrapper;
|
|
}
|
|
|
|
if (typeof(x) != 'object' && typeof(x) != 'function') {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(x.toString()));
|
|
return node;
|
|
}
|
|
|
|
var returnVal;
|
|
if (x.nodeType) {
|
|
returnVal = x;
|
|
} else if (typeof(x.toDomNode) !== 'undefined') {
|
|
returnVal = x.toDomNode(params);
|
|
} else if (params.getMode() === 'write' &&
|
|
typeof(x.toWrittenString) !== 'undefined') {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(x.toWrittenString(params)));
|
|
returnVal = node;
|
|
} else if (params.getMode() === 'display' &&
|
|
typeof(x.toDisplayedString) !== 'undefined') {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(x.toDisplayedString(params)));
|
|
returnVal = node;
|
|
} else {
|
|
var node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(x.toString()));
|
|
returnVal = node;
|
|
}
|
|
params.remove(x);
|
|
return returnVal;
|
|
};
|
|
|
|
|
|
|
|
// numberToDomNode: jsnum -> dom
|
|
// Given a jsnum, produces a dom-node representation.
|
|
var numberToDomNode = function(n, params) {
|
|
var node;
|
|
if (jsnums.isExact(n)) {
|
|
if (jsnums.isInteger(n)) {
|
|
node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(n.toString()));
|
|
return node;
|
|
} else if (jsnums.isRational(n)) {
|
|
return rationalToDomNode(n);
|
|
} else if (jsnums.isComplex(n)) {
|
|
node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(n.toString()));
|
|
return node;
|
|
} else {
|
|
node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(n.toString()));
|
|
return node;
|
|
}
|
|
} else {
|
|
node = document.createElement("span");
|
|
node.appendChild(document.createTextNode(n.toString()));
|
|
return node;
|
|
}
|
|
};
|
|
|
|
// rationalToDomNode: rational -> dom-node
|
|
var rationalToDomNode = function(n) {
|
|
var repeatingDecimalNode = document.createElement("span");
|
|
var chunks = jsnums.toRepeatingDecimal(jsnums.numerator(n),
|
|
jsnums.denominator(n),
|
|
{limit: 25});
|
|
repeatingDecimalNode.appendChild(document.createTextNode(chunks[0] + '.'))
|
|
repeatingDecimalNode.appendChild(document.createTextNode(chunks[1]));
|
|
if (chunks[2] === '...') {
|
|
repeatingDecimalNode.appendChild(
|
|
document.createTextNode(chunks[2]));
|
|
} else if (chunks[2] !== '0') {
|
|
var overlineSpan = document.createElement("span");
|
|
overlineSpan.style.textDecoration = 'overline';
|
|
overlineSpan.appendChild(document.createTextNode(chunks[2]));
|
|
repeatingDecimalNode.appendChild(overlineSpan);
|
|
}
|
|
|
|
|
|
var fractionalNode = document.createElement("span");
|
|
var numeratorNode = document.createElement("sup");
|
|
numeratorNode.appendChild(document.createTextNode(String(jsnums.numerator(n))));
|
|
var denominatorNode = document.createElement("sub");
|
|
denominatorNode.appendChild(document.createTextNode(String(jsnums.denominator(n))));
|
|
fractionalNode.appendChild(numeratorNode);
|
|
fractionalNode.appendChild(document.createTextNode("/"));
|
|
fractionalNode.appendChild(denominatorNode);
|
|
|
|
|
|
var numberNode = document.createElement("span");
|
|
numberNode.appendChild(repeatingDecimalNode);
|
|
numberNode.appendChild(fractionalNode);
|
|
fractionalNode.style['display'] = 'none';
|
|
|
|
var showingRepeating = true;
|
|
|
|
numberNode.onclick = function(e) {
|
|
showingRepeating = !showingRepeating;
|
|
repeatingDecimalNode.style['display'] =
|
|
(showingRepeating ? 'inline' : 'none')
|
|
fractionalNode.style['display'] =
|
|
(!showingRepeating ? 'inline' : 'none')
|
|
};
|
|
numberNode.style['cursor'] = 'pointer';
|
|
return numberNode;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var escapeString = function(s) {
|
|
return '"' + replaceUnprintableStringChars(s) + '"';
|
|
};
|
|
|
|
var replaceUnprintableStringChars = function(s) {
|
|
var ret = [];
|
|
for (var i = 0; i < s.length; i++) {
|
|
var val = s.charCodeAt(i);
|
|
switch(val) {
|
|
case 7: ret.push('\\a'); break;
|
|
case 8: ret.push('\\b'); break;
|
|
case 9: ret.push('\\t'); break;
|
|
case 10: ret.push('\\n'); break;
|
|
case 11: ret.push('\\v'); break;
|
|
case 12: ret.push('\\f'); break;
|
|
case 13: ret.push('\\r'); break;
|
|
case 34: ret.push('\\"'); break;
|
|
case 92: ret.push('\\\\'); break;
|
|
default: if (val >= 32 && val <= 126) {
|
|
ret.push( s.charAt(i) );
|
|
}
|
|
else {
|
|
var numStr = val.toString(16).toUpperCase();
|
|
while (numStr.length < 4) {
|
|
numStr = '0' + numStr;
|
|
}
|
|
ret.push('\\u' + numStr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return ret.join('');
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// clone: object -> object
|
|
// Copies an object. The new object should respond like the old
|
|
// object, including to things like instanceof
|
|
var clone = function(obj) {
|
|
var C = function() {}
|
|
C.prototype = obj;
|
|
var c = new C();
|
|
for (property in obj) {
|
|
if (obj.hasOwnProperty(property)) {
|
|
c[property] = obj[property];
|
|
}
|
|
}
|
|
return c;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////
|
|
|
|
helpers.format = format;
|
|
helpers.forEachK = forEachK;
|
|
helpers.reportError = reportError;
|
|
helpers.raise = raise;
|
|
|
|
helpers.procArityContains = procArityContains;
|
|
helpers.throwCheckError = throwCheckError;
|
|
helpers.isList = isList;
|
|
helpers.isListOf = isListOf;
|
|
helpers.check = check;
|
|
helpers.checkListOf = checkListOf;
|
|
|
|
// helpers.remove = remove;
|
|
helpers.map = map;
|
|
helpers.concatMap = concatMap;
|
|
helpers.schemeListToArray = schemeListToArray;
|
|
helpers.deepListToArray = deepListToArray;
|
|
helpers.flattenSchemeListToArray = flattenSchemeListToArray;
|
|
helpers.assocListToHash = assocListToHash;
|
|
|
|
helpers.ordinalize = ordinalize;
|
|
helpers.wrapJsValue = wrapJsValue;
|
|
|
|
helpers.getKeyCodeName = getKeyCodeName;
|
|
|
|
helpers.maybeCallAfterAttach = maybeCallAfterAttach;
|
|
|
|
helpers.makeLocationDom = makeLocationDom;
|
|
helpers.isLocationDom = isLocationDom;
|
|
|
|
|
|
helpers.getEqHashCode = getEqHashCode;
|
|
helpers.makeLowLevelEqHash = makeLowLevelEqHash;
|
|
|
|
helpers.heir = heir;
|
|
helpers.escapeString = escapeString;
|
|
helpers.toWrittenString = toWrittenString;
|
|
helpers.toDisplayedString = toDisplayedString;
|
|
|
|
|
|
helpers.toDomNode = toDomNode;
|
|
helpers.ToDomNodeParameters = ToDomNodeParameters;
|
|
|
|
helpers.clone = clone;
|
|
|
|
|
|
scope.link.announceReady('helpers');
|
|
})(this['plt']);
|
|
|
|
/////////////////////////////////////////////////////////////////
|