/* appjet:version 0.1 */ /* appjet:library */ // Copyright (c) 2009, Herbert Vojčík // Licensed by MIT license (http://www.opensource.org/licenses/mit-license.php) /**@overview This library contains various utility functions * that appeared during developing other apps but are general enough. */ /** * Returns global object. */ function global() { return {}.valueOf.call(); } /** * Read-only wrapper on an object. */ function readonly(o) { return appjet._native.createProxyObject( function(name) { return o[name]; }, function() {}, function() {}, function() { return []; }, o ); } /** * Extends an object by copying all other object's own properties. * Returns the extended object. This implies you can create clone * of simple object by extend({}, x). */ function extend(dst, src) { eachProperty(src, function(p) { dst[p] = src[p]; }); return dst; } /** * Akin to Array >> streamContents: in Smalltalk. * Calls f and returns array of every item that was printed using print. * Useful for collecting any item in some loop(s) for which * filter or map can not be straightforwardly used. Both examples get keys * of own properties @example collectPrint(function() { eachProperty(object, print); }); collectPrint(function() {for (var p in object) { if (object.hasOwnProperty(p) print(p); }}); */ function collectPrint(f) { var result = []; var _g = global(); var savedPrint = _g.print; _g.print = function() { result.push.apply(result, arguments); }; // cannot inline, or f***ing try/finally bug which nobody is willing to fix appears try { f(); } finally { _g.print = savedPrint; } return result; } function _collector(result) { return function() { result.push.apply(result, arguments); }; } /** * Akin to Array >> streamContents: in Smalltalk. * Calls f and returns array of every item that was yielded using yield. * Useful for collecting any item in some loop(s) for which * filter or map can not be straightforwardly used. An example gets keys * of own properties @example collectYield(function () {for (var p in object) { if (object.hasOwnProperty(p) yield p; }}); */ /*function collectYield(f) { var result = []; var gen = f(); try { for(;;) { result.push(gen.next()); } } catch (ex) { if (ex instanceof StopIteration) { //gen.close(); //strange, not needed } else throw ex; } return result; }*/ //from web; then edited /** Converts h,s,v (0-1) to rgb (0-255). Returns array [r, g, b]. */ function hsv2rgb(h,s,v) { // Adapted from http://www.easyrgb.com/math.html // hsv values = 0 - 1, rgb values = 0 - 255 var r, g, b; if (s == 0) { r = g = b = v; } else { var vh = 6*h; var vi = Math.floor(vh); var v1 = v*(1-s); var v2 = v*(1-s*(vh-vi)); var v3 = v*(1-s*(1-(vh-vi))); switch(vi) { case 0: r = v; g = v3; b = v1; break; case 1: r = v2; g = v; b = v1; break; case 2: r = v1; g = v; b = v3; break; case 3: r = v1; g = v2; b = v; break; case 4: r = v3; g = v1; b = v; break; case 5: r = v; g = v1; b = v2; break; } } return [ Math.round(255*r), Math.round(255*g), Math.round(255*b) ]; } /** * Call this function with etag of actual requested resource. * Includes caching headers in the response, * and if the ETag matches, it immediately stops the response * with "304 Not Modified"; in other case it returns * and lets you continue with populating the response data. * Call after you KNOW the resource exists! It is illegal * to change response status to 404 or similar after this call. */ function etagCaching(etag) { response.setCacheable(null); response.setHeader("Cache-Control", "public, must-revalidate"); response.setHeader("ETag", etag); var cond = request.headers["If-None-Match"]; if (cond && (cond === "*" || ([].concat(cond).indexOf(etag) !== -1))) { response.setStatusCode(304); response.stop(); } } /** * Splits all elements of an array by delimiter * and insert o in places where split occurred. * Returns modified array with split parts and delimiting o's. */ function splitReplace(array, delimiter, o) { var i, j, k, l = 0, m = array.length; array.forEach(function (v, k, a) { var split = a[k] = v.split ? v.split(delimiter) : [v]; l += 2 * split.length - 1; }); array.length = l; for (var i = m-1, j = l-1; i >= 0; i--) { var parts = array[i]; for (var k = parts.length - 1; k > 0; --k) { array[j--] = parts[k--]; array[j--] = o; } array[j--] = parts[0]; } return array; } /* appjet:server */ import("lib-support/jsunit"); var _l = import({}, "lib-support/useful"); // ==== Test cases ==== page.testSuite = [ function collectPrintCollectsPrintedArguments() { var collected = _l.collectPrint(function() { print("Hello", "world", "!"); print(); print(3,1,4); }); assertArrayEquals("Incorrect items collected.", ["Hello", "world", "!", 3, 1, 4], collected); }, function collectPrintRetainsLegacyPrint() { var savedPrint = this.print; _l.collectPrint(function() { print("Hello", "world", "!"); print(); print(3,1,4); }); assertEquals("Print not retained.", savedPrint, this.print); }, function collectPrintRetainsLegacyPrintInCaseOfException() { var savedPrint = this.print; var f = function() { throw "exception"; }; try { _l.collectPrint(f); fail("Exception not thrown"); } catch(ex) { if (ex === "exception") { // pass } else throw ex; } assertEquals("Print not retained.", savedPrint, this.print); }, function collectPrintRetainsLegacyPrintInCaseOfUndefinedException() { var savedPrint = this.print; var f = function() { throw undefined; }; try { _l.collectPrint(f); fail("Exception not thrown"); } catch(ex) { if (ex === undefined) { // pass } else throw ex; } assertEquals("Print not retained.", savedPrint, this.print); }, /*function collectYieldCollectsYieldedArguments() { var collected = _l.collectYield(function() { yield "Hello"; yield 3; }); assertArrayEquals("Incorrect items collected.", ["Hello", 3], collected); }, function collectYieldCollectsYieldeesFromTryBlockAndFinallyRuns() { var frun = false; var collected = _l.collectYield(function() { try { yield "Hello"; yield 3; } finally { frun = true; } }); assertArrayEquals("Incorrect items collected.", ["Hello", 3], collected); assertTrue("Finally did not run", frun); }, function collectYieldThrowsExceptionIfThrownInF() { try { var collected = _l.collectYield(function() { yield "Hello"; throw "exception"; yield "world"; }); fail("Exception not thrown."); } catch(ex) { if (ex === "exception") { // pass } else throw ex; } }, function collectYieldThrowsUndefinedIfThrownInF() { try { var collected = _l.collectYield(function() { yield "Hello"; throw undefined; yield "world"; }); fail("Exception not thrown."); } catch(ex) { if (ex === undefined) { // pass } else throw ex; } }, function collectYieldThrowsExceptionIfExceptionThrownInF_AndFinallyInFRuns() { var finallyRun = false; try { var collected = _l.collectYield(function() { try { yield "Hello"; throw "exception"; yield "world"; } finally { finallyRun = true; } }); fail("Exception not thrown."); } catch(ex) { if (ex === "exception") { // pass } else throw ex; } assertTrue("Finally in f did not run.", finallyRun); },*/ function readonlyReadsCorrectlyAndIsDynamic() { var x = {hello: "world"}; var rx = _l.readonly(x); assertEquals("Hello read incorrectly", "world", rx.hello); assertUndefined("Nonexistent property is not undefined", rx.thisIsNot); x.hello = "again"; x.thisIsNot = "this is"; assertEquals("Hello read incorrectly", "again", rx.hello); assertEquals("ThisIsNot read incorrectly", "this is", rx.thisIsNot); }, function readonlyIsReadOnly() { var x = {hello: "world"}; var rx = _l.readonly(x); rx.thisIsNot = "this is"; delete rx.hello; assertEquals("Hello read incorrectly", "world", rx.hello); assertUndefined("Nonexistent property is not undefined", rx.thisIsNot); }, function extendExtendsCorrectlyForAllCombinations() { var srcProto = { pro: "to" }; var src = object(srcProto); src.a = "b"; src.c = "d"; var dst = { pro: "fi", a: "bc", yes: "true" }; var result = _l.extend(dst, src); assertEquals("extend did not return extended object", dst, result); assertEquals("a wasn't copied", "b", dst.a); assertEquals("c wasn't copied", "d", dst.c); assertEquals("pro was changed", "fi", dst.pro); assertEquals("yes was changed", "true", dst.yes); }, //TODO tests for splitReplace ]; // ==== Test runner ==== import("lib-support/runner"); runTestsByDefault();