blob: 4e054006a45e1ffb049bd1a9121ab04143202fa5 [file] [log] [blame]
// 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