| // Copyright (c) 2000-2017 Ericsson Telecom AB // |
| // All rights reserved. This program and the accompanying materials are made available under the // |
| // terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at // |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html // |
| /////////////////////////////////////////////////////////////////////////////////////////////////////// |
| function CViewModel_DynamicTable(p_viewmodel, p_options) { |
| "use strict"; |
| |
| /** constructor */ |
| |
| var v_viewmodel = p_viewmodel; |
| var v_options = p_options; |
| if (v_options.iteratorColumnPosition == undefined) { |
| v_options.iteratorColumnPosition = 0; |
| } |
| |
| var v_binder; |
| var v_dataPaths = []; |
| var v_selections = []; |
| |
| var v_currentHeader = []; |
| // the view can use this info so that it can handle iterators in columns |
| var v_headerCounts = []; |
| // we store data so we can easily get the datapath, indexinlist and additional selections when a setdata occures |
| var v_setValueMatrix = []; |
| |
| /** public functions - interface for parent */ |
| |
| this.setSelectionToControl = function(p_selection) { |
| v_selections.push(p_selection); |
| }; |
| |
| this.setReponseDataPath = function(p_index, p_path) { |
| v_dataPaths[p_index] = p_path; |
| }; |
| |
| this.setBinder = function(p_binder) { |
| v_binder = p_binder; |
| }; |
| |
| /** public functions - interface for views */ |
| |
| this.select = function(p_index) { |
| for (var i = 0; i < v_selections.length; ++i) { |
| v_viewmodel.select(v_selections[i], p_index); |
| } |
| if (v_selections.length > 0) { |
| v_binder.notifyChange(true); |
| } |
| }; |
| |
| this.getHeader = function() { |
| var counter = 0; |
| var header = []; |
| for (var i = 0; i < v_headerCounts.length; ++i) { |
| for (var j = 0; j < v_headerCounts[i]; ++j) { |
| var nextHeaderIndex = header.length; |
| header.push({ |
| "heading": v_currentHeader[nextHeaderIndex], |
| "elementIndex": counter |
| }); |
| } |
| ++counter; |
| } |
| return header; |
| }; |
| |
| this.getName = function() { |
| if (v_options.name != undefined) { |
| return v_options.name; |
| } else { |
| return v_viewmodel.getRequestFromPath(v_dataPaths[0]).getData.element; |
| } |
| }; |
| |
| this.getTable = function() { |
| var tableData = []; |
| var separators = []; |
| |
| v_setValueMatrix = []; |
| v_currentHeader = []; |
| v_headerCounts = []; |
| |
| var response = v_viewmodel.getResponseElement(v_dataPaths[0]); |
| if (response != undefined && response.list != undefined) { |
| // we store the previous row so we can determine if a separator is needed (an empty list will work in the beginning) |
| var previousRow = []; |
| // we will only create the header when we calculate the first row |
| var first = true; |
| for (var i = 0; i < response.list.length; ++i) { |
| var listElement = response.list[i]; |
| var row = []; |
| var setValueMatrixRow = []; |
| fillRow(i, listElement, row, setValueMatrixRow, first); |
| tableData.push(row); |
| v_setValueMatrix.push(setValueMatrixRow); |
| |
| // if we want separators, we add it, if the currect data in the row is not the same as in the previous row |
| if (v_options.separateBy != undefined) { |
| var val; |
| if (row[v_options.separateBy] != undefined) { |
| val = row[v_options.separateBy].val; |
| } |
| var prevVal; |
| if (previousRow[v_options.separateBy] != undefined) { |
| prevVal = previousRow[v_options.separateBy].val |
| } |
| if (val != prevVal) { |
| separators.push({ |
| "index": i, |
| "text": row[v_options.separateBy].val |
| }); |
| } |
| } |
| |
| previousRow = row; |
| first = false; |
| } |
| } |
| |
| if (v_options.transposeTable) { |
| tableData = transpose(tableData); |
| v_setValueMatrix = transpose(v_setValueMatrix); |
| } |
| |
| if (v_options.iteratorIsHeader && tableData[0] != undefined) { |
| v_currentHeader = []; |
| for (var i = 0; i < tableData[0].length; ++i) { |
| v_currentHeader.push(tableData[0][i].val); |
| } |
| tableData.shift(); |
| for (var i = 0; i < v_currentHeader.length; ++i) { |
| v_headerCounts[i] = 1; |
| } |
| |
| while (v_headerCounts.length > v_currentHeader.length) { |
| v_headerCounts.pop(); |
| } |
| |
| v_setValueMatrix.shift(); |
| } |
| |
| return { |
| "table": tableData, |
| "selection": getSelectionIndexes(response), |
| "separators": separators |
| }; |
| }; |
| |
| this.setValue = function(p_row, p_col, p_value, p_callback, p_additionalData) { |
| if (v_setValueMatrix[p_row] != undefined && v_setValueMatrix[p_row][p_col] != undefined) { |
| var dataPath = v_setValueMatrix[p_row][p_col].dataPath; |
| // they can be undefined, but that is ok |
| var indexInList = v_setValueMatrix[p_row][p_col].indexInList; |
| var additionalSelections = v_setValueMatrix[p_row][p_col].additionalSelections; |
| v_viewmodel.setResponseElement(dataPath, p_value, indexInList, additionalSelections, p_callback, p_additionalData); |
| } |
| }; |
| |
| /** private functions */ |
| |
| function fillRow(rowIndex, listElement, row, setValueRow, first) { |
| // the number of elements processed, used to tell whether we have to insert the iterator now (we do not want to isert it in the middle of an iterator) |
| var elementsProcessed = 0; |
| var request = v_viewmodel.getRequestFromPath(v_dataPaths[0]);; |
| |
| if (first) { |
| // we only use the request in the header creation |
| //request = v_viewmodel.getRequestFromPath(v_dataPaths[0]); |
| tryToInsertIteratorHeader(request); |
| } |
| |
| if (tryToInsertIteratorValue(rowIndex, row, setValueRow, listElement, elementsProcessed)) { |
| ++elementsProcessed; |
| } |
| |
| if (v_dataPaths.length > 1) { |
| // if there are more than one data connections, than they represent the columns |
| fillRowMultipleDataPaths(rowIndex, listElement, row, setValueRow, first, request, elementsProcessed); |
| } else if (listElement.node.childVals != undefined) { |
| // the columns will be the children |
| fillRowOneDataPath(rowIndex, listElement, row, setValueRow, first, request, elementsProcessed); |
| } else { |
| // nothing to do |
| } |
| } |
| |
| function createListFromChildVals(list) { |
| var value = []; |
| for (var i = 0; i < list.length; ++i) { |
| value.push(list[i].node.val); |
| } |
| return {"val": [value], "isWritable": false}; |
| } |
| |
| function fillRowOneDataPath(rowIndex, listElement, row, setValueRow, first, request, elementsProcessed) { |
| // we do not support manipulation and iterators in columns in this case |
| for (var i = 0; i < listElement.node.childVals.length; ++i) { |
| tryToInsertIteratorHeader(request) |
| if (tryToInsertIteratorValue(rowIndex, row, setValueRow, listElement, elementsProcessed)) { |
| ++elementsProcessed; |
| } |
| |
| var childDataPath = mcopy(v_dataPaths[0]); |
| childDataPath.push(i); |
| if (first) { |
| if (request.getData.children != undefined && request.getData.children[i] != undefined) { |
| v_currentHeader.push(getHeaderValue(request.getData.children[i].getData.element, v_headerCounts.length)); |
| } else { |
| v_currentHeader.push(""); |
| } |
| v_headerCounts.push(1); |
| } |
| |
| if (listElement.node.childVals[i].node != undefined) { |
| if (listElement.node.childVals[i].node.tp == 0) { |
| row.push({"val": undefined, "isWritable": false, "tp": 0}); |
| } else { |
| row.push({"val": listElement.node.childVals[i].node.val, "isWritable": listElement.node.childVals[i].node.tp > 0, "tp": listElement.node.childVals[i].node.tp}); |
| } |
| } else if (listElement.node.childVals[i].list != undefined) { |
| row.push(createListFromChildVals(listElement.node.childVals[i].list)); |
| } else { |
| continue; |
| } |
| |
| setValueRow.push({ |
| "dataPath": childDataPath, |
| "additionalSelections": [rowIndex] |
| }); |
| ++elementsProcessed; |
| } |
| } |
| |
| function fillRowMultipleDataPaths(rowIndex, listElement, row, setValueRow, first, p_request, elementsProcessed) { |
| for (var i = 1; i < v_dataPaths.length; ++i) { |
| var request = p_request; |
| |
| tryToInsertIteratorHeader(p_request) |
| if (tryToInsertIteratorValue(rowIndex, row, setValueRow, listElement, elementsProcessed)) { |
| ++elementsProcessed; |
| } |
| |
| var found = true; |
| var responseNode = listElement; |
| |
| var responseList = []; |
| var headerInfo; |
| // we try to find the data belonging to the current iterator value and the current datapath by going down the response tree |
| for (var j = v_dataPaths[0].length; j < v_dataPaths[i].length; ++j) { |
| if (responseNode.node != undefined && responseNode.node.childVals != undefined && responseNode.node.childVals[v_dataPaths[i][j]] != undefined) { |
| // if the currently examined response is a node and it has the correct child |
| responseNode = responseNode.node.childVals[v_dataPaths[i][j]]; |
| if (request != undefined && request.getData.children != undefined) request = request.getData.children[v_dataPaths[i][j]]; |
| } else if (responseNode.list != undefined) { |
| if (request != undefined && request.getData.selection != undefined && request.getData.selection[0] != undefined) { |
| responseNode = responseNode.list[request.getData.selection[0]].node.childVals[v_dataPaths[i][j]]; |
| if (request.getData.children != undefined) request = request.getData.children[v_dataPaths[i][j]]; |
| } else if (request != undefined && request.getData.selectionValues != undefined) { |
| responseNode = responseNode.list[0].node.childVals[v_dataPaths[i][j]]; |
| if (request.getData.children != undefined) request = request.getData.children[v_dataPaths[i][j]]; |
| } else { |
| headerInfo = responseNode; |
| // if the currently examined response is a list |
| for (var k = 0; found && k < responseNode.list.length; ++k) { |
| var responseNodeInList = responseNode.list[k]; |
| // we go deeper in the tree util we find each element that belongs to the datapath |
| for (var l = j; found && l < v_dataPaths[i].length; ++l) { |
| if (responseNodeInList.node != undefined && responseNodeInList.node.childVals != undefined && responseNodeInList.node.childVals[v_dataPaths[i][l]] != undefined && responseNodeInList.node.childVals[v_dataPaths[i][l]].node != undefined) { |
| // if the currently examined response is a node and it has the correct child |
| responseNodeInList = responseNodeInList.node.childVals[v_dataPaths[i][l]]; |
| } else if (responseNodeInList.node != undefined && responseNodeInList.node.childVals != undefined && responseNodeInList.node.childVals[v_dataPaths[i][l]] != undefined && responseNodeInList.node.childVals[v_dataPaths[i][l]].list != undefined) { |
| responseNodeInList = responseNodeInList.node.childVals[v_dataPaths[i][l]]; |
| } else { |
| found = false; |
| } |
| if (request != undefined && request.getData.children != undefined) request = request.getData.children[v_dataPaths[i][l]]; |
| } |
| |
| if (found) { |
| responseList[k] = responseNodeInList; |
| } |
| } |
| |
| // we no longer need the response node, we either found the correct answer or failed |
| responseNode = undefined; |
| break; |
| } |
| } else { |
| found = false; |
| break; |
| } |
| } |
| |
| if (found) { |
| // we do not want to find a list element |
| if (responseNode != undefined) { |
| // we found a single node (that can also be a list... but the path points to it instead of a descendant) |
| var value = getValue(responseNode, i); |
| row.push(value); |
| setValueRow.push({ |
| "dataPath": v_dataPaths[i], |
| "additionalSelections": [rowIndex] |
| }); |
| ++elementsProcessed; |
| if (first) { |
| v_currentHeader.push(getHeaderValue(v_viewmodel.getRequestFromPath(v_dataPaths[i]).getData.element, v_headerCounts.length)); |
| v_headerCounts.push(1); |
| } |
| } else if (responseList != undefined) { |
| // we found the children of some list element |
| for (var j = 0; j < responseList.length; ++j) { |
| var value = getValue(responseList[j], i); |
| row.push(value); |
| setValueRow.push({ |
| "dataPath": v_dataPaths[i], |
| "additionalSelections": [rowIndex, j] |
| }); |
| ++elementsProcessed; |
| if (first) { |
| v_currentHeader.push(getHeaderValue(headerInfo.list[j].node.val, v_headerCounts.length)); |
| } |
| } |
| if (first) { |
| v_headerCounts.push(responseList.length); |
| } |
| } |
| } |
| } |
| } |
| |
| function tryToInsertIteratorHeader(request) { |
| if (v_options.iteratorColumnPosition == v_headerCounts.length) { |
| v_currentHeader.push(getHeaderValue(request.getData.element, v_headerCounts.length)); |
| v_headerCounts.push(1); |
| } |
| } |
| |
| function tryToInsertIteratorValue(rowIndex, row, setValueRow, listElement, elementsProcessed) { |
| if (v_options.iteratorColumnPosition == elementsProcessed) { |
| row.push(getValue(listElement, 0)); |
| setValueRow.push({ |
| "dataPath": v_dataPaths[0], |
| "indexInList": rowIndex |
| }); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| function getHeaderValue(value, headerIndex) { |
| if (v_options.header == undefined || v_options.header[headerIndex] == undefined) { |
| return replaceValueInText(value, "#"); |
| } else { |
| return replaceValueInText(value, v_options.header[headerIndex]); |
| } |
| } |
| |
| function getValue(content, dataPathIndex) { |
| var value; |
| if (content.node != undefined) { |
| var node = content.node; |
| if (node.tp == 0) { |
| value = {"val": undefined, "isWritable": false, "tp": 0}; |
| } else if (v_dataPaths.length > 1 && v_options.dataManipulator != undefined && v_options.dataManipulator[dataPathIndex] != undefined) { |
| if (v_options.dataManipulator[dataPathIndex].text != undefined) { |
| value = {"val": replaceValueInText(node.val, v_options.dataManipulator[dataPathIndex].text), "isWritable": node.tp > 0, "tp": node.tp}; |
| } else if (v_options.dataManipulator[dataPathIndex].excludeFromDisplay === true) { |
| value = undefined; |
| } else { |
| value = {"val": node.val, "isWritable": node.tp > 0, "tp": node.tp}; |
| } |
| } else { |
| value = {"val": node.val, "isWritable": node.tp > 0, "tp": node.tp}; |
| } |
| } else if (content.list != undefined) { |
| value = createListFromChildVals(content.list); |
| } |
| |
| return value; |
| } |
| |
| function replaceValueInText(p_value, p_text) { |
| var text = mcopy(p_text); |
| for (var i = text.length - 1; i >= 0; --i) { |
| // replace ## with # |
| if (text[i] == "#" && i != 0 && text[i-1] == "#") { |
| text = text.splice(i, 1); |
| --i; |
| } else if (text[i] == "#") { |
| text = text.splice(i, 1, p_value); |
| } |
| } |
| return text; |
| } |
| |
| function getSelectionIndexes(response) { |
| if (v_selections[0] != undefined && response != undefined && response.list != undefined) { |
| if (v_selections[0].selectionValues != undefined) { |
| var selection = []; |
| for (var i = 0; i < v_selections[0].selectionValues.length; ++i) { |
| for (var j = 0; j < response.list.length; ++j) { |
| if (v_selections[0].selectionValues[i] == response.list[j].node.val) { |
| selection.push(j); |
| } |
| } |
| } |
| return selection; |
| } else { |
| return v_selections[0].selection; |
| } |
| } else { |
| return undefined; |
| } |
| } |
| } |
| |
| CViewModel_DynamicTable.getHelp = function() { |
| return "A table viewmodel. The first data connection must be a list which defines the rows of the table.\nIf more connections are given, they will represent the columns of the table.\n"; |
| }; |
| |
| CViewModel_DynamicTable.providesInterface = function() { |
| return ["select", "getName", "getHeader", "getTable", "setValue"]; |
| }; |
| |
| CViewModel_DynamicTable.getCustomDataSchema = function() { |
| return { |
| "$schema": "http://json-schema.org/draft-04/schema#", |
| "title": "Custom data for CViewModel_DynamicTable", |
| "type": "object", |
| "properties": { |
| "name": { |
| "description": "The title of the table", |
| "type": "string" |
| }, |
| "header": { |
| "description": "The header of the table. It should have as many elements as the number of columns. Use # to refer to element name of the request (or in case of iterators, the parent request's response) that will be in the corresponding column.", |
| "type": "array", |
| "format": "table", |
| "items": { |
| "type": "string", |
| "title": "heading", |
| "default": "#" |
| } |
| }, |
| "iteratorColumnPosition": { |
| "description": "Insert the data from the connected iterator to this column (-1 if the table should not contain the values of the iterator).", |
| "type": "integer", |
| "default": -1, |
| "min": -1 |
| }, |
| "iteratorIsHeader": { |
| "description": "Whether the values from the iterator will be used as the header.", |
| "type": "boolean", |
| "format": "checkbox", |
| "default": true |
| }, |
| "transposeTable": { |
| "description": "Whether the table is transposed.", |
| "type": "boolean", |
| "format": "checkbox", |
| "default": true |
| }, |
| "separateBy": { |
| "description": "The index of the column by which the data will be separated.", |
| "type": "integer", |
| "default": 0, |
| "min": 0 |
| }, |
| "dataManipulator": { |
| "descriptor": "A list of rules that can manipulate the data obtained from the dataconnections of the table. It will only be processed if more than one dataConnection exists.", |
| "type": "array", |
| "format": "tabs", |
| "items": { |
| "oneOf": [ |
| { |
| "type": "object", |
| "title": "noManipulation", |
| "properties": {}, |
| "additionalProperties": false |
| }, |
| { |
| "type": "object", |
| "title": "text", |
| "properties": { |
| "text": { |
| "description": "The text to be inserted into the table. The data of this connection can be referred to as # (to escape it, use two hashmarks). If omitted, only the data will be inserted.", |
| "type": "string" |
| } |
| }, |
| "additionalProperties": false, |
| "required": ["text"] |
| }, |
| { |
| "type": "object", |
| "title": "excludeFromDisplay", |
| "properties": { |
| "excludeFromDisplay": { |
| "description": "To exclude the data from this dataconnection from being inserted into the table as a column. (Not applicable for the iterator connection, use iteratorColumnPosition = -1 instead.)", |
| "type": "boolean", |
| "format": "checkbox", |
| "default": true |
| } |
| }, |
| "additionalProperties": false, |
| "required": ["excludeFromDisplay"] |
| } |
| ] |
| } |
| } |
| }, |
| "additionalProperties": false |
| }; |
| }; |
| |
| CViewModel_DynamicTable.expectsConnection = function() { |
| var dataConnections = [ |
| { |
| "valueType": ["charstringlistType", "integerlistType", "floatlistType"] |
| }, |
| { |
| "childOfDataConnection": 0, |
| "multiple": true, |
| "optional": true |
| } |
| ]; |
| |
| var selectionConnections = [ |
| { |
| "dataElementOfDataConnection": 0, |
| "multiple": true, |
| "optional": true |
| } |
| ]; |
| |
| return { |
| "dataConnections": dataConnections, |
| "selectionConnections": selectionConnections |
| }; |
| }; |
| |
| //# sourceURL=CustomizableApp\ViewModels\ViewModel_DynamicTable.js |