Greasy Fork is available in English.

Brutusin Framework

JSON Forms

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/40518/264617/Brutusin%20Framework.js

  1. // ==UserScript==
  2. // @name Brutusin Framework
  3. // @namespace brutusin.org
  4. // @version 2018.4.4.2
  5. // @description JSON Forms
  6. // @author Ignacio del Valle Alles
  7. // ==/UserScript==
  8. /*
  9. * Copyright 2015 brutusin.org
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "SuperLicense");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. *
  23. * @author Ignacio del Valle Alles idelvall@brutusin.org
  24. */
  25.  
  26. if (typeof brutusin === "undefined") {
  27. window.brutusin = new Object();
  28. } else if (typeof brutusin !== "object") {
  29. throw ("brutusin global variable already exists");
  30. }
  31.  
  32. (function () {
  33. if (!String.prototype.startsWith) {
  34. String.prototype.startsWith = function (searchString, position) {
  35. position = position || 0;
  36. return this.indexOf(searchString, position) === position;
  37. };
  38. }
  39. if (!String.prototype.endsWith) {
  40. String.prototype.endsWith = function (searchString, position) {
  41. var subjectString = this.toString();
  42. if (position === undefined || position > subjectString.length) {
  43. position = subjectString.length;
  44. }
  45. position -= searchString.length;
  46. var lastIndex = subjectString.indexOf(searchString, position);
  47. return lastIndex !== -1 && lastIndex === position;
  48. };
  49. }
  50. if (!String.prototype.includes) {
  51. String.prototype.includes = function () {
  52. 'use strict';
  53. return String.prototype.indexOf.apply(this, arguments) !== -1;
  54. };
  55. }
  56. if (!String.prototype.format) {
  57. String.prototype.format = function () {
  58. var formatted = this;
  59. for (var i = 0; i < arguments.length; i++) {
  60. var regexp = new RegExp('\\{' + i + '\\}', 'gi');
  61. formatted = formatted.replace(regexp, arguments[i]);
  62. }
  63. return formatted;
  64. };
  65. }
  66.  
  67. var BrutusinForms = new Object();
  68. BrutusinForms.messages = {
  69. "validationError": "Validation error",
  70. "required": "This field is **required**",
  71. "invalidValue": "Invalid field value",
  72. "addpropNameExistent": "This property is already present in the object",
  73. "addpropNameRequired": "A name is required",
  74. "minItems": "At least `{0}` items are required",
  75. "maxItems": "At most `{0}` items are allowed",
  76. "pattern": "Value does not match pattern: `{0}`",
  77. "minLength": "Value must be **at least** `{0}` characters long",
  78. "maxLength": "Value must be **at most** `{0}` characters long",
  79. "multipleOf": "Value must be **multiple of** `{0}`",
  80. "minimum": "Value must be **greater or equal than** `{0}`",
  81. "exclusiveMinimum": "Value must be **greater than** `{0}`",
  82. "maximum": "Value must be **lower or equal than** `{0}`",
  83. "exclusiveMaximum": "Value must be **lower than** `{0}`",
  84. "minProperties": "At least `{0}` properties are required",
  85. "maxProperties": "At most `{0}` properties are allowed",
  86. "uniqueItems": "Array items must be unique",
  87. "addItem": "Add item",
  88. "true": "True",
  89. "false": "False"
  90. };
  91.  
  92. /**
  93. * Callback functions to be notified after an HTML element has been rendered (passed as parameter).
  94. * @type type
  95. */
  96. BrutusinForms.decorators = new Array();
  97.  
  98. BrutusinForms.addDecorator = function (f) {
  99. BrutusinForms.decorators[BrutusinForms.decorators.length] = f;
  100. };
  101.  
  102. BrutusinForms.onResolutionStarted = function (element) {
  103. };
  104.  
  105. BrutusinForms.onResolutionFinished = function (element) {
  106. };
  107.  
  108. BrutusinForms.onValidationError = function (element, message) {
  109. element.focus();
  110. if (!element.className.includes(" error")) {
  111. element.className += " error";
  112. }
  113. alert(message);
  114. };
  115.  
  116. BrutusinForms.onValidationSuccess = function (element) {
  117. element.className = element.className.replace(" error", "");
  118. };
  119.  
  120. /**
  121. * Callback function to be notified after a form has been rendered (passed as parameter).
  122. * @type type
  123. */
  124. BrutusinForms.postRender = null;
  125. /**
  126. * BrutusinForms instances created in the document
  127. * @type Array
  128. */
  129. BrutusinForms.instances = new Array();
  130. /**
  131. * BrutusinForms factory method
  132. * @param {type} schema schema object
  133. * @returns {BrutusinForms.create.obj|Object|Object.create.obj}
  134. */
  135. BrutusinForms.create = function (schema) {
  136. var SCHEMA_ANY = {"type": "any"};
  137. var obj = new Object();
  138.  
  139. var schemaMap = new Object();
  140. var dependencyMap = new Object();
  141. var renderInfoMap = new Object();
  142. var container;
  143. var data;
  144. var error;
  145. var initialValue;
  146. var inputCounter = 0;
  147. var root = schema;
  148. var formId = "BrutusinForms#" + BrutusinForms.instances.length;
  149.  
  150. renameRequiredPropeties(schema); // required v4 (array) -> requiredProperties
  151. populateSchemaMap("$", schema);
  152.  
  153. validateDepencyMapIsAcyclic();
  154.  
  155. var renderers = new Object();
  156.  
  157. renderers["integer"] = function (container, id, parentObject, propertyProvider, value) {
  158. renderers["string"](container, id, parentObject, propertyProvider, value);
  159. };
  160.  
  161. renderers["number"] = function (container, id, parentObject, propertyProvider, value) {
  162. renderers["string"](container, id, parentObject, propertyProvider, value);
  163. };
  164.  
  165. renderers["any"] = function (container, id, parentObject, propertyProvider, value) {
  166. renderers["string"](container, id, parentObject, propertyProvider, value);
  167. };
  168.  
  169. renderers["string"] = function (container, id, parentObject, propertyProvider, value) {
  170. /// TODO change the handler for when there is a 'media'
  171. /// specifier so it becomes a file element.
  172. var schemaId = getSchemaId(id);
  173. var parentId = getParentSchemaId(schemaId);
  174. var s = getSchema(schemaId);
  175. var parentSchema = getSchema(parentId);
  176. var input;
  177. if (s.type === "any") {
  178. input = document.createElement("textarea");
  179. if (value) {
  180. input.value = JSON.stringify(value, null, 4);
  181. if (s.readOnly)
  182. input.disabled = true;
  183. }
  184. } else if (s.media) {
  185. input = document.createElement("input");
  186. input.type = "file";
  187. // XXX TODO, encode the SOB properly.
  188. } else if (s.enum) {
  189. input = document.createElement("select");
  190. if (!s.required) {
  191. var option = document.createElement("option");
  192. var textNode = document.createTextNode("");
  193. option.value = "";
  194. appendChild(option, textNode, s);
  195. appendChild(input, option, s);
  196. }
  197. var selectedIndex = 0;
  198. for (var i = 0; i < s.enum.length; i++) {
  199. var option = document.createElement("option");
  200. var textNode = document.createTextNode(s.enum[i]);
  201. option.value = s.enum[i];
  202. appendChild(option, textNode, s);
  203. appendChild(input, option, s);
  204. if (value && s.enum[i] === value) {
  205. selectedIndex = i;
  206. if (!s.required) {
  207. selectedIndex++;
  208. }
  209. if (s.readOnly)
  210. input.disabled = true;
  211. }
  212. }
  213. if (s.enum.length === 1)
  214. input.selectedIndex = 0;
  215. else
  216. input.selectedIndex = selectedIndex;
  217. } else {
  218. input = document.createElement("input");
  219. if (s.type === "integer" || s.type === "number") {
  220. input.type = "number";
  221. input.step = s.step?""+s.step:"any";
  222. if (typeof value !== "number") {
  223. value = null;
  224. }
  225. } else if (s.format === "date-time") {
  226. try {
  227. input.type = "datetime-local";
  228. } catch (err) {
  229. // #46, problem in IE11. TODO polyfill?
  230. input.type = "text";
  231. }
  232. } else if (s.format === "date") {
  233. input.type = "date";
  234. } else if (s.format === "time") {
  235. input.type = "time";
  236. } else if (s.format === "email") {
  237. input.type = "email";
  238. } else if (s.format === "text") {
  239. input = document.createElement("textarea");
  240. } else {
  241. input.type = "text";
  242. }
  243. if (value !== null && typeof value !== "undefined") {
  244. // readOnly?
  245. input.value = value;
  246. if (s.readOnly)
  247. input.disabled = true;
  248.  
  249. }
  250. }
  251. input.schema = schemaId;
  252. input.setAttribute("autocorrect", "off");
  253.  
  254. input.getValidationError = function () {
  255. try {
  256. var value = getValue(s, input);
  257. if (value === null) {
  258. if (s.required) {
  259. if (parentSchema && parentSchema.type === "object") {
  260. if (parentSchema.required) {
  261. return BrutusinForms.messages["required"];
  262. } else {
  263. for (var prop in parentObject) {
  264. if (parentObject[prop] !== null) {
  265. return BrutusinForms.messages["required"];
  266. }
  267. }
  268. }
  269. } else {
  270. return BrutusinForms.messages["required"];
  271. }
  272. }
  273. } else {
  274. if (s.pattern && !s.pattern.test(value)) {
  275. return BrutusinForms.messages["pattern"].format(s.pattern.source);
  276. }
  277. if (s.minLength) {
  278. if (!value || s.minLength > value.length) {
  279. return BrutusinForms.messages["minLength"].format(s.minLength);
  280. }
  281. }
  282. if (s.maxLength) {
  283. if (value && s.maxLength < value.length) {
  284. return BrutusinForms.messages["maxLength"].format(s.maxLength);
  285. }
  286. }
  287. }
  288. if (value !== null && !isNaN(value)) {
  289. if (s.multipleOf && value % s.multipleOf !== 0) {
  290. return BrutusinForms.messages["multipleOf"].format(s.multipleOf);
  291. }
  292. if (s.hasOwnProperty("maximum")) {
  293. if (s.exclusiveMaximum && value >= s.maximum) {
  294. return BrutusinForms.messages["exclusiveMaximum"].format(s.maximum);
  295. } else if (!s.exclusiveMaximum && value > s.maximum) {
  296. return BrutusinForms.messages["maximum"].format(s.maximum);
  297. }
  298. }
  299. if (s.hasOwnProperty("minimum")) {
  300. if (s.exclusiveMinimum && value <= s.minimum) {
  301. return BrutusinForms.messages["exclusiveMinimum"].format(s.minimum);
  302. } else if (!s.exclusiveMinimum && value < s.minimum) {
  303. return BrutusinForms.messages["minimum"].format(s.minimum);
  304. }
  305. }
  306. }
  307. } catch (error) {
  308. return BrutusinForms.messages["invalidValue"];
  309. }
  310. };
  311.  
  312. input.onchange = function () {
  313. var value;
  314. try {
  315. value = getValue(s, input);
  316. } catch (error) {
  317. value = null;
  318. }
  319. if (parentObject) {
  320. parentObject[propertyProvider.getValue()] = value;
  321. } else {
  322. data = value;
  323. }
  324. onDependencyChanged(schemaId, input);
  325. };
  326.  
  327. if (s.description) {
  328. input.title = s.description;
  329. input.placeholder = s.description;
  330. }
  331. // if (s.pattern) {
  332. // input.pattern = s.pattern;
  333. // }
  334. // if (s.required) {
  335. // input.required = true;
  336. // }
  337. //
  338. // if (s.minimum) {
  339. // input.min = s.minimum;
  340. // }
  341. // if (s.maximum) {
  342. // input.max = s.maximum;
  343. // }
  344. input.onchange();
  345. input.id = getInputId();
  346. inputCounter++;
  347. appendChild(container, input, s);
  348. return parentObject;
  349. };
  350.  
  351. renderers["boolean"] = function (container, id, parentObject, propertyProvider, value) {
  352. var schemaId = getSchemaId(id);
  353. var s = getSchema(schemaId);
  354. var input;
  355. if (s.required) {
  356. input = document.createElement("input");
  357. input.type = "checkbox";
  358. if (value === true || value !== false && s.default) {
  359. input.checked = true;
  360. }
  361. } else {
  362. input = document.createElement("select");
  363. var emptyOption = document.createElement("option");
  364. var textEmpty = document.createTextNode("");
  365. textEmpty.value = "";
  366. appendChild(emptyOption, textEmpty, s);
  367. appendChild(input, emptyOption, s);
  368.  
  369. var optionTrue = document.createElement("option");
  370. var textTrue = document.createTextNode(BrutusinForms.messages["true"]);
  371. optionTrue.value = "true";
  372. appendChild(optionTrue, textTrue, s);
  373. appendChild(input, optionTrue, s);
  374.  
  375. var optionFalse = document.createElement("option");
  376. var textFalse = document.createTextNode(BrutusinForms.messages["false"]);
  377. optionFalse.value = "false";
  378. appendChild(optionFalse, textFalse, s);
  379. appendChild(input, optionFalse, s);
  380.  
  381. if (value === true) {
  382. input.selectedIndex = 1;
  383. } else if (value === false) {
  384. input.selectedIndex = 2;
  385. }
  386. }
  387. input.onchange = function () {
  388. if (parentObject) {
  389. parentObject[propertyProvider.getValue()] = getValue(s, input);
  390. } else {
  391. data = getValue(s, input);
  392. }
  393. onDependencyChanged(schemaId, input);
  394. };
  395. input.schema = schemaId;
  396. input.id = getInputId();
  397. inputCounter++;
  398. if (s.description) {
  399. input.title = s.description;
  400. }
  401. input.onchange();
  402. appendChild(container, input, s);
  403. };
  404.  
  405. renderers["oneOf"] = function (container, id, parentObject, propertyProvider, value) {
  406. var schemaId = getSchemaId(id);
  407. var s = getSchema(schemaId);
  408. var input = document.createElement("select");
  409. var display = document.createElement("div");
  410. display.innerHTML = "";
  411. input.type = "select";
  412. input.schema = schemaId;
  413. var noption = document.createElement("option");
  414. noption.value = null;
  415. appendChild(input, noption, s);
  416. for (var i = 0; i < s.oneOf.length; i++) {
  417. var option = document.createElement("option");
  418. var propId = schemaId + "." + i;
  419. var ss = getSchema(propId);
  420. var textNode = document.createTextNode(ss.title);
  421. option.value = s.oneOf[i];
  422. appendChild(option, textNode, s);
  423. appendChild(input, option, s);
  424. if (value === undefined || value === null)
  425. continue;
  426. if (s.readOnly)
  427. input.disabled = true;
  428. if (value.hasOwnProperty("type")) {
  429. if (ss.hasOwnProperty("properties")) {
  430. if (ss.properties.hasOwnProperty("type")) {
  431. var tryit = getSchema(ss.properties.type);
  432. if (value.type === tryit.enum[0]) {
  433. input.selectedIndex = i + 1;
  434. render(null, display, id + "." + (input.selectedIndex - 1), parentObject, propertyProvider, value);
  435. }
  436. }
  437. }
  438. }
  439. }
  440. input.onchange = function () {
  441. render(null, display, id + "." + (input.selectedIndex - 1), parentObject, propertyProvider, value);
  442. };
  443. appendChild(container, input, s);
  444. appendChild(container, display, s);
  445.  
  446. };
  447.  
  448. renderers["object"] = function (container, id, parentObject, propertyProvider, value) {
  449.  
  450. function createStaticPropertyProvider(propname) {
  451. var ret = new Object();
  452. ret.getValue = function () {
  453. return propname;
  454. };
  455. ret.onchange = function (oldName) {
  456. };
  457. return ret;
  458. }
  459.  
  460. function addAdditionalProperty(current, table, id, name, value, pattern) {
  461. var schemaId = getSchemaId(id);
  462. var s = getSchema(schemaId);
  463. var tbody = table.tBodies[0];
  464. var tr = document.createElement("tr");
  465. var td1 = document.createElement("td");
  466. td1.className = "add-prop-name";
  467. var innerTab = document.createElement("table");
  468. var innerTr = document.createElement("tr");
  469. var innerTd1 = document.createElement("td");
  470. var innerTd2 = document.createElement("td");
  471. var keyForBlank = "$" + Object.keys(current).length + "$";
  472. var td2 = document.createElement("td");
  473. td2.className = "prop-value";
  474. var nameInput = document.createElement("input");
  475. nameInput.type = "text";
  476. var regExp;
  477. if (pattern) {
  478. regExp = RegExp(pattern);
  479. }
  480. nameInput.getValidationError = function () {
  481. if (nameInput.previousValue !== nameInput.value) {
  482. if (current.hasOwnProperty(nameInput.value)) {
  483. return BrutusinForms.messages["addpropNameExistent"];
  484. }
  485. }
  486. if (!nameInput.value) {
  487. return BrutusinForms.messages["addpropNameRequired"];
  488. }
  489. };
  490. var pp = createPropertyProvider(
  491. function () {
  492. if (nameInput.value) {
  493. if (regExp) {
  494. if (nameInput.value.search(regExp) !== -1) {
  495. return nameInput.value;
  496. }
  497. } else {
  498. return nameInput.value;
  499. }
  500. }
  501. return keyForBlank;
  502. },
  503. function (oldPropertyName) {
  504. if (pp.getValue() === oldPropertyName) {
  505. return;
  506. }
  507. if (!oldPropertyName || !current.hasOwnProperty(oldPropertyName)) {
  508. oldPropertyName = keyForBlank;
  509. }
  510. if (current.hasOwnProperty(oldPropertyName) || regExp && pp.getValue().search(regExp) === -1) {
  511. current[pp.getValue()] = current[oldPropertyName];
  512. delete current[oldPropertyName];
  513. }
  514. });
  515.  
  516. nameInput.onblur = function () {
  517. if (nameInput.previousValue !== nameInput.value) {
  518. var name = nameInput.value;
  519. var i = 1;
  520. while (nameInput.previousValue !== name && current.hasOwnProperty(name)) {
  521. name = nameInput.value + "(" + i + ")";
  522. i++;
  523. }
  524. nameInput.value = name;
  525. pp.onchange(nameInput.previousValue);
  526. nameInput.previousValue = nameInput.value;
  527. return;
  528. }
  529. };
  530. var removeButton = document.createElement("button");
  531. removeButton.setAttribute('type', 'button');
  532. removeButton.className = "remove";
  533. appendChild(removeButton, document.createTextNode("x"), s);
  534. removeButton.onclick = function () {
  535. delete current[nameInput.value];
  536. table.deleteRow(tr.rowIndex);
  537. nameInput.value = null;
  538. pp.onchange(nameInput.previousValue);
  539. };
  540. appendChild(innerTd1, nameInput, s);
  541. appendChild(innerTd2, removeButton, s);
  542. appendChild(innerTr, innerTd1, s);
  543. appendChild(innerTr, innerTd2, s);
  544. appendChild(innerTab, innerTr, s);
  545. appendChild(td1, innerTab, s);
  546.  
  547. if (pattern !== undefined) {
  548. nameInput.placeholder = pattern;
  549. }
  550.  
  551. appendChild(tr, td1, s);
  552. appendChild(tr, td2, s);
  553. appendChild(tbody, tr, s);
  554. appendChild(table, tbody, s);
  555. render(null, td2, id, current, pp, value);
  556.  
  557. if (name) {
  558. nameInput.value = name;
  559. nameInput.onblur();
  560. }
  561. }
  562.  
  563. var schemaId = getSchemaId(id);
  564. var s = getSchema(schemaId);
  565. var current = new Object();
  566. if (!parentObject) {
  567. data = current;
  568. } else {
  569. if (propertyProvider.getValue() || propertyProvider.getValue() === 0) {
  570. parentObject[propertyProvider.getValue()] = current;
  571. }
  572. }
  573. var table = document.createElement("table");
  574. table.className = "object";
  575. var tbody = document.createElement("tbody");
  576. appendChild(table, tbody, s);
  577. var propNum = 0;
  578. if (s.hasOwnProperty("properties")) {
  579. propNum = s.properties.length;
  580. for (var prop in s.properties) {
  581. var tr = document.createElement("tr");
  582. var td1 = document.createElement("td");
  583. td1.className = "prop-name";
  584. var propId = id + "." + prop;
  585. var propSchema = getSchema(getSchemaId(propId));
  586. var td2 = document.createElement("td");
  587. td2.className = "prop-value";
  588.  
  589. appendChild(tbody, tr, propSchema);
  590. appendChild(tr, td1, propSchema);
  591. appendChild(tr, td2, propSchema);
  592. var pp = createStaticPropertyProvider(prop);
  593. var propInitialValue = null;
  594. if (value) {
  595. propInitialValue = value[prop];
  596. }
  597. render(td1, td2, propId, current, pp, propInitialValue);
  598. }
  599. }
  600. var usedProps = [];
  601. if (s.patternProperties || s.additionalProperties) {
  602. var div = document.createElement("div");
  603. appendChild(div, table, s);
  604. if (s.patternProperties) {
  605. for (var pattern in s.patternProperties) {
  606. var patProps = s.patternProperties[pattern];
  607. var patdiv = document.createElement("div");
  608. patdiv.className = "add-pattern-div";
  609. var addButton = document.createElement("button");
  610. addButton.setAttribute('type', 'button');
  611. addButton.pattern = pattern;
  612. addButton.id = id + "[" + pattern + "]";
  613. addButton.onclick = function () {
  614. addAdditionalProperty(current, table, this.id, undefined, undefined, this.pattern);
  615. };
  616. if (s.maxProperties || s.minProperties) {
  617. addButton.getValidationError = function () {
  618. if (s.minProperties && propNum + table.rows.length < s.minProperties) {
  619. return BrutusinForms.messages["minProperties"].format(s.minProperties);
  620. }
  621. if (s.maxProperties && propNum + table.rows.length > s.maxProperties) {
  622. return BrutusinForms.messages["maxProperties"].format(s.maxProperties);
  623. }
  624. };
  625. }
  626. if (patProps.description) {
  627. addButton.title = patProps.description;
  628. }
  629. appendChild(addButton, document.createTextNode("Add " + pattern), s);
  630. appendChild(patdiv, addButton, s);
  631. if (value) {
  632. for (var p in value) {
  633. if (s.properties && s.properties.hasOwnProperty(p)) {
  634. continue;
  635. }
  636. var r = RegExp(pattern);
  637. if (p.search(r) === -1) {
  638. continue;
  639. }
  640. if (usedProps.indexOf(p) !== -1) {
  641. continue;
  642. }
  643. addAdditionalProperty(current, table, id + "[" + pattern + "]", p, value[p], pattern);
  644. usedProps.push(p);
  645. }
  646. }
  647. appendChild(div, patdiv, s);
  648. }
  649. }
  650. if (s.additionalProperties) {
  651. var addPropS = getSchema(s.additionalProperties);
  652. var addButton = document.createElement("button");
  653. addButton.setAttribute('type', 'button');
  654. addButton.onclick = function () {
  655. addAdditionalProperty(current, table, id + "[*]", undefined);
  656. };
  657. if (s.maxProperties || s.minProperties) {
  658. addButton.getValidationError = function () {
  659. if (s.minProperties && propNum + table.rows.length < s.minProperties) {
  660. return BrutusinForms.messages["minProperties"].format(s.minProperties);
  661. }
  662. if (s.maxProperties && propNum + table.rows.length > s.maxProperties) {
  663. return BrutusinForms.messages["maxProperties"].format(s.maxProperties);
  664. }
  665. };
  666. }
  667. if (addPropS.description) {
  668. addButton.title = addPropS.description;
  669. }
  670. appendChild(addButton, document.createTextNode("Add"), s);
  671. appendChild(div, addButton, s);
  672. if (value) {
  673. for (var p in value) {
  674. if (s.properties && s.properties.hasOwnProperty(p)) {
  675. continue;
  676. }
  677. if (usedProps.indexOf(p) !== -1) {
  678. continue;
  679. }
  680. addAdditionalProperty(current, table, id + "[\"" + prop + "\"]", p, value[p]);
  681. }
  682. }
  683. }
  684. appendChild(container, div, s);
  685. } else {
  686. appendChild(container, table, s);
  687. }
  688. };
  689. // end of object renderer
  690. renderers["array"] = function (container, id, parentObject, propertyProvider, value) {
  691. function addItem(current, table, id, value, readOnly) {
  692. var schemaId = getSchemaId(id);
  693. var s = getSchema(schemaId);
  694. var tbody = document.createElement("tbody");
  695. var tr = document.createElement("tr");
  696. tr.className = "item";
  697. var td1 = document.createElement("td");
  698. td1.className = "item-index";
  699. var td2 = document.createElement("td");
  700. td2.className = "item-action";
  701. var td3 = document.createElement("td");
  702. td3.className = "item-value";
  703. var removeButton = document.createElement("button");
  704. removeButton.setAttribute('type', 'button');
  705. removeButton.className = "remove";
  706. if (readOnly === true)
  707. removeButton.disabled = true;
  708. appendChild(removeButton, document.createTextNode("x"), s);
  709. var computRowCount = function () {
  710. for (var i = 0; i < table.rows.length; i++) {
  711. var row = table.rows[i];
  712. row.cells[0].innerHTML = i + 1;
  713. }
  714. };
  715. removeButton.onclick = function () {
  716. current.splice(tr.rowIndex, 1);
  717. table.deleteRow(tr.rowIndex);
  718. computRowCount();
  719. };
  720. appendChild(td2, removeButton, s);
  721. var number = document.createTextNode(table.rows.length + 1);
  722. appendChild(td1, number, s);
  723. appendChild(tr, td1, s);
  724. appendChild(tr, td2, s);
  725. appendChild(tr, td3, s);
  726. appendChild(tbody, tr, s);
  727. appendChild(table, tbody, s);
  728. var pp = createPropertyProvider(function () {
  729. return tr.rowIndex;
  730. });
  731. render(null, td3, id, current, pp, value);
  732. }
  733.  
  734. var schemaId = getSchemaId(id);
  735. var s = getSchema(schemaId);
  736. var itemS = getSchema(s.items);
  737. var current = new Array();
  738. if (!parentObject) {
  739. data = current;
  740. } else {
  741. if (propertyProvider.getValue() || propertyProvider.getValue() === 0) {
  742. parentObject[propertyProvider.getValue()] = current;
  743. }
  744. }
  745. if (propertyProvider) {
  746. propertyProvider.onchange = function (oldPropertyName) {
  747. delete parentObject[oldPropertyName];
  748. parentObject[propertyProvider.getValue()] = current;
  749. };
  750. }
  751. var div = document.createElement("div");
  752. var table = document.createElement("table");
  753. table.className = "array";
  754. appendChild(div, table, s);
  755. appendChild(container, div, s);
  756. var addButton = document.createElement("button");
  757. if (s.readOnly)
  758. addButton.disabled = true;
  759. addButton.setAttribute('type', 'button');
  760. addButton.className = "addItem";
  761. addButton.getValidationError = function () {
  762. if (s.minItems && s.minItems > table.rows.length) {
  763. return BrutusinForms.messages["minItems"].format(s.minItems);
  764. }
  765. if (s.maxItems && s.maxItems < table.rows.length) {
  766. return BrutusinForms.messages["maxItems"].format(s.maxItems);
  767. }
  768. if (s.uniqueItems) {
  769. for (var i = 0; i < current.length; i++) {
  770. for (var j = i + 1; j < current.length; j++) {
  771. if (JSON.stringify(current[i]) === JSON.stringify(current[j])) {
  772. return BrutusinForms.messages["uniqueItems"];
  773. }
  774. }
  775. }
  776. }
  777. };
  778. addButton.onclick = function () {
  779. addItem(current, table, id + "[#]", null);
  780. };
  781. if (itemS.description) {
  782. addButton.title = itemS.description;
  783. }
  784. appendChild(addButton, document.createTextNode(BrutusinForms.messages["addItem"]), s);
  785. appendChild(div, table, s);
  786. appendChild(div, addButton, s);
  787. if (value && value instanceof Array) {
  788. for (var i = 0; i < value.length; i++) {
  789. addItem(current, table, id + "[" + i + "]", value[i], s.readOnly);
  790. }
  791. }
  792. appendChild(container, div, s);
  793. };
  794. // end of array render
  795. /**
  796. * Renders the form inside the the container, with the specified data preloaded
  797. * @param {type} c container
  798. * @param {type} data json data
  799. * @returns {undefined}
  800. */
  801. obj.render = function (c, data) {
  802. container = c;
  803. initialValue = data;
  804. var form = document.createElement("form");
  805. form.className = "brutusin-form";
  806. form.onsubmit = function (event) {
  807. return false;
  808. };
  809. if (container) {
  810. appendChild(container, form);
  811. } else {
  812. appendChild(document.body, form);
  813. }
  814. if (error) {
  815. var errLabel = document.createElement("label");
  816. var errNode = document.createTextNode(error);
  817. appendChild(errLabel, errNode);
  818. errLabel.className = "error-message";
  819. appendChild(form, errLabel);
  820. } else {
  821. render(null, form, "$", null, null);
  822. }
  823. if (dependencyMap.hasOwnProperty("$")) {
  824. onDependencyChanged("$");
  825. }
  826. if (BrutusinForms.postRender) {
  827. BrutusinForms.postRender(obj);
  828. }
  829. };
  830.  
  831. obj.getRenderingContainer = function () {
  832. return container;
  833. };
  834.  
  835. obj.validate = function () {
  836. return validate(container);
  837. };
  838.  
  839. obj.getData = function () {
  840. function removeEmptiesAndNulls(object, s) {
  841. if (s === null) {
  842. s = SCHEMA_ANY;
  843. }
  844. if (s.$ref) {
  845. s = getDefinition(s.$ref);
  846. }
  847. if (object instanceof Array) {
  848. if (object.length === 0) {
  849. return null;
  850. }
  851. var clone = new Array();
  852. for (var i = 0; i < object.length; i++) {
  853. clone[i] = removeEmptiesAndNulls(object[i], s.items);
  854. }
  855. return clone;
  856. } else if (object === "") {
  857. return null;
  858. } else if (object instanceof Object) {
  859. var clone = new Object();
  860. var nonEmpty = false;
  861. for (var prop in object) {
  862. if (prop.startsWith("$") && prop.endsWith("$")) {
  863. continue;
  864. }
  865. var ss = null;
  866. if (s.hasOwnProperty("properties") && s.properties.hasOwnProperty(prop)) {
  867. ss = s.properties[prop];
  868. }
  869. if (ss === null && s.hasOwnProperty("additionalProperties")) {
  870. if (typeof s.additionalProperties === 'object') {
  871. ss = s.additionalProperties;
  872. }
  873. }
  874. if (ss === null && s.hasOwnProperty("patternProperties")) {
  875. for (var p in s.patternProperties) {
  876. var r = RegExp(p);
  877. if (prop.search(r) !== -1) {
  878. ss = s.patternProperties[p];
  879. break;
  880. }
  881. }
  882. }
  883. var value = removeEmptiesAndNulls(object[prop], ss);
  884. if (value !== null) {
  885. clone[prop] = value;
  886. nonEmpty = true;
  887. }
  888. }
  889. if (nonEmpty || s.required) {
  890. return clone;
  891. } else {
  892. return null;
  893. }
  894. } else {
  895. return object;
  896. }
  897. }
  898. if (!container) {
  899. return null;
  900. } else {
  901. return removeEmptiesAndNulls(data, schema);
  902. }
  903. };
  904.  
  905. BrutusinForms.instances[BrutusinForms.instances.length] = obj;
  906.  
  907. return obj;
  908.  
  909. function validateDepencyMapIsAcyclic() {
  910. function dfs(visitInfo, stack, id) {
  911. if (stack.hasOwnProperty(id)) {
  912. error = "Schema dependency graph has cycles";
  913. return;
  914. }
  915. stack[id] = null;
  916. if (visitInfo.hasOwnProperty(id)) {
  917. return;
  918. }
  919. visitInfo[id] = null;
  920. var arr = dependencyMap[id];
  921. if (arr) {
  922. for (var i = 0; i < arr.length; i++) {
  923. dfs(visitInfo, stack, arr[i]);
  924. }
  925. }
  926. delete stack[id];
  927. }
  928. var visitInfo = new Object();
  929. for (var id in dependencyMap) {
  930. if (visitInfo.hasOwnProperty(id)) {
  931. continue;
  932. }
  933. dfs(visitInfo, new Object(), id);
  934. }
  935. }
  936.  
  937. function appendChild(parent, child, schema) {
  938. parent.appendChild(child);
  939. for (var i = 0; i < BrutusinForms.decorators.length; i++) {
  940. BrutusinForms.decorators[i](child, schema);
  941. }
  942. }
  943.  
  944. function createPseudoSchema(schema) {
  945. var pseudoSchema = new Object();
  946. for (var p in schema) {
  947. if (p === "items" || p === "properties" || p === "additionalProperties") {
  948. continue;
  949. }
  950. if (p === "pattern") {
  951. pseudoSchema[p] = new RegExp(schema[p]);
  952. } else {
  953. pseudoSchema[p] = schema[p];
  954. }
  955.  
  956. }
  957. return pseudoSchema;
  958. }
  959.  
  960. function getDefinition(path) {
  961. var parts = path.split('/');
  962. var def = root;
  963. for (var p in parts) {
  964. if (p === "0")
  965. continue;
  966. def = def[parts[p]];
  967.  
  968. }
  969. return def;
  970. }
  971.  
  972. function containsStr(array, string) {
  973. for (var i = 0; i < array.length; i++) {
  974. if (array[i] == string) {
  975. return true;
  976. }
  977. }
  978. return false;
  979. }
  980.  
  981. function renameRequiredPropeties(schema) {
  982. if (!schema) {
  983. return;
  984. } else if (schema.hasOwnProperty("oneOf")) {
  985. for (var i in schema.oneOf) {
  986. renameRequiredPropeties(schema.oneOf[i]);
  987. }
  988. } else if (schema.hasOwnProperty("$ref")) {
  989. var newSchema = getDefinition(schema["$ref"]);
  990. renameRequiredPropeties(newSchema);
  991. } else if (schema.type === "object") {
  992. if (schema.properties) {
  993. if (schema.hasOwnProperty("required")) {
  994. if (Array.isArray(schema.required)) {
  995. schema.requiredProperties = schema.required;
  996. delete schema.required;
  997. }
  998. }
  999. for (var prop in schema.properties) {
  1000. renameRequiredPropeties(schema.properties[prop]);
  1001. }
  1002. }
  1003. if (schema.patternProperties) {
  1004. for (var pat in schema.patternProperties) {
  1005. var s = schema.patternProperties[pat];
  1006. if (s.hasOwnProperty("type") || s.hasOwnProperty("$ref") || s.hasOwnProperty("oneOf")) {
  1007. renameRequiredPropeties(schema.patternProperties[pat]);
  1008. }
  1009. }
  1010. }
  1011. if (schema.additionalProperties) {
  1012. if (schema.additionalProperties.hasOwnProperty("type") || schema.additionalProperties.hasOwnProperty("oneOf")) {
  1013. renameRequiredPropeties(schema.additionalProperties);
  1014.  
  1015. }
  1016. }
  1017. } else if (schema.type === "array") {
  1018. renameRequiredPropeties(schema.items);
  1019. }
  1020. }
  1021.  
  1022. function populateSchemaMap(name, schema) {
  1023. var pseudoSchema = createPseudoSchema(schema);
  1024. pseudoSchema["$id"] = name;
  1025. schemaMap[name] = pseudoSchema;
  1026.  
  1027. if (!schema) {
  1028. return;
  1029. } else if (schema.hasOwnProperty("oneOf")) {
  1030. pseudoSchema.oneOf = new Array();
  1031. pseudoSchema.type = "oneOf";
  1032. for (var i in schema.oneOf) {
  1033. var childProp = name + "." + i;
  1034. pseudoSchema.oneOf[i] = childProp;
  1035. populateSchemaMap(childProp, schema.oneOf[i]);
  1036. }
  1037. } else if (schema.hasOwnProperty("$ref")) {
  1038. var refSchema = getDefinition(schema["$ref"]);
  1039. if (refSchema) {
  1040. if (schema.hasOwnProperty("title") || schema.hasOwnProperty("description")) {
  1041. var clonedRefSchema = {};
  1042. for (var prop in refSchema) {
  1043. clonedRefSchema[prop] = refSchema[prop];
  1044. }
  1045. if (schema.hasOwnProperty("title")) {
  1046. clonedRefSchema.title = schema.title;
  1047. }
  1048. if (schema.hasOwnProperty("description")) {
  1049. clonedRefSchema.description = schema.description;
  1050. }
  1051. refSchema = clonedRefSchema;
  1052. }
  1053. populateSchemaMap(name, refSchema);
  1054. }
  1055. } else if (schema.type === "object") {
  1056. if (schema.properties) {
  1057. pseudoSchema.properties = new Object();
  1058. for (var prop in schema.properties) {
  1059. var childProp = name + "." + prop;
  1060. pseudoSchema.properties[prop] = childProp;
  1061. var subSchema = schema.properties[prop];
  1062. if (schema.requiredProperties) {
  1063. if (containsStr(schema.requiredProperties, prop)) {
  1064. subSchema.required = true;
  1065. } else {
  1066. subSchema.required = false;
  1067. }
  1068. }
  1069. populateSchemaMap(childProp, subSchema);
  1070. }
  1071. }
  1072. if (schema.patternProperties) {
  1073. pseudoSchema.patternProperties = new Object();
  1074. for (var pat in schema.patternProperties) {
  1075. var patChildProp = name + "[" + pat + "]";
  1076. pseudoSchema.patternProperties[pat] = patChildProp;
  1077. var s = schema.patternProperties[pat];
  1078.  
  1079. if (s.hasOwnProperty("type") || s.hasOwnProperty("$ref") ||
  1080. s.hasOwnProperty("oneOf")) {
  1081. populateSchemaMap(patChildProp, schema.patternProperties[pat]);
  1082. } else {
  1083. populateSchemaMap(patChildProp, SCHEMA_ANY);
  1084. }
  1085. }
  1086. }
  1087. if (schema.additionalProperties) {
  1088. var childProp = name + "[*]";
  1089. pseudoSchema.additionalProperties = childProp;
  1090. if (schema.additionalProperties.hasOwnProperty("type") ||
  1091. schema.additionalProperties.hasOwnProperty("oneOf")) {
  1092. populateSchemaMap(childProp, schema.additionalProperties);
  1093. } else {
  1094. populateSchemaMap(childProp, SCHEMA_ANY);
  1095. }
  1096. }
  1097. } else if (schema.type === "array") {
  1098. pseudoSchema.items = name + "[#]";
  1099. populateSchemaMap(pseudoSchema.items, schema.items);
  1100. }
  1101. if (schema.hasOwnProperty("dependsOn")) {
  1102. if (schema.dependsOn === null) {
  1103. schema.dependsOn = ["$"];
  1104. }
  1105. var arr = new Array();
  1106. for (var i = 0; i < schema.dependsOn.length; i++) {
  1107. if (!schema.dependsOn[i]) {
  1108. arr[i] = "$";
  1109. // Relative cases
  1110. } else if (schema.dependsOn[i].startsWith("$")) {
  1111. arr[i] = schema.dependsOn[i];
  1112. // Relative cases
  1113. } else if (name.endsWith("]")) {
  1114. arr[i] = name + "." + schema.dependsOn[i];
  1115. } else {
  1116. arr[i] = name.substring(0, name.lastIndexOf(".")) + "." + schema.dependsOn[i];
  1117. }
  1118. }
  1119. schemaMap[name].dependsOn = arr;
  1120. for (var i = 0; i < arr.length; i++) {
  1121. var entry = dependencyMap[arr[i]];
  1122. if (!entry) {
  1123. entry = new Array();
  1124. dependencyMap[arr[i]] = entry;
  1125. }
  1126. entry[entry.length] = name;
  1127. }
  1128. }
  1129. }
  1130.  
  1131. function renderTitle(container, title, schema) {
  1132. if (container) {
  1133. if (title) {
  1134. var titleLabel = document.createElement("label");
  1135. if (schema.type !== "any" && schema.type !== "object" && schema.type !== "array") {
  1136. titleLabel.htmlFor = getInputId();
  1137. }
  1138. var titleNode = document.createTextNode(title + ":");
  1139. appendChild(titleLabel, titleNode, schema);
  1140. if (schema.description) {
  1141. titleLabel.title = schema.description;
  1142. }
  1143. if (schema.required) {
  1144. var sup = document.createElement("sup");
  1145. appendChild(sup, document.createTextNode("*"), schema);
  1146. appendChild(titleLabel, sup, schema);
  1147. titleLabel.className = "required";
  1148. }
  1149. appendChild(container, titleLabel, schema);
  1150. }
  1151. }
  1152. }
  1153.  
  1154. function getInputId() {
  1155. return formId + "_" + inputCounter;
  1156. }
  1157.  
  1158. function validate(element) {
  1159. var ret = true;
  1160. if (element.hasOwnProperty("getValidationError")) {
  1161. var error = element.getValidationError();
  1162. if (error) {
  1163. BrutusinForms.onValidationError(element, error);
  1164. ret = false;
  1165. } else {
  1166. BrutusinForms.onValidationSuccess(element);
  1167. }
  1168. }
  1169. if (element.childNodes) {
  1170. for (var i = 0; i < element.childNodes.length; i++) {
  1171. if (!validate(element.childNodes[i])) {
  1172. ret = false;
  1173. }
  1174. }
  1175. }
  1176. return ret;
  1177. }
  1178.  
  1179. function clear(container) {
  1180. if (container) {
  1181. while (container.firstChild) {
  1182. container.removeChild(container.firstChild);
  1183. }
  1184. }
  1185. }
  1186.  
  1187. function render(titleContainer, container, id, parentObject, propertyProvider, value) {
  1188. //console.log(id);
  1189. var schemaId = getSchemaId(id);
  1190. var s = getSchema(schemaId);
  1191. renderInfoMap[schemaId] = new Object();
  1192. renderInfoMap[schemaId].titleContainer = titleContainer;
  1193. renderInfoMap[schemaId].container = container;
  1194. renderInfoMap[schemaId].parentObject = parentObject;
  1195. renderInfoMap[schemaId].propertyProvider = propertyProvider;
  1196. renderInfoMap[schemaId].value = value;
  1197. clear(titleContainer);
  1198. clear(container);
  1199. //console.log(id,s,value);
  1200. var r = renderers[s.type];
  1201. if (r && !s.dependsOn) {
  1202. if (s.title) {
  1203. renderTitle(titleContainer, s.title, s);
  1204. } else if (propertyProvider) {
  1205. renderTitle(titleContainer, propertyProvider.getValue(), s);
  1206. }
  1207. if (!value) {
  1208. if (typeof initialValue !== "undefined" && initialValue !== null) {
  1209. value = getInitialValue(id);
  1210. } else {
  1211. value = s.default;
  1212. }
  1213. }
  1214. r(container, id, parentObject, propertyProvider, value);
  1215. } else if (s.$ref) {
  1216. if (obj.schemaResolver) {
  1217. var cb = function (schemas) {
  1218. if (schemas && schemas.hasOwnProperty(id)) {
  1219. if (JSON.stringify(schemaMap[id]) !== JSON.stringify(schemas[id])) {
  1220. cleanSchemaMap(id);
  1221. cleanData(id);
  1222. populateSchemaMap(id, schemas[id]);
  1223. var renderInfo = renderInfoMap[id];
  1224. if (renderInfo) {
  1225. render(renderInfo.titleContainer, renderInfo.container, id, renderInfo.parentObject, renderInfo.propertyProvider, renderInfo.value);
  1226. }
  1227. }
  1228. }
  1229. BrutusinForms.onResolutionFinished(parentObject);
  1230. };
  1231. BrutusinForms.onResolutionStarted(parentObject);
  1232. obj.schemaResolver([id], obj.getData(), cb);
  1233. }
  1234. }
  1235. }
  1236.  
  1237. /**
  1238. * Used in object additionalProperties and arrays
  1239. * @param {type} getValue
  1240. * @param {type} onchange
  1241. * @returns {Object.create.createPropertyProvider.ret}
  1242. */
  1243. function createPropertyProvider(getValue, onchange) {
  1244. var ret = new Object();
  1245. ret.getValue = getValue;
  1246. ret.onchange = onchange;
  1247. return ret;
  1248. }
  1249.  
  1250. function getInitialValue(id) {
  1251. var ret;
  1252. try {
  1253. eval("ret = initialValue" + id.substring(1));
  1254. } catch (e) {
  1255. ret = null;
  1256. }
  1257. return ret;
  1258. }
  1259.  
  1260. function getValue(schema, input) {
  1261. if (typeof input.getValue === "function") {
  1262. return input.getValue();
  1263. }
  1264. var value;
  1265. if (input.tagName.toLowerCase() === "select") {
  1266. value = input.options[input.selectedIndex].value;
  1267. } else {
  1268. value = input.value;
  1269. }
  1270. if (value === "") {
  1271. return null;
  1272. }
  1273. if (schema.type === "integer") {
  1274. value = parseInt(value);
  1275. if (!isFinite(value)) {
  1276. value = null;
  1277. }
  1278. } else if (schema.type === "number") {
  1279. value = parseFloat(value);
  1280. if (!isFinite(value)) {
  1281. value = null;
  1282. }
  1283. } else if (schema.type === "boolean") {
  1284. if (input.tagName.toLowerCase() === "input") {
  1285. value = input.checked;
  1286. if (!value) {
  1287. value = false;
  1288. }
  1289. } else if (input.tagName.toLowerCase() === "select") {
  1290. if (input.value === "true") {
  1291. value = true;
  1292. } else if (input.value === "false") {
  1293. value = false;
  1294. } else {
  1295. value = null;
  1296. }
  1297. }
  1298. } else if (schema.type === "any") {
  1299. if (value) {
  1300. eval("value=" + value);
  1301. }
  1302. }
  1303. return value;
  1304. }
  1305.  
  1306. function getSchemaId(id) {
  1307. return id.replace(/\["[^"]*"\]/g, "[*]").replace(/\[\d*\]/g, "[#]");
  1308. }
  1309.  
  1310. function getParentSchemaId(id) {
  1311. return id.substring(0, id.lastIndexOf("."));
  1312. }
  1313.  
  1314. function getSchema(schemaId) {
  1315. return schemaMap[schemaId];
  1316. }
  1317.  
  1318. function cleanSchemaMap(schemaId) {
  1319. for (var prop in schemaMap) {
  1320. if (prop.startsWith(schemaId)) {
  1321. delete schemaMap[prop];
  1322. }
  1323. }
  1324. }
  1325. function cleanData(schemaId) {
  1326. var expression = new Expression(schemaId);
  1327. expression.visit(data, function (data, parent, property) {
  1328. delete parent[property];
  1329. });
  1330. }
  1331.  
  1332. function onDependencyChanged(name, source) {
  1333.  
  1334. var arr = dependencyMap[name];
  1335. if (!arr || !obj.schemaResolver) {
  1336. return;
  1337. }
  1338. var cb = function (schemas) {
  1339. if (schemas) {
  1340. for (var id in schemas) {
  1341. if (JSON.stringify(schemaMap[id]) !== JSON.stringify(schemas[id])) {
  1342. cleanSchemaMap(id);
  1343. cleanData(id);
  1344. populateSchemaMap(id, schemas[id]);
  1345. var renderInfo = renderInfoMap[id];
  1346. if (renderInfo) {
  1347. render(renderInfo.titleContainer, renderInfo.container, id, renderInfo.parentObject, renderInfo.propertyProvider, renderInfo.value);
  1348. }
  1349. }
  1350. }
  1351. }
  1352. BrutusinForms.onResolutionFinished(source);
  1353. };
  1354. BrutusinForms.onResolutionStarted(source);
  1355. obj.schemaResolver(arr, obj.getData(), cb);
  1356.  
  1357.  
  1358. }
  1359.  
  1360. function Expression(exp) {
  1361. if (exp === null || exp.length === 0 || exp === ".") {
  1362. exp = "$";
  1363. }
  1364. var queue = new Array();
  1365. var tokens = parseTokens(exp);
  1366. var isInBracket = false;
  1367. var numInBracket = 0;
  1368. var sb = "";
  1369. for (var i = 0; i < tokens.length; i++) {
  1370. var token = tokens[i];
  1371. if (token === "[") {
  1372. if (isInBracket) {
  1373. throw ("Error parsing expression '" + exp + "': Nested [ found");
  1374. }
  1375. isInBracket = true;
  1376. numInBracket = 0;
  1377. sb = sb + token;
  1378. } else if (token === "]") {
  1379. if (!isInBracket) {
  1380. throw ("Error parsing expression '" + exp + "': Unbalanced ] found");
  1381. }
  1382. isInBracket = false;
  1383. sb = sb + token;
  1384. queue[queue.length] = sb;
  1385. sb = "";
  1386. } else {
  1387. if (isInBracket) {
  1388. if (numInBracket > 0) {
  1389. throw ("Error parsing expression '" + exp + "': Multiple tokens found inside a bracket");
  1390. }
  1391. sb = sb + token;
  1392. numInBracket++;
  1393. } else {
  1394. queue[queue.length] = token;
  1395. }
  1396. }
  1397. if (i === tokens.length - 1) {
  1398. if (isInBracket) {
  1399. throw ("Error parsing expression '" + exp + "': Unbalanced [ found");
  1400. }
  1401. }
  1402. }
  1403. this.exp = exp;
  1404. this.queue = queue;
  1405. this.visit = function (data, visitor) {
  1406. function visit(name, queue, data, parentData, property) {
  1407. if (data == null) {
  1408. return;
  1409. }
  1410. var currentToken = queue.shift();
  1411. if (currentToken === "$") {
  1412. name = "$";
  1413. var currentToken = queue.shift();
  1414. }
  1415. if (!currentToken) {
  1416. visitor(data, parentData, property);
  1417. } else if (Array.isArray(data)) {
  1418. if (!currentToken.startsWith("[")) {
  1419. throw ("Node '" + name + "' is of type array");
  1420. }
  1421. var element = currentToken.substring(1, currentToken.length - 1);
  1422. if (element.equals("#")) {
  1423. for (var i = 0; i < data.length; i++) {
  1424. var child = data[i];
  1425. visit(name + currentToken, queue.slice(0), child, data, i);
  1426. visit(name + "[" + i + "]", queue.slice(0), child, data, i);
  1427. }
  1428. } else if (element === "$") {
  1429. var child = data[data.length - 1];
  1430. visit(name + currentToken, queue.slice(0), child, data, data.length - 1);
  1431. } else {
  1432. var index = parseInt(element);
  1433. if (isNaN(index)) {
  1434. throw ("Element '" + element + "' of node '" + name + "' is not an integer, or the '$' last element symbol, or the wilcard symbol '#'");
  1435. }
  1436. if (index < 0) {
  1437. throw ("Element '" + element + "' of node '" + name + "' is lower than zero");
  1438. }
  1439. var child = data[index];
  1440. visit(name + currentToken, queue.slice(0), child, data, index);
  1441. }
  1442. } else if ("object" === typeof data) {
  1443. if (currentToken === "[*]") {
  1444. for (var p in data) {
  1445. var child = data[p];
  1446. visit(name + currentToken, queue.slice(0), child, data, p);
  1447. visit(name + "[\"" + p + "\"]", queue.slice(0), child, data, p);
  1448. }
  1449. } else {
  1450. var child;
  1451. if (currentToken.startsWith("[")) {
  1452. var element = currentToken.substring(1, currentToken.length - 1);
  1453. if (element.startsWith("\"") || element.startsWith("'")) {
  1454. element = element.substring(1, element.length() - 1);
  1455. } else {
  1456. throw ("Element '" + element + "' of node '" + name + "' must be a string expression or wilcard '*'");
  1457. }
  1458. name = name + currentToken;
  1459. child = data[element];
  1460. } else {
  1461. if (name.length > 0) {
  1462. name = name + "." + currentToken;
  1463. } else {
  1464. name = currentToken;
  1465. }
  1466. child = data[currentToken];
  1467. }
  1468. visit(name, queue, child, data, currentToken);
  1469. }
  1470. } else if ("boolean" === typeof data
  1471. || "number" === typeof data
  1472. || "string" === typeof data) {
  1473. throw ("Node is leaf but still are tokens remaining: " + currentToken);
  1474. } else {
  1475. throw ("Node type '" + typeof data + "' not supported for index field '" + name + "'");
  1476. }
  1477. }
  1478. visit(this.exp, this.queue, data);
  1479. };
  1480.  
  1481. function parseTokens(exp) {
  1482. if (exp === null) {
  1483. return null;
  1484. }
  1485. var ret = new Array();
  1486. var commentChar = null;
  1487. var start = 0;
  1488. for (var i = 0; i < exp.length; i++) {
  1489. if (exp.charAt(i) === '"') {
  1490. if (commentChar === null) {
  1491. commentChar = '"';
  1492. } else if (commentChar === '"') {
  1493. commentChar = null;
  1494. ret[ret.length] = exp.substring(start, i + 1).trim();
  1495. start = i + 1;
  1496. }
  1497. } else if (exp.charAt(i) === '\'') {
  1498. if (commentChar === null) {
  1499. commentChar = '\'';
  1500. } else if (commentChar === '\'') {
  1501. commentChar = null;
  1502. ret[ret.length] = exp.substring(start, i + 1).trim();
  1503. start = i + 1;
  1504. }
  1505. } else if (exp.charAt(i) === '[') {
  1506. if (commentChar === null) {
  1507. if (start !== i) {
  1508. ret[ret.length] = exp.substring(start, i).trim();
  1509. }
  1510. ret[ret.length] = "[";
  1511. start = i + 1;
  1512. }
  1513. } else if (exp.charAt(i) === ']') {
  1514. if (commentChar === null) {
  1515. if (start !== i) {
  1516. ret[ret.length] = exp.substring(start, i).trim();
  1517. }
  1518. ret[ret.length] = "]";
  1519. start = i + 1;
  1520. }
  1521. } else if (exp.charAt(i) === '.') {
  1522. if (commentChar === null) {
  1523. if (start !== i) {
  1524. ret[ret.length] = exp.substring(start, i).trim();
  1525. }
  1526. start = i + 1;
  1527. }
  1528. } else if (i === exp.length - 1) {
  1529. ret[ret.length] = exp.substring(start, i + 1).trim();
  1530. }
  1531. }
  1532. return ret;
  1533. }
  1534. }
  1535. };
  1536. brutusin["json-forms"] = BrutusinForms;
  1537. }());