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
- // ==UserScript==
- // @name Brutusin Framework
- // @namespace brutusin.org
- // @version 2018.4.4.2
- // @description JSON Forms
- // @author Ignacio del Valle Alles
- // ==/UserScript==
- /*
- * Copyright 2015 brutusin.org
- *
- * Licensed under the Apache License, Version 2.0 (the "SuperLicense");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @author Ignacio del Valle Alles idelvall@brutusin.org
- */
-
- if (typeof brutusin === "undefined") {
- window.brutusin = new Object();
- } else if (typeof brutusin !== "object") {
- throw ("brutusin global variable already exists");
- }
-
- (function () {
- if (!String.prototype.startsWith) {
- String.prototype.startsWith = function (searchString, position) {
- position = position || 0;
- return this.indexOf(searchString, position) === position;
- };
- }
- if (!String.prototype.endsWith) {
- String.prototype.endsWith = function (searchString, position) {
- var subjectString = this.toString();
- if (position === undefined || position > subjectString.length) {
- position = subjectString.length;
- }
- position -= searchString.length;
- var lastIndex = subjectString.indexOf(searchString, position);
- return lastIndex !== -1 && lastIndex === position;
- };
- }
- if (!String.prototype.includes) {
- String.prototype.includes = function () {
- 'use strict';
- return String.prototype.indexOf.apply(this, arguments) !== -1;
- };
- }
- if (!String.prototype.format) {
- String.prototype.format = function () {
- var formatted = this;
- for (var i = 0; i < arguments.length; i++) {
- var regexp = new RegExp('\\{' + i + '\\}', 'gi');
- formatted = formatted.replace(regexp, arguments[i]);
- }
- return formatted;
- };
- }
-
- var BrutusinForms = new Object();
- BrutusinForms.messages = {
- "validationError": "Validation error",
- "required": "This field is **required**",
- "invalidValue": "Invalid field value",
- "addpropNameExistent": "This property is already present in the object",
- "addpropNameRequired": "A name is required",
- "minItems": "At least `{0}` items are required",
- "maxItems": "At most `{0}` items are allowed",
- "pattern": "Value does not match pattern: `{0}`",
- "minLength": "Value must be **at least** `{0}` characters long",
- "maxLength": "Value must be **at most** `{0}` characters long",
- "multipleOf": "Value must be **multiple of** `{0}`",
- "minimum": "Value must be **greater or equal than** `{0}`",
- "exclusiveMinimum": "Value must be **greater than** `{0}`",
- "maximum": "Value must be **lower or equal than** `{0}`",
- "exclusiveMaximum": "Value must be **lower than** `{0}`",
- "minProperties": "At least `{0}` properties are required",
- "maxProperties": "At most `{0}` properties are allowed",
- "uniqueItems": "Array items must be unique",
- "addItem": "Add item",
- "true": "True",
- "false": "False"
- };
-
- /**
- * Callback functions to be notified after an HTML element has been rendered (passed as parameter).
- * @type type
- */
- BrutusinForms.decorators = new Array();
-
- BrutusinForms.addDecorator = function (f) {
- BrutusinForms.decorators[BrutusinForms.decorators.length] = f;
- };
-
- BrutusinForms.onResolutionStarted = function (element) {
- };
-
- BrutusinForms.onResolutionFinished = function (element) {
- };
-
- BrutusinForms.onValidationError = function (element, message) {
- element.focus();
- if (!element.className.includes(" error")) {
- element.className += " error";
- }
- alert(message);
- };
-
- BrutusinForms.onValidationSuccess = function (element) {
- element.className = element.className.replace(" error", "");
- };
-
- /**
- * Callback function to be notified after a form has been rendered (passed as parameter).
- * @type type
- */
- BrutusinForms.postRender = null;
- /**
- * BrutusinForms instances created in the document
- * @type Array
- */
- BrutusinForms.instances = new Array();
- /**
- * BrutusinForms factory method
- * @param {type} schema schema object
- * @returns {BrutusinForms.create.obj|Object|Object.create.obj}
- */
- BrutusinForms.create = function (schema) {
- var SCHEMA_ANY = {"type": "any"};
- var obj = new Object();
-
- var schemaMap = new Object();
- var dependencyMap = new Object();
- var renderInfoMap = new Object();
- var container;
- var data;
- var error;
- var initialValue;
- var inputCounter = 0;
- var root = schema;
- var formId = "BrutusinForms#" + BrutusinForms.instances.length;
-
- renameRequiredPropeties(schema); // required v4 (array) -> requiredProperties
- populateSchemaMap("$", schema);
-
- validateDepencyMapIsAcyclic();
-
- var renderers = new Object();
-
- renderers["integer"] = function (container, id, parentObject, propertyProvider, value) {
- renderers["string"](container, id, parentObject, propertyProvider, value);
- };
-
- renderers["number"] = function (container, id, parentObject, propertyProvider, value) {
- renderers["string"](container, id, parentObject, propertyProvider, value);
- };
-
- renderers["any"] = function (container, id, parentObject, propertyProvider, value) {
- renderers["string"](container, id, parentObject, propertyProvider, value);
- };
-
- renderers["string"] = function (container, id, parentObject, propertyProvider, value) {
- /// TODO change the handler for when there is a 'media'
- /// specifier so it becomes a file element.
- var schemaId = getSchemaId(id);
- var parentId = getParentSchemaId(schemaId);
- var s = getSchema(schemaId);
- var parentSchema = getSchema(parentId);
- var input;
- if (s.type === "any") {
- input = document.createElement("textarea");
- if (value) {
- input.value = JSON.stringify(value, null, 4);
- if (s.readOnly)
- input.disabled = true;
- }
- } else if (s.media) {
- input = document.createElement("input");
- input.type = "file";
- // XXX TODO, encode the SOB properly.
- } else if (s.enum) {
- input = document.createElement("select");
- if (!s.required) {
- var option = document.createElement("option");
- var textNode = document.createTextNode("");
- option.value = "";
- appendChild(option, textNode, s);
- appendChild(input, option, s);
- }
- var selectedIndex = 0;
- for (var i = 0; i < s.enum.length; i++) {
- var option = document.createElement("option");
- var textNode = document.createTextNode(s.enum[i]);
- option.value = s.enum[i];
- appendChild(option, textNode, s);
- appendChild(input, option, s);
- if (value && s.enum[i] === value) {
- selectedIndex = i;
- if (!s.required) {
- selectedIndex++;
- }
- if (s.readOnly)
- input.disabled = true;
- }
- }
- if (s.enum.length === 1)
- input.selectedIndex = 0;
- else
- input.selectedIndex = selectedIndex;
- } else {
- input = document.createElement("input");
- if (s.type === "integer" || s.type === "number") {
- input.type = "number";
- input.step = s.step?""+s.step:"any";
- if (typeof value !== "number") {
- value = null;
- }
- } else if (s.format === "date-time") {
- try {
- input.type = "datetime-local";
- } catch (err) {
- // #46, problem in IE11. TODO polyfill?
- input.type = "text";
- }
- } else if (s.format === "date") {
- input.type = "date";
- } else if (s.format === "time") {
- input.type = "time";
- } else if (s.format === "email") {
- input.type = "email";
- } else if (s.format === "text") {
- input = document.createElement("textarea");
- } else {
- input.type = "text";
- }
- if (value !== null && typeof value !== "undefined") {
- // readOnly?
- input.value = value;
- if (s.readOnly)
- input.disabled = true;
-
- }
- }
- input.schema = schemaId;
- input.setAttribute("autocorrect", "off");
-
- input.getValidationError = function () {
- try {
- var value = getValue(s, input);
- if (value === null) {
- if (s.required) {
- if (parentSchema && parentSchema.type === "object") {
- if (parentSchema.required) {
- return BrutusinForms.messages["required"];
- } else {
- for (var prop in parentObject) {
- if (parentObject[prop] !== null) {
- return BrutusinForms.messages["required"];
- }
- }
- }
- } else {
- return BrutusinForms.messages["required"];
- }
- }
- } else {
- if (s.pattern && !s.pattern.test(value)) {
- return BrutusinForms.messages["pattern"].format(s.pattern.source);
- }
- if (s.minLength) {
- if (!value || s.minLength > value.length) {
- return BrutusinForms.messages["minLength"].format(s.minLength);
- }
- }
- if (s.maxLength) {
- if (value && s.maxLength < value.length) {
- return BrutusinForms.messages["maxLength"].format(s.maxLength);
- }
- }
- }
- if (value !== null && !isNaN(value)) {
- if (s.multipleOf && value % s.multipleOf !== 0) {
- return BrutusinForms.messages["multipleOf"].format(s.multipleOf);
- }
- if (s.hasOwnProperty("maximum")) {
- if (s.exclusiveMaximum && value >= s.maximum) {
- return BrutusinForms.messages["exclusiveMaximum"].format(s.maximum);
- } else if (!s.exclusiveMaximum && value > s.maximum) {
- return BrutusinForms.messages["maximum"].format(s.maximum);
- }
- }
- if (s.hasOwnProperty("minimum")) {
- if (s.exclusiveMinimum && value <= s.minimum) {
- return BrutusinForms.messages["exclusiveMinimum"].format(s.minimum);
- } else if (!s.exclusiveMinimum && value < s.minimum) {
- return BrutusinForms.messages["minimum"].format(s.minimum);
- }
- }
- }
- } catch (error) {
- return BrutusinForms.messages["invalidValue"];
- }
- };
-
- input.onchange = function () {
- var value;
- try {
- value = getValue(s, input);
- } catch (error) {
- value = null;
- }
- if (parentObject) {
- parentObject[propertyProvider.getValue()] = value;
- } else {
- data = value;
- }
- onDependencyChanged(schemaId, input);
- };
-
- if (s.description) {
- input.title = s.description;
- input.placeholder = s.description;
- }
- // if (s.pattern) {
- // input.pattern = s.pattern;
- // }
- // if (s.required) {
- // input.required = true;
- // }
- //
- // if (s.minimum) {
- // input.min = s.minimum;
- // }
- // if (s.maximum) {
- // input.max = s.maximum;
- // }
- input.onchange();
- input.id = getInputId();
- inputCounter++;
- appendChild(container, input, s);
- return parentObject;
- };
-
- renderers["boolean"] = function (container, id, parentObject, propertyProvider, value) {
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var input;
- if (s.required) {
- input = document.createElement("input");
- input.type = "checkbox";
- if (value === true || value !== false && s.default) {
- input.checked = true;
- }
- } else {
- input = document.createElement("select");
- var emptyOption = document.createElement("option");
- var textEmpty = document.createTextNode("");
- textEmpty.value = "";
- appendChild(emptyOption, textEmpty, s);
- appendChild(input, emptyOption, s);
-
- var optionTrue = document.createElement("option");
- var textTrue = document.createTextNode(BrutusinForms.messages["true"]);
- optionTrue.value = "true";
- appendChild(optionTrue, textTrue, s);
- appendChild(input, optionTrue, s);
-
- var optionFalse = document.createElement("option");
- var textFalse = document.createTextNode(BrutusinForms.messages["false"]);
- optionFalse.value = "false";
- appendChild(optionFalse, textFalse, s);
- appendChild(input, optionFalse, s);
-
- if (value === true) {
- input.selectedIndex = 1;
- } else if (value === false) {
- input.selectedIndex = 2;
- }
- }
- input.onchange = function () {
- if (parentObject) {
- parentObject[propertyProvider.getValue()] = getValue(s, input);
- } else {
- data = getValue(s, input);
- }
- onDependencyChanged(schemaId, input);
- };
- input.schema = schemaId;
- input.id = getInputId();
- inputCounter++;
- if (s.description) {
- input.title = s.description;
- }
- input.onchange();
- appendChild(container, input, s);
- };
-
- renderers["oneOf"] = function (container, id, parentObject, propertyProvider, value) {
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var input = document.createElement("select");
- var display = document.createElement("div");
- display.innerHTML = "";
- input.type = "select";
- input.schema = schemaId;
- var noption = document.createElement("option");
- noption.value = null;
- appendChild(input, noption, s);
- for (var i = 0; i < s.oneOf.length; i++) {
- var option = document.createElement("option");
- var propId = schemaId + "." + i;
- var ss = getSchema(propId);
- var textNode = document.createTextNode(ss.title);
- option.value = s.oneOf[i];
- appendChild(option, textNode, s);
- appendChild(input, option, s);
- if (value === undefined || value === null)
- continue;
- if (s.readOnly)
- input.disabled = true;
- if (value.hasOwnProperty("type")) {
- if (ss.hasOwnProperty("properties")) {
- if (ss.properties.hasOwnProperty("type")) {
- var tryit = getSchema(ss.properties.type);
- if (value.type === tryit.enum[0]) {
- input.selectedIndex = i + 1;
- render(null, display, id + "." + (input.selectedIndex - 1), parentObject, propertyProvider, value);
- }
- }
- }
- }
- }
- input.onchange = function () {
- render(null, display, id + "." + (input.selectedIndex - 1), parentObject, propertyProvider, value);
- };
- appendChild(container, input, s);
- appendChild(container, display, s);
-
- };
-
- renderers["object"] = function (container, id, parentObject, propertyProvider, value) {
-
- function createStaticPropertyProvider(propname) {
- var ret = new Object();
- ret.getValue = function () {
- return propname;
- };
- ret.onchange = function (oldName) {
- };
- return ret;
- }
-
- function addAdditionalProperty(current, table, id, name, value, pattern) {
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var tbody = table.tBodies[0];
- var tr = document.createElement("tr");
- var td1 = document.createElement("td");
- td1.className = "add-prop-name";
- var innerTab = document.createElement("table");
- var innerTr = document.createElement("tr");
- var innerTd1 = document.createElement("td");
- var innerTd2 = document.createElement("td");
- var keyForBlank = "$" + Object.keys(current).length + "$";
- var td2 = document.createElement("td");
- td2.className = "prop-value";
- var nameInput = document.createElement("input");
- nameInput.type = "text";
- var regExp;
- if (pattern) {
- regExp = RegExp(pattern);
- }
- nameInput.getValidationError = function () {
- if (nameInput.previousValue !== nameInput.value) {
- if (current.hasOwnProperty(nameInput.value)) {
- return BrutusinForms.messages["addpropNameExistent"];
- }
- }
- if (!nameInput.value) {
- return BrutusinForms.messages["addpropNameRequired"];
- }
- };
- var pp = createPropertyProvider(
- function () {
- if (nameInput.value) {
- if (regExp) {
- if (nameInput.value.search(regExp) !== -1) {
- return nameInput.value;
- }
- } else {
- return nameInput.value;
- }
- }
- return keyForBlank;
- },
- function (oldPropertyName) {
- if (pp.getValue() === oldPropertyName) {
- return;
- }
- if (!oldPropertyName || !current.hasOwnProperty(oldPropertyName)) {
- oldPropertyName = keyForBlank;
- }
- if (current.hasOwnProperty(oldPropertyName) || regExp && pp.getValue().search(regExp) === -1) {
- current[pp.getValue()] = current[oldPropertyName];
- delete current[oldPropertyName];
- }
- });
-
- nameInput.onblur = function () {
- if (nameInput.previousValue !== nameInput.value) {
- var name = nameInput.value;
- var i = 1;
- while (nameInput.previousValue !== name && current.hasOwnProperty(name)) {
- name = nameInput.value + "(" + i + ")";
- i++;
- }
- nameInput.value = name;
- pp.onchange(nameInput.previousValue);
- nameInput.previousValue = nameInput.value;
- return;
- }
- };
- var removeButton = document.createElement("button");
- removeButton.setAttribute('type', 'button');
- removeButton.className = "remove";
- appendChild(removeButton, document.createTextNode("x"), s);
- removeButton.onclick = function () {
- delete current[nameInput.value];
- table.deleteRow(tr.rowIndex);
- nameInput.value = null;
- pp.onchange(nameInput.previousValue);
- };
- appendChild(innerTd1, nameInput, s);
- appendChild(innerTd2, removeButton, s);
- appendChild(innerTr, innerTd1, s);
- appendChild(innerTr, innerTd2, s);
- appendChild(innerTab, innerTr, s);
- appendChild(td1, innerTab, s);
-
- if (pattern !== undefined) {
- nameInput.placeholder = pattern;
- }
-
- appendChild(tr, td1, s);
- appendChild(tr, td2, s);
- appendChild(tbody, tr, s);
- appendChild(table, tbody, s);
- render(null, td2, id, current, pp, value);
-
- if (name) {
- nameInput.value = name;
- nameInput.onblur();
- }
- }
-
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var current = new Object();
- if (!parentObject) {
- data = current;
- } else {
- if (propertyProvider.getValue() || propertyProvider.getValue() === 0) {
- parentObject[propertyProvider.getValue()] = current;
- }
- }
- var table = document.createElement("table");
- table.className = "object";
- var tbody = document.createElement("tbody");
- appendChild(table, tbody, s);
- var propNum = 0;
- if (s.hasOwnProperty("properties")) {
- propNum = s.properties.length;
- for (var prop in s.properties) {
- var tr = document.createElement("tr");
- var td1 = document.createElement("td");
- td1.className = "prop-name";
- var propId = id + "." + prop;
- var propSchema = getSchema(getSchemaId(propId));
- var td2 = document.createElement("td");
- td2.className = "prop-value";
-
- appendChild(tbody, tr, propSchema);
- appendChild(tr, td1, propSchema);
- appendChild(tr, td2, propSchema);
- var pp = createStaticPropertyProvider(prop);
- var propInitialValue = null;
- if (value) {
- propInitialValue = value[prop];
- }
- render(td1, td2, propId, current, pp, propInitialValue);
- }
- }
- var usedProps = [];
- if (s.patternProperties || s.additionalProperties) {
- var div = document.createElement("div");
- appendChild(div, table, s);
- if (s.patternProperties) {
- for (var pattern in s.patternProperties) {
- var patProps = s.patternProperties[pattern];
- var patdiv = document.createElement("div");
- patdiv.className = "add-pattern-div";
- var addButton = document.createElement("button");
- addButton.setAttribute('type', 'button');
- addButton.pattern = pattern;
- addButton.id = id + "[" + pattern + "]";
- addButton.onclick = function () {
- addAdditionalProperty(current, table, this.id, undefined, undefined, this.pattern);
- };
- if (s.maxProperties || s.minProperties) {
- addButton.getValidationError = function () {
- if (s.minProperties && propNum + table.rows.length < s.minProperties) {
- return BrutusinForms.messages["minProperties"].format(s.minProperties);
- }
- if (s.maxProperties && propNum + table.rows.length > s.maxProperties) {
- return BrutusinForms.messages["maxProperties"].format(s.maxProperties);
- }
- };
- }
- if (patProps.description) {
- addButton.title = patProps.description;
- }
- appendChild(addButton, document.createTextNode("Add " + pattern), s);
- appendChild(patdiv, addButton, s);
- if (value) {
- for (var p in value) {
- if (s.properties && s.properties.hasOwnProperty(p)) {
- continue;
- }
- var r = RegExp(pattern);
- if (p.search(r) === -1) {
- continue;
- }
- if (usedProps.indexOf(p) !== -1) {
- continue;
- }
- addAdditionalProperty(current, table, id + "[" + pattern + "]", p, value[p], pattern);
- usedProps.push(p);
- }
- }
- appendChild(div, patdiv, s);
- }
- }
- if (s.additionalProperties) {
- var addPropS = getSchema(s.additionalProperties);
- var addButton = document.createElement("button");
- addButton.setAttribute('type', 'button');
- addButton.onclick = function () {
- addAdditionalProperty(current, table, id + "[*]", undefined);
- };
- if (s.maxProperties || s.minProperties) {
- addButton.getValidationError = function () {
- if (s.minProperties && propNum + table.rows.length < s.minProperties) {
- return BrutusinForms.messages["minProperties"].format(s.minProperties);
- }
- if (s.maxProperties && propNum + table.rows.length > s.maxProperties) {
- return BrutusinForms.messages["maxProperties"].format(s.maxProperties);
- }
- };
- }
- if (addPropS.description) {
- addButton.title = addPropS.description;
- }
- appendChild(addButton, document.createTextNode("Add"), s);
- appendChild(div, addButton, s);
- if (value) {
- for (var p in value) {
- if (s.properties && s.properties.hasOwnProperty(p)) {
- continue;
- }
- if (usedProps.indexOf(p) !== -1) {
- continue;
- }
- addAdditionalProperty(current, table, id + "[\"" + prop + "\"]", p, value[p]);
- }
- }
- }
- appendChild(container, div, s);
- } else {
- appendChild(container, table, s);
- }
- };
- // end of object renderer
- renderers["array"] = function (container, id, parentObject, propertyProvider, value) {
- function addItem(current, table, id, value, readOnly) {
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var tbody = document.createElement("tbody");
- var tr = document.createElement("tr");
- tr.className = "item";
- var td1 = document.createElement("td");
- td1.className = "item-index";
- var td2 = document.createElement("td");
- td2.className = "item-action";
- var td3 = document.createElement("td");
- td3.className = "item-value";
- var removeButton = document.createElement("button");
- removeButton.setAttribute('type', 'button');
- removeButton.className = "remove";
- if (readOnly === true)
- removeButton.disabled = true;
- appendChild(removeButton, document.createTextNode("x"), s);
- var computRowCount = function () {
- for (var i = 0; i < table.rows.length; i++) {
- var row = table.rows[i];
- row.cells[0].innerHTML = i + 1;
- }
- };
- removeButton.onclick = function () {
- current.splice(tr.rowIndex, 1);
- table.deleteRow(tr.rowIndex);
- computRowCount();
- };
- appendChild(td2, removeButton, s);
- var number = document.createTextNode(table.rows.length + 1);
- appendChild(td1, number, s);
- appendChild(tr, td1, s);
- appendChild(tr, td2, s);
- appendChild(tr, td3, s);
- appendChild(tbody, tr, s);
- appendChild(table, tbody, s);
- var pp = createPropertyProvider(function () {
- return tr.rowIndex;
- });
- render(null, td3, id, current, pp, value);
- }
-
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- var itemS = getSchema(s.items);
- var current = new Array();
- if (!parentObject) {
- data = current;
- } else {
- if (propertyProvider.getValue() || propertyProvider.getValue() === 0) {
- parentObject[propertyProvider.getValue()] = current;
- }
- }
- if (propertyProvider) {
- propertyProvider.onchange = function (oldPropertyName) {
- delete parentObject[oldPropertyName];
- parentObject[propertyProvider.getValue()] = current;
- };
- }
- var div = document.createElement("div");
- var table = document.createElement("table");
- table.className = "array";
- appendChild(div, table, s);
- appendChild(container, div, s);
- var addButton = document.createElement("button");
- if (s.readOnly)
- addButton.disabled = true;
- addButton.setAttribute('type', 'button');
- addButton.className = "addItem";
- addButton.getValidationError = function () {
- if (s.minItems && s.minItems > table.rows.length) {
- return BrutusinForms.messages["minItems"].format(s.minItems);
- }
- if (s.maxItems && s.maxItems < table.rows.length) {
- return BrutusinForms.messages["maxItems"].format(s.maxItems);
- }
- if (s.uniqueItems) {
- for (var i = 0; i < current.length; i++) {
- for (var j = i + 1; j < current.length; j++) {
- if (JSON.stringify(current[i]) === JSON.stringify(current[j])) {
- return BrutusinForms.messages["uniqueItems"];
- }
- }
- }
- }
- };
- addButton.onclick = function () {
- addItem(current, table, id + "[#]", null);
- };
- if (itemS.description) {
- addButton.title = itemS.description;
- }
- appendChild(addButton, document.createTextNode(BrutusinForms.messages["addItem"]), s);
- appendChild(div, table, s);
- appendChild(div, addButton, s);
- if (value && value instanceof Array) {
- for (var i = 0; i < value.length; i++) {
- addItem(current, table, id + "[" + i + "]", value[i], s.readOnly);
- }
- }
- appendChild(container, div, s);
- };
- // end of array render
- /**
- * Renders the form inside the the container, with the specified data preloaded
- * @param {type} c container
- * @param {type} data json data
- * @returns {undefined}
- */
- obj.render = function (c, data) {
- container = c;
- initialValue = data;
- var form = document.createElement("form");
- form.className = "brutusin-form";
- form.onsubmit = function (event) {
- return false;
- };
- if (container) {
- appendChild(container, form);
- } else {
- appendChild(document.body, form);
- }
- if (error) {
- var errLabel = document.createElement("label");
- var errNode = document.createTextNode(error);
- appendChild(errLabel, errNode);
- errLabel.className = "error-message";
- appendChild(form, errLabel);
- } else {
- render(null, form, "$", null, null);
- }
- if (dependencyMap.hasOwnProperty("$")) {
- onDependencyChanged("$");
- }
- if (BrutusinForms.postRender) {
- BrutusinForms.postRender(obj);
- }
- };
-
- obj.getRenderingContainer = function () {
- return container;
- };
-
- obj.validate = function () {
- return validate(container);
- };
-
- obj.getData = function () {
- function removeEmptiesAndNulls(object, s) {
- if (s === null) {
- s = SCHEMA_ANY;
- }
- if (s.$ref) {
- s = getDefinition(s.$ref);
- }
- if (object instanceof Array) {
- if (object.length === 0) {
- return null;
- }
- var clone = new Array();
- for (var i = 0; i < object.length; i++) {
- clone[i] = removeEmptiesAndNulls(object[i], s.items);
- }
- return clone;
- } else if (object === "") {
- return null;
- } else if (object instanceof Object) {
- var clone = new Object();
- var nonEmpty = false;
- for (var prop in object) {
- if (prop.startsWith("$") && prop.endsWith("$")) {
- continue;
- }
- var ss = null;
- if (s.hasOwnProperty("properties") && s.properties.hasOwnProperty(prop)) {
- ss = s.properties[prop];
- }
- if (ss === null && s.hasOwnProperty("additionalProperties")) {
- if (typeof s.additionalProperties === 'object') {
- ss = s.additionalProperties;
- }
- }
- if (ss === null && s.hasOwnProperty("patternProperties")) {
- for (var p in s.patternProperties) {
- var r = RegExp(p);
- if (prop.search(r) !== -1) {
- ss = s.patternProperties[p];
- break;
- }
- }
- }
- var value = removeEmptiesAndNulls(object[prop], ss);
- if (value !== null) {
- clone[prop] = value;
- nonEmpty = true;
- }
- }
- if (nonEmpty || s.required) {
- return clone;
- } else {
- return null;
- }
- } else {
- return object;
- }
- }
- if (!container) {
- return null;
- } else {
- return removeEmptiesAndNulls(data, schema);
- }
- };
-
- BrutusinForms.instances[BrutusinForms.instances.length] = obj;
-
- return obj;
-
- function validateDepencyMapIsAcyclic() {
- function dfs(visitInfo, stack, id) {
- if (stack.hasOwnProperty(id)) {
- error = "Schema dependency graph has cycles";
- return;
- }
- stack[id] = null;
- if (visitInfo.hasOwnProperty(id)) {
- return;
- }
- visitInfo[id] = null;
- var arr = dependencyMap[id];
- if (arr) {
- for (var i = 0; i < arr.length; i++) {
- dfs(visitInfo, stack, arr[i]);
- }
- }
- delete stack[id];
- }
- var visitInfo = new Object();
- for (var id in dependencyMap) {
- if (visitInfo.hasOwnProperty(id)) {
- continue;
- }
- dfs(visitInfo, new Object(), id);
- }
- }
-
- function appendChild(parent, child, schema) {
- parent.appendChild(child);
- for (var i = 0; i < BrutusinForms.decorators.length; i++) {
- BrutusinForms.decorators[i](child, schema);
- }
- }
-
- function createPseudoSchema(schema) {
- var pseudoSchema = new Object();
- for (var p in schema) {
- if (p === "items" || p === "properties" || p === "additionalProperties") {
- continue;
- }
- if (p === "pattern") {
- pseudoSchema[p] = new RegExp(schema[p]);
- } else {
- pseudoSchema[p] = schema[p];
- }
-
- }
- return pseudoSchema;
- }
-
- function getDefinition(path) {
- var parts = path.split('/');
- var def = root;
- for (var p in parts) {
- if (p === "0")
- continue;
- def = def[parts[p]];
-
- }
- return def;
- }
-
- function containsStr(array, string) {
- for (var i = 0; i < array.length; i++) {
- if (array[i] == string) {
- return true;
- }
- }
- return false;
- }
-
- function renameRequiredPropeties(schema) {
- if (!schema) {
- return;
- } else if (schema.hasOwnProperty("oneOf")) {
- for (var i in schema.oneOf) {
- renameRequiredPropeties(schema.oneOf[i]);
- }
- } else if (schema.hasOwnProperty("$ref")) {
- var newSchema = getDefinition(schema["$ref"]);
- renameRequiredPropeties(newSchema);
- } else if (schema.type === "object") {
- if (schema.properties) {
- if (schema.hasOwnProperty("required")) {
- if (Array.isArray(schema.required)) {
- schema.requiredProperties = schema.required;
- delete schema.required;
- }
- }
- for (var prop in schema.properties) {
- renameRequiredPropeties(schema.properties[prop]);
- }
- }
- if (schema.patternProperties) {
- for (var pat in schema.patternProperties) {
- var s = schema.patternProperties[pat];
- if (s.hasOwnProperty("type") || s.hasOwnProperty("$ref") || s.hasOwnProperty("oneOf")) {
- renameRequiredPropeties(schema.patternProperties[pat]);
- }
- }
- }
- if (schema.additionalProperties) {
- if (schema.additionalProperties.hasOwnProperty("type") || schema.additionalProperties.hasOwnProperty("oneOf")) {
- renameRequiredPropeties(schema.additionalProperties);
-
- }
- }
- } else if (schema.type === "array") {
- renameRequiredPropeties(schema.items);
- }
- }
-
- function populateSchemaMap(name, schema) {
- var pseudoSchema = createPseudoSchema(schema);
- pseudoSchema["$id"] = name;
- schemaMap[name] = pseudoSchema;
-
- if (!schema) {
- return;
- } else if (schema.hasOwnProperty("oneOf")) {
- pseudoSchema.oneOf = new Array();
- pseudoSchema.type = "oneOf";
- for (var i in schema.oneOf) {
- var childProp = name + "." + i;
- pseudoSchema.oneOf[i] = childProp;
- populateSchemaMap(childProp, schema.oneOf[i]);
- }
- } else if (schema.hasOwnProperty("$ref")) {
- var refSchema = getDefinition(schema["$ref"]);
- if (refSchema) {
- if (schema.hasOwnProperty("title") || schema.hasOwnProperty("description")) {
- var clonedRefSchema = {};
- for (var prop in refSchema) {
- clonedRefSchema[prop] = refSchema[prop];
- }
- if (schema.hasOwnProperty("title")) {
- clonedRefSchema.title = schema.title;
- }
- if (schema.hasOwnProperty("description")) {
- clonedRefSchema.description = schema.description;
- }
- refSchema = clonedRefSchema;
- }
- populateSchemaMap(name, refSchema);
- }
- } else if (schema.type === "object") {
- if (schema.properties) {
- pseudoSchema.properties = new Object();
- for (var prop in schema.properties) {
- var childProp = name + "." + prop;
- pseudoSchema.properties[prop] = childProp;
- var subSchema = schema.properties[prop];
- if (schema.requiredProperties) {
- if (containsStr(schema.requiredProperties, prop)) {
- subSchema.required = true;
- } else {
- subSchema.required = false;
- }
- }
- populateSchemaMap(childProp, subSchema);
- }
- }
- if (schema.patternProperties) {
- pseudoSchema.patternProperties = new Object();
- for (var pat in schema.patternProperties) {
- var patChildProp = name + "[" + pat + "]";
- pseudoSchema.patternProperties[pat] = patChildProp;
- var s = schema.patternProperties[pat];
-
- if (s.hasOwnProperty("type") || s.hasOwnProperty("$ref") ||
- s.hasOwnProperty("oneOf")) {
- populateSchemaMap(patChildProp, schema.patternProperties[pat]);
- } else {
- populateSchemaMap(patChildProp, SCHEMA_ANY);
- }
- }
- }
- if (schema.additionalProperties) {
- var childProp = name + "[*]";
- pseudoSchema.additionalProperties = childProp;
- if (schema.additionalProperties.hasOwnProperty("type") ||
- schema.additionalProperties.hasOwnProperty("oneOf")) {
- populateSchemaMap(childProp, schema.additionalProperties);
- } else {
- populateSchemaMap(childProp, SCHEMA_ANY);
- }
- }
- } else if (schema.type === "array") {
- pseudoSchema.items = name + "[#]";
- populateSchemaMap(pseudoSchema.items, schema.items);
- }
- if (schema.hasOwnProperty("dependsOn")) {
- if (schema.dependsOn === null) {
- schema.dependsOn = ["$"];
- }
- var arr = new Array();
- for (var i = 0; i < schema.dependsOn.length; i++) {
- if (!schema.dependsOn[i]) {
- arr[i] = "$";
- // Relative cases
- } else if (schema.dependsOn[i].startsWith("$")) {
- arr[i] = schema.dependsOn[i];
- // Relative cases
- } else if (name.endsWith("]")) {
- arr[i] = name + "." + schema.dependsOn[i];
- } else {
- arr[i] = name.substring(0, name.lastIndexOf(".")) + "." + schema.dependsOn[i];
- }
- }
- schemaMap[name].dependsOn = arr;
- for (var i = 0; i < arr.length; i++) {
- var entry = dependencyMap[arr[i]];
- if (!entry) {
- entry = new Array();
- dependencyMap[arr[i]] = entry;
- }
- entry[entry.length] = name;
- }
- }
- }
-
- function renderTitle(container, title, schema) {
- if (container) {
- if (title) {
- var titleLabel = document.createElement("label");
- if (schema.type !== "any" && schema.type !== "object" && schema.type !== "array") {
- titleLabel.htmlFor = getInputId();
- }
- var titleNode = document.createTextNode(title + ":");
- appendChild(titleLabel, titleNode, schema);
- if (schema.description) {
- titleLabel.title = schema.description;
- }
- if (schema.required) {
- var sup = document.createElement("sup");
- appendChild(sup, document.createTextNode("*"), schema);
- appendChild(titleLabel, sup, schema);
- titleLabel.className = "required";
- }
- appendChild(container, titleLabel, schema);
- }
- }
- }
-
- function getInputId() {
- return formId + "_" + inputCounter;
- }
-
- function validate(element) {
- var ret = true;
- if (element.hasOwnProperty("getValidationError")) {
- var error = element.getValidationError();
- if (error) {
- BrutusinForms.onValidationError(element, error);
- ret = false;
- } else {
- BrutusinForms.onValidationSuccess(element);
- }
- }
- if (element.childNodes) {
- for (var i = 0; i < element.childNodes.length; i++) {
- if (!validate(element.childNodes[i])) {
- ret = false;
- }
- }
- }
- return ret;
- }
-
- function clear(container) {
- if (container) {
- while (container.firstChild) {
- container.removeChild(container.firstChild);
- }
- }
- }
-
- function render(titleContainer, container, id, parentObject, propertyProvider, value) {
- //console.log(id);
- var schemaId = getSchemaId(id);
- var s = getSchema(schemaId);
- renderInfoMap[schemaId] = new Object();
- renderInfoMap[schemaId].titleContainer = titleContainer;
- renderInfoMap[schemaId].container = container;
- renderInfoMap[schemaId].parentObject = parentObject;
- renderInfoMap[schemaId].propertyProvider = propertyProvider;
- renderInfoMap[schemaId].value = value;
- clear(titleContainer);
- clear(container);
- //console.log(id,s,value);
- var r = renderers[s.type];
- if (r && !s.dependsOn) {
- if (s.title) {
- renderTitle(titleContainer, s.title, s);
- } else if (propertyProvider) {
- renderTitle(titleContainer, propertyProvider.getValue(), s);
- }
- if (!value) {
- if (typeof initialValue !== "undefined" && initialValue !== null) {
- value = getInitialValue(id);
- } else {
- value = s.default;
- }
- }
- r(container, id, parentObject, propertyProvider, value);
- } else if (s.$ref) {
- if (obj.schemaResolver) {
- var cb = function (schemas) {
- if (schemas && schemas.hasOwnProperty(id)) {
- if (JSON.stringify(schemaMap[id]) !== JSON.stringify(schemas[id])) {
- cleanSchemaMap(id);
- cleanData(id);
- populateSchemaMap(id, schemas[id]);
- var renderInfo = renderInfoMap[id];
- if (renderInfo) {
- render(renderInfo.titleContainer, renderInfo.container, id, renderInfo.parentObject, renderInfo.propertyProvider, renderInfo.value);
- }
- }
- }
- BrutusinForms.onResolutionFinished(parentObject);
- };
- BrutusinForms.onResolutionStarted(parentObject);
- obj.schemaResolver([id], obj.getData(), cb);
- }
- }
- }
-
- /**
- * Used in object additionalProperties and arrays
- * @param {type} getValue
- * @param {type} onchange
- * @returns {Object.create.createPropertyProvider.ret}
- */
- function createPropertyProvider(getValue, onchange) {
- var ret = new Object();
- ret.getValue = getValue;
- ret.onchange = onchange;
- return ret;
- }
-
- function getInitialValue(id) {
- var ret;
- try {
- eval("ret = initialValue" + id.substring(1));
- } catch (e) {
- ret = null;
- }
- return ret;
- }
-
- function getValue(schema, input) {
- if (typeof input.getValue === "function") {
- return input.getValue();
- }
- var value;
-
- if (input.tagName.toLowerCase() === "select") {
- value = input.options[input.selectedIndex].value;
- } else {
- value = input.value;
- }
- if (value === "") {
- return null;
- }
- if (schema.type === "integer") {
- value = parseInt(value);
- if (!isFinite(value)) {
- value = null;
- }
- } else if (schema.type === "number") {
- value = parseFloat(value);
- if (!isFinite(value)) {
- value = null;
- }
- } else if (schema.type === "boolean") {
- if (input.tagName.toLowerCase() === "input") {
- value = input.checked;
- if (!value) {
- value = false;
- }
- } else if (input.tagName.toLowerCase() === "select") {
- if (input.value === "true") {
- value = true;
- } else if (input.value === "false") {
- value = false;
- } else {
- value = null;
- }
- }
- } else if (schema.type === "any") {
- if (value) {
- eval("value=" + value);
- }
- }
- return value;
- }
-
- function getSchemaId(id) {
- return id.replace(/\["[^"]*"\]/g, "[*]").replace(/\[\d*\]/g, "[#]");
- }
-
- function getParentSchemaId(id) {
- return id.substring(0, id.lastIndexOf("."));
- }
-
- function getSchema(schemaId) {
- return schemaMap[schemaId];
- }
-
- function cleanSchemaMap(schemaId) {
- for (var prop in schemaMap) {
- if (prop.startsWith(schemaId)) {
- delete schemaMap[prop];
- }
- }
- }
- function cleanData(schemaId) {
- var expression = new Expression(schemaId);
- expression.visit(data, function (data, parent, property) {
- delete parent[property];
- });
- }
-
- function onDependencyChanged(name, source) {
-
- var arr = dependencyMap[name];
- if (!arr || !obj.schemaResolver) {
- return;
- }
- var cb = function (schemas) {
- if (schemas) {
- for (var id in schemas) {
- if (JSON.stringify(schemaMap[id]) !== JSON.stringify(schemas[id])) {
- cleanSchemaMap(id);
- cleanData(id);
- populateSchemaMap(id, schemas[id]);
- var renderInfo = renderInfoMap[id];
- if (renderInfo) {
- render(renderInfo.titleContainer, renderInfo.container, id, renderInfo.parentObject, renderInfo.propertyProvider, renderInfo.value);
- }
- }
- }
- }
- BrutusinForms.onResolutionFinished(source);
- };
- BrutusinForms.onResolutionStarted(source);
- obj.schemaResolver(arr, obj.getData(), cb);
-
-
- }
-
- function Expression(exp) {
- if (exp === null || exp.length === 0 || exp === ".") {
- exp = "$";
- }
- var queue = new Array();
- var tokens = parseTokens(exp);
- var isInBracket = false;
- var numInBracket = 0;
- var sb = "";
- for (var i = 0; i < tokens.length; i++) {
- var token = tokens[i];
- if (token === "[") {
- if (isInBracket) {
- throw ("Error parsing expression '" + exp + "': Nested [ found");
- }
- isInBracket = true;
- numInBracket = 0;
- sb = sb + token;
- } else if (token === "]") {
- if (!isInBracket) {
- throw ("Error parsing expression '" + exp + "': Unbalanced ] found");
- }
- isInBracket = false;
- sb = sb + token;
- queue[queue.length] = sb;
- sb = "";
- } else {
- if (isInBracket) {
- if (numInBracket > 0) {
- throw ("Error parsing expression '" + exp + "': Multiple tokens found inside a bracket");
- }
- sb = sb + token;
- numInBracket++;
- } else {
- queue[queue.length] = token;
- }
- }
- if (i === tokens.length - 1) {
- if (isInBracket) {
- throw ("Error parsing expression '" + exp + "': Unbalanced [ found");
- }
- }
- }
- this.exp = exp;
- this.queue = queue;
- this.visit = function (data, visitor) {
- function visit(name, queue, data, parentData, property) {
- if (data == null) {
- return;
- }
- var currentToken = queue.shift();
- if (currentToken === "$") {
- name = "$";
- var currentToken = queue.shift();
- }
- if (!currentToken) {
- visitor(data, parentData, property);
- } else if (Array.isArray(data)) {
- if (!currentToken.startsWith("[")) {
- throw ("Node '" + name + "' is of type array");
- }
- var element = currentToken.substring(1, currentToken.length - 1);
- if (element.equals("#")) {
- for (var i = 0; i < data.length; i++) {
- var child = data[i];
- visit(name + currentToken, queue.slice(0), child, data, i);
- visit(name + "[" + i + "]", queue.slice(0), child, data, i);
- }
- } else if (element === "$") {
- var child = data[data.length - 1];
- visit(name + currentToken, queue.slice(0), child, data, data.length - 1);
- } else {
- var index = parseInt(element);
- if (isNaN(index)) {
- throw ("Element '" + element + "' of node '" + name + "' is not an integer, or the '$' last element symbol, or the wilcard symbol '#'");
- }
- if (index < 0) {
- throw ("Element '" + element + "' of node '" + name + "' is lower than zero");
- }
- var child = data[index];
- visit(name + currentToken, queue.slice(0), child, data, index);
- }
- } else if ("object" === typeof data) {
- if (currentToken === "[*]") {
- for (var p in data) {
- var child = data[p];
- visit(name + currentToken, queue.slice(0), child, data, p);
- visit(name + "[\"" + p + "\"]", queue.slice(0), child, data, p);
- }
- } else {
- var child;
- if (currentToken.startsWith("[")) {
- var element = currentToken.substring(1, currentToken.length - 1);
- if (element.startsWith("\"") || element.startsWith("'")) {
- element = element.substring(1, element.length() - 1);
- } else {
- throw ("Element '" + element + "' of node '" + name + "' must be a string expression or wilcard '*'");
- }
- name = name + currentToken;
- child = data[element];
- } else {
- if (name.length > 0) {
- name = name + "." + currentToken;
- } else {
- name = currentToken;
- }
- child = data[currentToken];
- }
- visit(name, queue, child, data, currentToken);
- }
- } else if ("boolean" === typeof data
- || "number" === typeof data
- || "string" === typeof data) {
- throw ("Node is leaf but still are tokens remaining: " + currentToken);
- } else {
- throw ("Node type '" + typeof data + "' not supported for index field '" + name + "'");
- }
- }
- visit(this.exp, this.queue, data);
- };
-
- function parseTokens(exp) {
- if (exp === null) {
- return null;
- }
- var ret = new Array();
- var commentChar = null;
- var start = 0;
- for (var i = 0; i < exp.length; i++) {
- if (exp.charAt(i) === '"') {
- if (commentChar === null) {
- commentChar = '"';
- } else if (commentChar === '"') {
- commentChar = null;
- ret[ret.length] = exp.substring(start, i + 1).trim();
- start = i + 1;
- }
- } else if (exp.charAt(i) === '\'') {
- if (commentChar === null) {
- commentChar = '\'';
- } else if (commentChar === '\'') {
- commentChar = null;
- ret[ret.length] = exp.substring(start, i + 1).trim();
- start = i + 1;
- }
- } else if (exp.charAt(i) === '[') {
- if (commentChar === null) {
- if (start !== i) {
- ret[ret.length] = exp.substring(start, i).trim();
- }
- ret[ret.length] = "[";
- start = i + 1;
- }
- } else if (exp.charAt(i) === ']') {
- if (commentChar === null) {
- if (start !== i) {
- ret[ret.length] = exp.substring(start, i).trim();
- }
- ret[ret.length] = "]";
- start = i + 1;
- }
- } else if (exp.charAt(i) === '.') {
- if (commentChar === null) {
- if (start !== i) {
- ret[ret.length] = exp.substring(start, i).trim();
- }
- start = i + 1;
- }
- } else if (i === exp.length - 1) {
- ret[ret.length] = exp.substring(start, i + 1).trim();
- }
- }
- return ret;
- }
- }
- };
- brutusin["json-forms"] = BrutusinForms;
- }());