useful.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /* appjet:version 0.1 */
  2. /* appjet:library */
  3. // Copyright (c) 2009, Herbert Vojčík
  4. // Licensed by MIT license (http://www.opensource.org/licenses/mit-license.php)
  5. /**@overview This library contains various utility functions
  6. * that appeared during developing other apps but are general enough.
  7. */
  8. /**
  9. * Returns global object.
  10. */
  11. function global() { return {}.valueOf.call(); }
  12. /**
  13. * Read-only wrapper on an object.
  14. */
  15. function readonly(o) {
  16. return appjet._native.createProxyObject(
  17. function(name) { return o[name]; },
  18. function() {},
  19. function() {},
  20. function() { return []; },
  21. o
  22. );
  23. }
  24. /**
  25. * Extends an object by copying all other object's own properties.
  26. * Returns the extended object. This implies you can create clone
  27. * of simple object by extend({}, x).
  28. */
  29. function extend(dst, src) {
  30. eachProperty(src, function(p) { dst[p] = src[p]; });
  31. return dst;
  32. }
  33. /**
  34. * Akin to <code>Array >> streamContents:</code> in Smalltalk.
  35. * Calls f and returns array of every item that was printed using print.
  36. * Useful for collecting any item in some loop(s) for which
  37. * filter or map can not be straightforwardly used. Both examples get keys
  38. * of own properties
  39. @example
  40. collectPrint(function() { eachProperty(object, print); });
  41. collectPrint(function() {for (var p in object) { if (object.hasOwnProperty(p) print(p); }});
  42. */
  43. function collectPrint(f) {
  44. var result = [];
  45. var _g = global();
  46. var savedPrint = _g.print;
  47. _g.print = function() { result.push.apply(result, arguments); }; // cannot inline, or f***ing try/finally bug which nobody is willing to fix appears
  48. try { f(); } finally { _g.print = savedPrint; }
  49. return result;
  50. }
  51. function _collector(result) { return function() { result.push.apply(result, arguments); }; }
  52. /**
  53. * Akin to <code>Array >> streamContents:</code> in Smalltalk.
  54. * Calls f and returns array of every item that was yielded using yield.
  55. * Useful for collecting any item in some loop(s) for which
  56. * filter or map can not be straightforwardly used. An example gets keys
  57. * of own properties
  58. @example
  59. collectYield(function () {for (var p in object) { if (object.hasOwnProperty(p) yield p; }});
  60. */
  61. /*function collectYield(f) {
  62. var result = [];
  63. var gen = f();
  64. try {
  65. for(;;) { result.push(gen.next()); }
  66. } catch (ex) {
  67. if (ex instanceof StopIteration) {
  68. //gen.close(); //strange, not needed
  69. } else throw ex;
  70. }
  71. return result;
  72. }*/
  73. //from web; then edited
  74. /** Converts h,s,v (0-1) to rgb (0-255). Returns array [r, g, b]. */
  75. function hsv2rgb(h,s,v) {
  76. // Adapted from http://www.easyrgb.com/math.html
  77. // hsv values = 0 - 1, rgb values = 0 - 255
  78. var r, g, b;
  79. if (s == 0) {
  80. r = g = b = v;
  81. } else {
  82. var vh = 6*h;
  83. var vi = Math.floor(vh);
  84. var v1 = v*(1-s);
  85. var v2 = v*(1-s*(vh-vi));
  86. var v3 = v*(1-s*(1-(vh-vi)));
  87. switch(vi) {
  88. case 0: r = v; g = v3; b = v1; break;
  89. case 1: r = v2; g = v; b = v1; break;
  90. case 2: r = v1; g = v; b = v3; break;
  91. case 3: r = v1; g = v2; b = v; break;
  92. case 4: r = v3; g = v1; b = v; break;
  93. case 5: r = v; g = v1; b = v2; break;
  94. }
  95. }
  96. return [ Math.round(255*r), Math.round(255*g), Math.round(255*b) ];
  97. }
  98. /**
  99. * Call this function with etag of actual requested resource.
  100. * Includes caching headers in the response,
  101. * and if the ETag matches, it immediately stops the response
  102. * with "304 Not Modified"; in other case it returns
  103. * and lets you continue with populating the response data.
  104. * Call after you KNOW the resource exists! It is illegal
  105. * to change response status to 404 or similar after this call.
  106. */
  107. function etagCaching(etag) {
  108. response.setCacheable(null);
  109. response.setHeader("Cache-Control", "public, must-revalidate");
  110. response.setHeader("ETag", etag);
  111. var cond = request.headers["If-None-Match"];
  112. if (cond && (cond === "*" || ([].concat(cond).indexOf(etag) !== -1))) {
  113. response.setStatusCode(304);
  114. response.stop();
  115. }
  116. }
  117. /**
  118. * Splits all elements of an array by delimiter
  119. * and insert o in places where split occurred.
  120. * Returns modified array with split parts and delimiting o's.
  121. */
  122. function splitReplace(array, delimiter, o) {
  123. var i, j, k, l = 0, m = array.length;
  124. array.forEach(function (v, k, a) {
  125. var split = a[k] = v.split ? v.split(delimiter) : [v];
  126. l += 2 * split.length - 1;
  127. });
  128. array.length = l;
  129. for (var i = m-1, j = l-1; i >= 0; i--) {
  130. var parts = array[i];
  131. for (var k = parts.length - 1; k > 0; --k) {
  132. array[j--] = parts[k--];
  133. array[j--] = o;
  134. }
  135. array[j--] = parts[0];
  136. }
  137. return array;
  138. }
  139. /* appjet:server */
  140. import("lib-support/jsunit");
  141. var _l = import({}, "lib-support/useful");
  142. // ==== Test cases ====
  143. page.testSuite = [
  144. function collectPrintCollectsPrintedArguments() {
  145. var collected = _l.collectPrint(function() {
  146. print("Hello", "world", "!");
  147. print();
  148. print(3,1,4);
  149. });
  150. assertArrayEquals("Incorrect items collected.", ["Hello", "world", "!", 3, 1, 4], collected);
  151. },
  152. function collectPrintRetainsLegacyPrint() {
  153. var savedPrint = this.print;
  154. _l.collectPrint(function() {
  155. print("Hello", "world", "!");
  156. print();
  157. print(3,1,4);
  158. });
  159. assertEquals("Print not retained.", savedPrint, this.print);
  160. },
  161. function collectPrintRetainsLegacyPrintInCaseOfException() {
  162. var savedPrint = this.print;
  163. var f = function() { throw "exception"; };
  164. try {
  165. _l.collectPrint(f);
  166. fail("Exception not thrown");
  167. } catch(ex) {
  168. if (ex === "exception") {
  169. // pass
  170. } else throw ex;
  171. }
  172. assertEquals("Print not retained.", savedPrint, this.print);
  173. },
  174. function collectPrintRetainsLegacyPrintInCaseOfUndefinedException() {
  175. var savedPrint = this.print;
  176. var f = function() { throw undefined; };
  177. try {
  178. _l.collectPrint(f);
  179. fail("Exception not thrown");
  180. } catch(ex) {
  181. if (ex === undefined) {
  182. // pass
  183. } else throw ex;
  184. }
  185. assertEquals("Print not retained.", savedPrint, this.print);
  186. },
  187. /*function collectYieldCollectsYieldedArguments() {
  188. var collected = _l.collectYield(function() {
  189. yield "Hello";
  190. yield 3;
  191. });
  192. assertArrayEquals("Incorrect items collected.", ["Hello", 3], collected);
  193. },
  194. function collectYieldCollectsYieldeesFromTryBlockAndFinallyRuns() {
  195. var frun = false;
  196. var collected = _l.collectYield(function() {
  197. try {
  198. yield "Hello";
  199. yield 3;
  200. } finally { frun = true; }
  201. });
  202. assertArrayEquals("Incorrect items collected.", ["Hello", 3], collected);
  203. assertTrue("Finally did not run", frun);
  204. },
  205. function collectYieldThrowsExceptionIfThrownInF() {
  206. try {
  207. var collected = _l.collectYield(function() {
  208. yield "Hello";
  209. throw "exception";
  210. yield "world";
  211. });
  212. fail("Exception not thrown.");
  213. } catch(ex) {
  214. if (ex === "exception") {
  215. // pass
  216. } else throw ex;
  217. }
  218. },
  219. function collectYieldThrowsUndefinedIfThrownInF() {
  220. try {
  221. var collected = _l.collectYield(function() {
  222. yield "Hello";
  223. throw undefined;
  224. yield "world";
  225. });
  226. fail("Exception not thrown.");
  227. } catch(ex) {
  228. if (ex === undefined) {
  229. // pass
  230. } else throw ex;
  231. }
  232. },
  233. function collectYieldThrowsExceptionIfExceptionThrownInF_AndFinallyInFRuns() {
  234. var finallyRun = false;
  235. try {
  236. var collected = _l.collectYield(function() {
  237. try {
  238. yield "Hello";
  239. throw "exception";
  240. yield "world";
  241. } finally { finallyRun = true; }
  242. });
  243. fail("Exception not thrown.");
  244. } catch(ex) {
  245. if (ex === "exception") {
  246. // pass
  247. } else throw ex;
  248. }
  249. assertTrue("Finally in f did not run.", finallyRun);
  250. },*/
  251. function readonlyReadsCorrectlyAndIsDynamic() {
  252. var x = {hello: "world"};
  253. var rx = _l.readonly(x);
  254. assertEquals("Hello read incorrectly", "world", rx.hello);
  255. assertUndefined("Nonexistent property is not undefined", rx.thisIsNot);
  256. x.hello = "again";
  257. x.thisIsNot = "this is";
  258. assertEquals("Hello read incorrectly", "again", rx.hello);
  259. assertEquals("ThisIsNot read incorrectly", "this is", rx.thisIsNot);
  260. },
  261. function readonlyIsReadOnly() {
  262. var x = {hello: "world"};
  263. var rx = _l.readonly(x);
  264. rx.thisIsNot = "this is";
  265. delete rx.hello;
  266. assertEquals("Hello read incorrectly", "world", rx.hello);
  267. assertUndefined("Nonexistent property is not undefined", rx.thisIsNot);
  268. },
  269. function extendExtendsCorrectlyForAllCombinations() {
  270. var srcProto = { pro: "to" };
  271. var src = object(srcProto);
  272. src.a = "b"; src.c = "d";
  273. var dst = { pro: "fi", a: "bc", yes: "true" };
  274. var result = _l.extend(dst, src);
  275. assertEquals("extend did not return extended object", dst, result);
  276. assertEquals("a wasn't copied", "b", dst.a);
  277. assertEquals("c wasn't copied", "d", dst.c);
  278. assertEquals("pro was changed", "fi", dst.pro);
  279. assertEquals("yes was changed", "true", dst.yes);
  280. },
  281. //TODO tests for splitReplace
  282. ];
  283. // ==== Test runner ====
  284. import("lib-support/runner");
  285. runTestsByDefault();