PageRenderTime 306ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/extensions/SemanticResultFormats/Exhibit/exhibit/scripts/util/settings.js

https://github.com/ChuguluGames/mediawiki-svn
JavaScript | 439 lines | 361 code | 56 blank | 22 comment | 130 complexity | 64d59882704bc2c6594bbb8119b4d176 MD5 | raw file
  1. /*==================================================
  2. * Exhibit.SettingsUtilities
  3. *
  4. * Utilities for various parts of Exhibit to
  5. * collect their settings.
  6. *==================================================
  7. */
  8. Exhibit.SettingsUtilities = new Object();
  9. /*--------------------------------------------------
  10. * Static settings
  11. *--------------------------------------------------
  12. */
  13. Exhibit.SettingsUtilities.collectSettings = function(config, specs, settings) {
  14. Exhibit.SettingsUtilities._internalCollectSettings(
  15. function(field) { return config[field]; },
  16. specs,
  17. settings
  18. );
  19. };
  20. Exhibit.SettingsUtilities.collectSettingsFromDOM = function(configElmt, specs, settings) {
  21. Exhibit.SettingsUtilities._internalCollectSettings(
  22. function(field) { return Exhibit.getAttribute(configElmt, field); },
  23. specs,
  24. settings
  25. );
  26. };
  27. Exhibit.SettingsUtilities._internalCollectSettings = function(f, specs, settings) {
  28. for (var field in specs) {
  29. var spec = specs[field];
  30. var name = field;
  31. if ("name" in spec) {
  32. name = spec.name;
  33. }
  34. if (!(name in settings) && "defaultValue" in spec) {
  35. settings[name] = spec.defaultValue;
  36. }
  37. var value = f(field);
  38. if (value == null) {
  39. continue;
  40. }
  41. if (typeof value == "string") {
  42. value = value.trim();
  43. if (value.length == 0) {
  44. continue;
  45. }
  46. }
  47. var type = "text";
  48. if ("type" in spec) {
  49. type = spec.type;
  50. }
  51. var dimensions = 1;
  52. if ("dimensions" in spec) {
  53. dimensions = spec.dimensions;
  54. }
  55. try {
  56. if (dimensions > 1) {
  57. var separator = ",";
  58. if ("separator" in spec) {
  59. separator = spec.separator;
  60. }
  61. var a = value.split(separator);
  62. if (a.length != dimensions) {
  63. throw new Error("Expected a tuple of " + dimensions + " dimensions separated with " + separator + " but got " + value);
  64. } else {
  65. for (var i = 0; i < a.length; i++) {
  66. a[i] = Exhibit.SettingsUtilities._parseSetting(a[i].trim(), type, spec);
  67. }
  68. settings[name] = a;
  69. }
  70. } else {
  71. settings[name] = Exhibit.SettingsUtilities._parseSetting(value, type, spec);
  72. }
  73. } catch (e) {
  74. SimileAjax.Debug.exception(e);
  75. }
  76. }
  77. };
  78. Exhibit.SettingsUtilities._parseSetting = function(s, type, spec) {
  79. var sType = typeof s;
  80. if (type == "text") {
  81. return s;
  82. } else if (type == "float") {
  83. if (sType == "number") {
  84. return s;
  85. } else if (sType == "string") {
  86. var f = parseFloat(s);
  87. if (!isNaN(f)) {
  88. return f;
  89. }
  90. }
  91. throw new Error("Expected a floating point number but got " + s);
  92. } else if (type == "int") {
  93. if (sType == "number") {
  94. return Math.round(s);
  95. } else if (sType == "string") {
  96. var n = parseInt(s);
  97. if (!isNaN(n)) {
  98. return n;
  99. }
  100. }
  101. throw new Error("Expected an integer but got " + s);
  102. } else if (type == "boolean") {
  103. if (sType == "boolean") {
  104. return s;
  105. } else if (sType == "string") {
  106. s = s.toLowerCase();
  107. if (s == "true") {
  108. return true;
  109. } else if (s == "false") {
  110. return false;
  111. }
  112. }
  113. throw new Error("Expected either 'true' or 'false' but got " + s);
  114. } else if (type == "function") {
  115. if (sType == "function") {
  116. return s;
  117. } else if (sType == "string") {
  118. try {
  119. var f = eval(s);
  120. if (typeof f == "function") {
  121. return f;
  122. }
  123. } catch (e) {
  124. // silent
  125. }
  126. }
  127. throw new Error("Expected a function or the name of a function but got " + s);
  128. } else if (type == "enum") {
  129. var choices = spec.choices;
  130. for (var i = 0; i < choices.length; i++) {
  131. if (choices[i] == s) {
  132. return s;
  133. }
  134. }
  135. throw new Error("Expected one of " + choices.join(", ") + " but got " + s);
  136. } else {
  137. throw new Error("Unknown setting type " + type);
  138. }
  139. };
  140. /*--------------------------------------------------
  141. * Accessors
  142. *--------------------------------------------------
  143. */
  144. Exhibit.SettingsUtilities.createAccessors = function(config, specs, accessors) {
  145. Exhibit.SettingsUtilities._internalCreateAccessors(
  146. function(field) { return config[field]; },
  147. specs,
  148. accessors
  149. );
  150. };
  151. Exhibit.SettingsUtilities.createAccessorsFromDOM = function(configElmt, specs, accessors) {
  152. Exhibit.SettingsUtilities._internalCreateAccessors(
  153. function(field) { return Exhibit.getAttribute(configElmt, field); },
  154. specs,
  155. accessors
  156. );
  157. };
  158. Exhibit.SettingsUtilities._internalCreateAccessors = function(f, specs, accessors) {
  159. for (var field in specs) {
  160. var spec = specs[field];
  161. var accessorName = spec.accessorName;
  162. var accessor = null;
  163. var isTuple = false;
  164. var createOneAccessor = function(spec2) {
  165. isTuple = false;
  166. if ("bindings" in spec2) {
  167. return Exhibit.SettingsUtilities._createBindingsAccessor(f, spec2.bindings);
  168. } else if ("bindingNames" in spec2) {
  169. isTuple = true;
  170. return Exhibit.SettingsUtilities._createTupleAccessor(f, spec2);
  171. } else {
  172. return Exhibit.SettingsUtilities._createElementalAccessor(f, spec2);
  173. }
  174. };
  175. if ("alternatives" in spec) {
  176. var alternatives = spec.alternatives;
  177. for (var i = 0; i < alternatives.length; i++) {
  178. accessor = createOneAccessor(alternatives[i]);
  179. if (accessor != null) {
  180. break;
  181. }
  182. }
  183. } else {
  184. accessor = createOneAccessor(spec);
  185. }
  186. if (accessor != null) {
  187. accessors[accessorName] = accessor;
  188. } else if (!(accessorName in accessors)) {
  189. accessors[accessorName] = function(value, database, visitor) {};
  190. }
  191. }
  192. };
  193. Exhibit.SettingsUtilities._createBindingsAccessor = function(f, bindingSpecs) {
  194. var bindings = [];
  195. for (var i = 0; i < bindingSpecs.length; i++) {
  196. var bindingSpec = bindingSpecs[i];
  197. var accessor = null;
  198. var isTuple = false;
  199. if ("bindingNames" in bindingSpec) {
  200. isTuple = true;
  201. accessor = Exhibit.SettingsUtilities._createTupleAccessor(f, bindingSpec);
  202. } else {
  203. accessor = Exhibit.SettingsUtilities._createElementalAccessor(f, bindingSpec);
  204. }
  205. if (accessor == null) {
  206. if (!("optional" in bindingSpec) || !bindingSpec.optional) {
  207. return null;
  208. }
  209. } else {
  210. bindings.push({
  211. bindingName: bindingSpec.bindingName,
  212. accessor: accessor,
  213. isTuple: isTuple
  214. });
  215. }
  216. }
  217. return function(value, database, visitor) {
  218. Exhibit.SettingsUtilities._evaluateBindings(value, database, visitor, bindings);
  219. };
  220. };
  221. Exhibit.SettingsUtilities._createTupleAccessor = function(f, spec) {
  222. var value = f(spec.attributeName);
  223. if (value == null) {
  224. return null;
  225. }
  226. if (typeof value == "string") {
  227. value = value.trim();
  228. if (value.length == 0) {
  229. return null;
  230. }
  231. }
  232. try {
  233. var expression = Exhibit.ExpressionParser.parse(value);
  234. var parsers = [];
  235. var bindingTypes = spec.types;
  236. for (var i = 0; i < bindingTypes.length; i++) {
  237. parsers.push(Exhibit.SettingsUtilities._typeToParser(bindingTypes[i]));
  238. }
  239. var bindingNames = spec.bindingNames;
  240. var separator = ",";
  241. if ("separator" in spec) {
  242. separator = spec.separator;
  243. }
  244. return function(itemID, database, visitor, tuple) {
  245. expression.evaluateOnItem(itemID, database).values.visit(
  246. function(v) {
  247. var a = v.split(separator);
  248. if (a.length == parsers.length) {
  249. var tuple2 = {};
  250. if (tuple) {
  251. for (var n in tuple) {
  252. tuple2[n] = tuple[n];
  253. }
  254. }
  255. for (var i = 0; i < bindingNames.length; i++) {
  256. tuple2[bindingNames[i]] = null;
  257. parsers[i](a[i], function(v) { tuple2[bindingNames[i]] = v; });
  258. }
  259. visitor(tuple2);
  260. }
  261. }
  262. );
  263. };
  264. } catch (e) {
  265. SimileAjax.Debug.exception(e);
  266. return null;
  267. }
  268. };
  269. Exhibit.SettingsUtilities._createElementalAccessor = function(f, spec) {
  270. var value = f(spec.attributeName);
  271. if (value == null) {
  272. return null;
  273. }
  274. if (typeof value == "string") {
  275. value = value.trim();
  276. if (value.length == 0) {
  277. return null;
  278. }
  279. }
  280. var bindingType = "text";
  281. if ("type" in spec) {
  282. bindingType = spec.type;
  283. }
  284. try {
  285. var expression = Exhibit.ExpressionParser.parse(value);
  286. var parser = Exhibit.SettingsUtilities._typeToParser(bindingType);
  287. return function(itemID, database, visitor) {
  288. expression.evaluateOnItem(itemID, database).values.visit(
  289. function(v) { return parser(v, visitor); }
  290. );
  291. };
  292. } catch (e) {
  293. SimileAjax.Debug.exception(e);
  294. return null;
  295. }
  296. }
  297. Exhibit.SettingsUtilities._typeToParser = function(type) {
  298. switch (type) {
  299. case "text": return Exhibit.SettingsUtilities._textParser;
  300. case "url": return Exhibit.SettingsUtilities._urlParser;
  301. case "float": return Exhibit.SettingsUtilities._floatParser;
  302. case "int": return Exhibit.SettingsUtilities._intParser;
  303. case "date": return Exhibit.SettingsUtilities._dateParser;
  304. case "boolean": return Exhibit.SettingsUtilities._booleanParser;
  305. default:
  306. throw new Error("Unknown setting type " + type);
  307. }
  308. }
  309. Exhibit.SettingsUtilities._textParser = function(v, f) {
  310. return f(v);
  311. };
  312. Exhibit.SettingsUtilities._floatParser = function(v, f) {
  313. var n = parseFloat(v);
  314. if (!isNaN(n)) {
  315. return f(n);
  316. }
  317. return false;
  318. };
  319. Exhibit.SettingsUtilities._intParser = function(v, f) {
  320. var n = parseInt(v);
  321. if (!isNaN(n)) {
  322. return f(n);
  323. }
  324. return false;
  325. };
  326. Exhibit.SettingsUtilities._dateParser = function(v, f) {
  327. if (v instanceof Date) {
  328. return f(v);
  329. } else if (typeof v == "number") {
  330. var d = new Date(0);
  331. d.setUTCFullYear(v);
  332. return f(d);
  333. } else {
  334. var d = SimileAjax.DateTime.parseIso8601DateTime(v.toString());
  335. if (d != null) {
  336. return f(d);
  337. }
  338. }
  339. return false;
  340. };
  341. Exhibit.SettingsUtilities._booleanParser = function(v, f) {
  342. v = v.toString().toLowerCase();
  343. if (v == "true") {
  344. return f(true);
  345. } else if (v == "false") {
  346. return f(false);
  347. }
  348. return false;
  349. };
  350. Exhibit.SettingsUtilities._urlParser = function(v, f) {
  351. return f(Exhibit.Persistence.resolveURL(v.toString()));
  352. };
  353. Exhibit.SettingsUtilities._evaluateBindings = function(value, database, visitor, bindings) {
  354. var maxIndex = bindings.length - 1;
  355. var f = function(tuple, index) {
  356. var binding = bindings[index];
  357. var visited = false;
  358. var recurse = index == maxIndex ? function() { visitor(tuple); } : function() { f(tuple, index + 1); };
  359. if (binding.isTuple) {
  360. /*
  361. The tuple accessor will copy existing fields out of "tuple" into a new
  362. object and then injects new fields into it before calling the visitor.
  363. This is so that the same tuple object is not reused for different
  364. tuple values, which would cause old tuples to be overwritten by new ones.
  365. */
  366. binding.accessor(
  367. value,
  368. database,
  369. function(tuple2) { visited = true; tuple = tuple2; recurse(); },
  370. tuple
  371. );
  372. } else {
  373. var bindingName = binding.bindingName;
  374. binding.accessor(
  375. value,
  376. database,
  377. function(v) { visited = true; tuple[bindingName] = v; recurse(); }
  378. );
  379. }
  380. if (!visited) { recurse(); }
  381. };
  382. f({}, 0);
  383. };