UNPKG

fenix-ui-visualization-box

Version:
1,629 lines (1,193 loc) 80.3 kB
define([ "loglevel", "jquery", "underscore", "underscore.string", "../config/config", "../config/errors", "../config/events", "./utils", "fenix-ui-filter-utils", "fenix-ui-metadata-viewer", "../html/template.hbs", "../html/filter-filter-template.hbs", "../html/filter-map-template.hbs", "../html/filter-aggregation-template.hbs", "../html/steps/step-filter.hbs", "../html/steps/step-map.hbs", "../html/steps/step-metadata.hbs", "fenix-ui-dropdown", "../config/right-menu-model", "../config/tabs/map-earthstat-layers", //for map backtab "../nls/labels", "fenix-ui-bridge", "fenix-ui-reports", "swiper", "amplify-pubsub" ], function (log, $, _, _str, C, ERR, EVT, BoxUtils, Utils, MetadataViewer, Template, FilterFilterTemplate, FilterMapTemplate, FilterAggregationTemplate, StepFilterTemplate, StepMapTemplate, StepMetadataTemplate, JsonMenu, menuModel, mapEarthstatLayers, i18nLabels, Bridge, Report, Swiper, amplify) { 'use strict'; var steps = { filter: StepFilterTemplate, map: StepMapTemplate, metadata: StepMetadataTemplate }, s = { BOX: "[data-role='box']", CONTENT_READY: "[data-content='ready']", RIGHT_MENU: "[data-role='right-menu']", FLIP_CONTAINER: "[data-role='flip-container']", FLIP_BUTTONS: "[data-action='flip']", FRONT_CONTENT: "[data-role='front-content']", FRONT_TOOLBAR: "[data-role='front-toolbar']", BACK_TOOLBAR: "[data-role='back-toolbar']", PROCESS_STEPS: "[data-role='process-steps']", PROCESS_DETAILS: "[data-role='process-details']", BACK_CONTENT: "[data-role='back-content']", BOX_TITLE: "[data-role='box-title']", QUERY_BUTTON: "[data-action='query']", ERROR_TEXT: "[data-role='error-text']", EMPTY_FILTER_BUTTON: "[data-content='empty'] [data-action='filter'], [data-content='error'] [data-action='filter']", BACK_FILTER_ERRORS: "[data-role='filter-error']", FILTER_AGGREGATION_TEMPLATE: "[data-role='filter-aggregation-template']", FILTER_FILTER_TEMPLATE: "[data-role='filter-filter-template']", FILTER_MAP_TEMPLATE: "[data-role='filter-map-template']", ROWS_SWIPER: "[data-role='filter-rows-swiper']", BTN_SIDEBAR: "[data-action='show-back-sidebar']", SIDEBAR: "[data-role='back-sidebar']", FRONT_FACE: "[data-face='front']", BACK_FACE: "[data-face='back']", OTHER_CONTENT: "[data-content='empty'], [data-content='error'], [data-content='huge'], [data-content='toomany']" }, pluginFolder = "./tabs/"; /* API */ /** * Constructor * @param {Object} obj * @return {Object} box instance */ function Box(obj) { log.info("Create box"); log.info(obj); //import css require("../css/fenix-ui-visualization-box.css"); require("../css/sandboxed-fenix-ui-visualization-box.css"); //Extend instance with obj and $el $.extend(true, this, C, {initial: obj || {}, $el: $(obj.el)}); var valid = this._validateInput(); if (valid === true) { this._initState(); this._initVariables(); this._initObj(); this._setStatus("loading"); this._renderMenu(); this._bindEventListeners(); this._initTabSources(); this.valid = true; this._getModelInfo(); this._reactToModelStatus(); return this; } else { this.valid = false; log.error("Impossible to create visualization box"); log.error(valid); this._setObjState("error", valid); } } /** * Set box status * @param {Object} obj * @return {Object} box instance */ Box.prototype.render = function (obj) { log.info("Render model for box [" + this.id + "]:"); log.info(obj); $.extend(true, this, obj); this._reactToModelStatus(); }; /** * Dispose box * @return {null} */ Box.prototype.dispose = function () { this._dispose(); log.info("Box [" + this.id + "] disposed"); }; /** * Set box status * @param {String} status * @return {null} */ Box.prototype.setStatus = function (status) { this._setStatus(status); }; /** * Show a box's tab * @param {String} tab. tab'sid * @param {Object} opts. Options passed to tab instance * @return {null} */ Box.prototype.showTab = function (tab, opts) { this._showFrontTab(tab, opts); }; /** * Set box width * @param {String} size * @return {null} */ Box.prototype.setSize = function (size) { this._setSize(size); }; /** * Flip the visualization box * @param {String} face * @return {null} */ Box.prototype.flip = function (face) { return this._flip(face); }; /** * pub/sub * @return {Object} component instance */ Box.prototype.on = function (channel, fn, context) { var _context = context || this; if (!this.channels[channel]) { this.channels[channel] = []; } this.channels[channel].push({context: _context, callback: fn}); return this; }; /** * get box state * @return {Object} box state */ Box.prototype.getState = function () { return this.state; }; Box.prototype.setTitle = function (title) { log.info("Set box title: " + title); this._updateBoxTitle(title); }; /* Internal fns*/ Box.prototype._validateInput = function () { var valid = true, errors = []; //Check if box has a valid id if (!this.id) { window.fx_vis_box_id >= 0 ? window.fx_vis_box_id++ : window.fx_vis_box_id = 0; this.id = "fx-box-" + window.fx_vis_box_id; this._setObjState("id", this.id); log.info("Set box id: " + this.id); } //Check if $el exist if (this.$el.length === 0) { errors.push({code: ERR.MISSING_CONTAINER}); log.warn("Impossible to find box container"); } return errors.length > 0 ? errors : valid; }; Box.prototype._initVariables = function () { this.front_tab_instances = {}; this.back_tab_instances = {}; this.serviceProvider = this.initial.serviceProvider; //console.log(this.serviceProvider); this.bridge = new Bridge({ environment: this._getObjState("environment"), cache: this._getObjState("cache"), serviceProvider: this.serviceProvider }); this.report = new Report({ environment: this._getObjState("environment"), cache: this._getObjState("cache"), serviceProvider: this.serviceProvider }); }; Box.prototype._initObj = function () { //Inject box blank template var $html = $(Template($.extend(true, {}, this.getState(), this._getObjState("nls")))); this.$el.html($html); //pub/sub this.channels = {}; //this._setObjState("hasMenu", this.$el.find(s.RIGHT_MENU).length > 0); this.hasMenu = this.$el.find(s.RIGHT_MENU).length > 0; //set flip side this.flip(this._getObjState("face")); //step process list this.$processSteps = this.$el.find(s.PROCESS_STEPS); this.$processStepDetails = this.$el.find(s.PROCESS_DETAILS); this.$boxTitle = this.$el.find(s.BOX_TITLE); }; Box.prototype._initState = function () { var lang = this.initial.lang || C.lang; lang = lang.toLowerCase(); this._setObjState("lang", lang); //template options this._setObjState("hideToolbar", !!this.initial.hideToolbar); this._setObjState("hideMenu", !!this.initial.hideMenu); this._setObjState("hideMetadataButton", !!this.initial.hideMetadataButton); this._setObjState("hideRemoveButton", !!this.initial.hideRemoveButton); this._setObjState("hideDownloadButton", !!this.initial.hideDownloadButton); this._setObjState("hideCloneButton", !!this.initial.hideCloneButton); this._setObjState("hideFlipButton", !!this.initial.hideFlipButton); this._setObjState("hideMinimizeButton", !!this.initial.hideMinimizeButton); //tabs this._setObjState("tabStates", this.initial.tabStates || {}); this._setObjState("tabOptions", this.initial.tabOptions || {}); this._setObjState("tab", this.initial.tab); this._setObjState("tabConfig", this.initial.tabConfig || C.tabConfig); //flip side this._setObjState("face", this.initial.face || C.face); this._setObjState("faces", this.initial.faces || C.faces); //resource process steps this._setObjState("model", this.initial.model); this._setObjState("version", this.initial.version ? this.initial.version : undefined); this._setObjState("values", this.initial.values); this._setObjState("process", this.initial.process || []); this._setObjState("uid", this.initial.uid || BoxUtils.getNestedProperty("metadata.uid", this._getObjState("model"))); this._setObjState("size", this.initial.size || C.size); this._setObjState("status", this.initial.status || C.status); this._setObjState("environment", this.initial.environment); //data validation this._setObjState("maxDataSize", this.initial.maxDataSize || C.maxDataSize); this._setObjState("minDataSize", this.initial.minDataSize || C.minDataSize); this._setObjState("cache", typeof this.initial.cache === "boolean" ? this.initial.cache : C.cache); // back filter values this._setObjState("backFilter", this.initial.backFilter); this._setObjState("backMap", this.initial.backMap); var loadResourceServiceQueryParams = $.extend(true, this.initial.loadResourceServiceQueryParams || C.loadResourceServiceQueryParams, { language: this._getObjState("lang").toUpperCase() !== "EN" ? "EN," + this._getObjState("lang").toUpperCase() : "EN" }); this._setObjState("loadResourceServiceQueryParams", loadResourceServiceQueryParams); this._setObjState("menuModel", this.initial.menu || menuModel); this._setObjState("title", this.initial.title || this._getModelTitle); this._setObjState("showFilter", typeof this.initial.showFilter === "boolean" ? this.initial.showFilter : false); this._setObjState("nls", $.extend(true, {}, i18nLabels[this._getObjState("lang")], this.initial.nls)); }; Box.prototype._setObjState = function (key, val) { BoxUtils.assign(this.state, key, val); return val; }; Box.prototype._getObjState = function (path) { return BoxUtils.getNestedProperty(path, this.getState()); }; Box.prototype._reactToModelStatus = function (s) { log.info("React to model status: "); log.info(s); //reset error this._setObjState("error", null); var status = s || this._getModelStatus(); log.info("Status found: "); log.info(status); switch (status) { case 'ready' : this._getModelInfo(); this._renderBox(); break; case 'empty' : this.setStatus("empty"); break; case 'huge' : this.setStatus("huge"); break; case 'toomany' : this.setStatus("toomany"); break; case 'no_model' : this.setStatus("loading"); break; case 'to_load' : this._loadResource() .then( _.bind(this._onLoadResourceSuccess, this), _.bind(this._onLoadResourceError, this)); break; case "missing_metadata": this._loadResourceMetadata() .then( _.bind(this._loadResourceMetadataSuccess, this), _.bind(this._loadResourceMetadataError, this)); break; case "to_filter": this._forceModelFilter(); break; default : this.setStatus("error"); break; } }; Box.prototype._getModelStatus = function () { var uid = this._getObjState("uid"), process = this._getObjState("process"), version = this._getObjState("version"), model = this._getObjState("model"), metadata = this._getObjState("model.metadata"), resourceType = this._getObjState("resourceRepresentationType"); if (_.isEmpty(metadata)) { return 'missing_metadata'; } if (resourceType === 'dataset') { if (model) { if (typeof model !== 'object') { return 'error'; } if (!model.size || model.size === 0) { return 'empty'; } if (!Array.isArray(model.data)) { return 'to_filter'; } if (Array.isArray(model.data) && model.data.length <= this._getObjState("minDataSize")) { return 'empty'; } // This line changes due to changes on d3p if (model.data.length > this._getObjState("maxDataSize")) { return 'huge'; } if (Array.isArray(model.data) && model.data.length > 0) { return 'ready'; } } } if (resourceType === 'geographic') { var dsd = this._getObjState("model.metadata.dsd"), workspace = dsd.workspace, layerName = dsd.layerName; if (workspace && layerName) { return 'ready' } } this._setObjState("error", {code: ERR.RESOURCE_STATUS}); return 'error'; }; Box.prototype._loadResource = function (p) { log.info("Loading FENIX resource"); this.setStatus("loading"); this._setObjState("showFilter", true); var queryParams = this._getObjState("loadResourceServiceQueryParams"), process = Array.isArray(p) ? p : []; this._setObjState("process", process.slice(0)); return this.bridge.getProcessedResource({ body: process, uid: this._getObjState("uid"), version: this._getObjState("version"), params: $.extend(true, {}, queryParams) }); }; Box.prototype._onLoadResourceError = function (e) { log.error("Impossible to load resource"); log.error(e); if (e.responseText == '\"Result limit exceeded exception\"') { this._setObjState("toomany", {code: ERR.LOAD_RESOURCE, filter: true}); this._setStatus("toomany"); return; } log.error({code: ERR.LOAD_RESOURCE}); this._setObjState("error", {code: ERR.LOAD_RESOURCE, filter: true}); this._setStatus("error"); }; Box.prototype._onLoadResourceSuccess = function (resource) { log.info("Load resource success"); this._disposeBoxFaces(); this._updateModel(resource); this._getModelInfo(); this.setStatus("ready"); this._reactToModelStatus(); }; Box.prototype._updateModel = function (resource) { var model = this._getObjState("model") || {}, newMetadata = BoxUtils.getNestedProperty("metadata", resource), newDsd = BoxUtils.getNestedProperty("dsd", newMetadata) || {}, newData = BoxUtils.getNestedProperty("data", resource), newSize = BoxUtils.getNestedProperty("size", resource); var dsdWithoutRid = _.without(Object.keys(newDsd), "rid"); //if metadata exists updated only dsd if (dsdWithoutRid.length > 0) { BoxUtils.assign(model, "metadata.dsd", newDsd); } if (Array.isArray(newData)) { BoxUtils.assign(model, "data", newData); } if (model.size !== newSize) { BoxUtils.assign(model, "size", newSize); } this._setObjState("model", model); }; //preload resource info Box.prototype._loadResourceMetadata = function () { log.info("Loading FENIX resource metadata"); this.setStatus("loading"); var queryParams = this._getObjState("loadResourceServiceQueryParams"); return this.bridge.getMetadata({ uid: this._getObjState("uid"), version: this._getObjState("version"), params: $.extend(queryParams, {dsd: true, full: true}) }); }; Box.prototype._loadResourceMetadataError = function () { log.error("Impossible to load resource"); log.error({code: ERR.LOAD_METADATA}); this._setObjState("error", {code: ERR.LOAD_METADATA}); this._setStatus("error"); }; Box.prototype._loadResourceMetadataSuccess = function (metadata) { log.info("Load resource metadata success"); log.info(metadata); this._setObjState("model", {metadata: metadata}); this._checkResourceType(); }; Box.prototype._checkResourceType = function () { log.info("Check Resource type"); this._getModelInfo(); var resourceType = this._getObjState("resourceRepresentationType"); log.info("Check resource type: " + resourceType); switch (resourceType) { case "dataset" : var datasources = BoxUtils.getNestedProperty("metadata.dsd.datasources", this._getObjState("model")); if (_.isArray(datasources) && datasources.length > 0) { if (this._getObjState("process").length > 0) { log.info("Process present. Load resource instead of fetch()"); this._loadResource(this._getObjState("process")) .then( _.bind(this._onLoadResourceSuccess, this), _.bind(this._onLoadResourceError, this)); } else { this._fetchResource().then( _.bind(this._fetchResourceSuccess, this), _.bind(this._fetchResourceError, this) ); } } else { log.error({code: ERR.UNKNOWN_RESOURCE_TYPE}); this._setObjState("error", {code: ERR.MISSING_DATASOURCES}); this._setStatus("error"); } break; case "geographic" : this.setStatus("ready"); this._reactToModelStatus(); break; default : log.error({code: ERR.UNKNOWN_RESOURCE_TYPE}); this._setObjState("error", {code: ERR.UNKNOWN_RESOURCE_TYPE}); this._setStatus("error") } }; Box.prototype._fetchResource = function () { log.info("Fetching FENIX resource"); this.setStatus("loading"); this._setObjState("showFilter", false); return this.bridge.getResource({ body: [], uid: this._getObjState("uid"), version: this._getObjState("version"), params: $.extend(true, {}, {perPage: 1}) }); }; Box.prototype._fetchResourceError = function (e) { log.error("Impossible to fetch resource"); log.error(e); log.error({code: ERR.FETCH_RESOURCE}); this._setObjState("error", {code: ERR.FETCH_RESOURCE}); this._setStatus("error"); }; Box.prototype._fetchResourceSuccess = function (resource) { log.info("Fetch resource success"); this._updateModel(resource); this._checkModelSize(); }; Box.prototype._checkModelSize = function () { var status = this._getModelStatus(); switch (status.toLowerCase()) { case "ready" : this._reactToModelStatus("to_load"); break; default: this._reactToModelStatus(status); } }; Box.prototype._getModelInfo = function () { var rt = this._getObjState("model.metadata.meContent.resourceRepresentationType") || "", resourceType = rt.toLowerCase(); log.info("Resource type is: "); log.info(resourceType); this._setObjState("resourceRepresentationType", resourceType); }; Box.prototype._renderBox = function () { log.info("Render box start:"); this._setStatus("ready"); this._renderBoxFaces(); }; Box.prototype._initTabSources = function () { var registeredTabs = $.extend(true, {}, this.pluginRegistry), tabs = this.tabs, tabsKeys = Object.keys(tabs), paths = []; _.each(tabsKeys, _.bind(function (tab) { var conf = registeredTabs[tab]; if (!conf) { log.error('Registration not found for "' + tab + ' tab".'); } if (!$.isPlainObject(tabs[tab])) { this._setObjState("tabs." + tab, {}); } this._setObjState("tabs." + tab + ".suitable", false); if (conf.path) { paths.push(conf.path); } else { log.error('Impossible to find path configuration for "' + tab + ' tab".'); } }, this)); log.info("Tab paths found: "); log.info(paths); }; Box.prototype._renderBoxFaces = function () { log.info("Render box faces"); this._updateBoxTitle(); this._renderFrontFace(); this._renderBackFace(); this._updateBoxTitle(); }; Box.prototype._updateBoxTitle = function (tl) { var extractTitleFn = this._getObjState("title"), title = typeof extractTitleFn === 'function' ? extractTitleFn.call(this, this._getObjState("model")) : extractTitleFn; if (this.computed) { title = title + i18nLabels[this._getObjState("lang")]["computed_resource"]; } this.$boxTitle.html(tl || title); }; Box.prototype._getModelTitle = function (model) { var title = BoxUtils.getNestedProperty("metadata.title", model), uid = BoxUtils.getNestedProperty("metadata.uid", model); return this._getI18nLabel(title) || uid; }; Box.prototype._getI18nLabel = function (obj) { if (typeof obj !== "object") { return ""; } var languages = this.langFallbackOrder.slice(0), label = "Missing label"; languages.unshift(this._getObjState("lang")); languages = _.uniq(languages); for (var i = 0; i < languages.length; i++) { label = obj[languages[i]]; if (label) { break; } } return label; }; // Load resource Box.prototype._validateValues = function (values) { this._hideFilterError(); var valid = true, model = this._getObjState("model"), resourceColumns = BoxUtils.getNestedProperty("metadata.dsd.columns", model) || [], resourceKeyColumns = BoxUtils.cleanArray(resourceColumns.map(function (c) { if (c.key === true) { return c.id; } })), errors = [], aggregations = BoxUtils.getNestedProperty("aggregations.values.aggregations", values) || [], columns = BoxUtils.getNestedProperty("filter.values", values) || {}, columnsKey = Object.keys(columns) || [], valueDimension = _.findWhere(resourceColumns, {subject: "value"}) || {}, valueId = valueDimension.id; var sum = _.where(aggregations, {parent: 'sum'}).map(function (item) { return item.value; }), avg = _.where(aggregations, {parent: 'avg'}).map(function (item) { return item.value; }), first = _.where(aggregations, {parent: 'first'}).map(function (item) { return item.value; }), last = _.where(aggregations, {parent: 'last'}).map(function (item) { return item.value; }), group = _.where(aggregations, {parent: 'group'}).map(function (item) { return item.value; }), sumLength = parseInt(sum.length, 10), avgLength = parseInt(avg.length, 10), firstLength = parseInt(first.length, 10), lastLength = parseInt(last.length, 10), groupLength = parseInt(group.length, 10), aggregationRulesLength = sumLength + avgLength + firstLength + lastLength; // aggregations on dataType !== number _.each(sum, function (dimension) { var column = _.findWhere(resourceColumns, {id: dimension}) || {}, dataType = column.dataType; if (dataType !== 'number') { errors.push({ code: ERR.NO_NUMBER_DATATYPE, value: dimension, label: _.findWhere(aggregations, {value: dimension}).label }); } }); _.each(avg, function (dimension) { var column = _.findWhere(resourceColumns, {id: dimension}) || {}, dataType = column.dataType; if (dataType !== 'number') { errors.push({ code: ERR.NO_NUMBER_DATATYPE, value: dimension, label: _.findWhere(aggregations, {value: dimension}).label }); } }); //no 'value' on group by if (_.contains(group, valueId)) { var valueInGroupBy = _.findWhere(aggregations, {value: valueId}); errors.push({ code: ERR.VALUE_IN_GROUP_BY, value: valueInGroupBy.value, label: valueInGroupBy.label }); } //if aggregation rules -> group has to be populated if (aggregationRulesLength > 0 && groupLength === 0) { errors.push({ code: ERR.MISSING_GROUP_BY }); } //if group by is populated -> value in aggregation rules if (groupLength > 0) { var isValueInAggregationRules = _.contains(sum, valueId) || _.contains(avg, valueId) || _.contains(first, valueId) || _.contains(last, valueId); if (!isValueInAggregationRules) { errors.push({ code: ERR.MISSING_VALUE_IN_AGGREGATION_RULES }); } } //no exclude key dimensions var excludedColumns = _.difference(resourceKeyColumns, columnsKey); if (columnsKey.length > 0 && excludedColumns.length > 0) { var labels = _.map(excludedColumns, _.bind(function (dim) { return _.findWhere(resourceColumns, {id: dim}).title[this._getObjState("lang")] }, this)); errors.push({ code: ERR.EXCLUDE_KEY_DIMENSION, value: labels, label: labels.join(", ") }); } return errors.length > 0 ? errors : valid; }; Box.prototype._createQuery = function (payload) { var self = this, filter = [], filterStep, groupStep, computed = false; filterStep = createFilterStep(payload); groupStep = createGroupStep(payload); if (filterStep) { computed = true; filter.push(filterStep); } if (groupStep) { computed = true; filter.push(groupStep); } this.computed = computed; return filter; function createFilterStep(payload) { var step = { name: "filter", parameters: {} }, hasValues = false, columns = Object.keys(BoxUtils.getNestedProperty('filter.values', payload) || {}), rowValues = payload.rows || {}, resourceColumns = BoxUtils.getNestedProperty("metadata.dsd.columns", self._getObjState("model")) || [], columnsSet = resourceColumns .filter(function (c) { return !Utils._endsWith(c.id, "_" + self._getObjState("lang").toUpperCase()) && !Utils._endsWith(c.id,"_EN"); }) .map(function (c) { return c.id; }).sort(), valueDimension = _.findWhere(resourceColumns, {subject: "value"}); if (Object.getOwnPropertyNames(rowValues).length > 0) { step.parameters.rows = rowValues; hasValues = true; } else { log.warn("Filter.rows not included"); } if (columns.length > 0 && !_.contains(columns, valueDimension.id)) { columns.push(valueDimension.id); } //If they are equals it means i want to include all columns so no filter is needed columns = columns.sort(); if (columns.length > 0 && !_.isEqual(columnsSet, columns)) { step.parameters.columns = columns; hasValues = true; } else { log.warn("Filter.columns not included"); } return hasValues ? step : null; } function createGroupStep(payload) { if (!payload.aggregations) { return; } var step = { name: "group", parameters: {} }, hasValues = false, values = BoxUtils.getNestedProperty("aggregations.values.aggregations", payload), by = [], sum = [], avg = [], first = [], last = [], aggregations = []; _.each(values, function (obj) { switch (obj.parent.toLowerCase()) { case "group": by.push(obj.value); break; case "sum": sum.push(obj.value); break; case "avg": avg.push(obj.value); break; case "first": first.push(obj.value); break; case "last": last.push(obj.value); break; } }); sum = BoxUtils.cleanArray(sum).map(function (i) { return {"columns": [i], "rule": "SUM"}; }); avg = BoxUtils.cleanArray(avg).map(function (i) { return {"columns": [i], "rule": "AVG"}; }); first = BoxUtils.cleanArray(first).map(function (i) { return {"columns": [i], "rule": "FIRST"}; }); last = BoxUtils.cleanArray(last).map(function (i) { return {"columns": [i], "rule": "LAST"}; }); //Add group by if (by.length > 0) { step.parameters.by = BoxUtils.cleanArray(by); hasValues = true } //Add aggregations _.each([sum, avg, first, last], function (a) { if (a.length > 0) { aggregations = _.uniq(_.union(aggregations, a), false); } }); if (aggregations.length > 0) { step.parameters.aggregations = aggregations; hasValues = true } return hasValues ? step : null; } }; // Front face Box.prototype._renderFrontFace = function () { log.info("Start rendering box front face"); var faces = this._getObjState("faces"); if (!_.contains(faces, 'front') || this.frontFaceIsRendered === true) { log.warn("Abort 'front' face rendering. face is already rendered: " + this.frontFaceIsRendered + ", config render face: " + _.contains(faces, 'front')); return; } this.frontFaceIsRendered = true; this._checkSuitableTabs(); //TODO enforce logic //check is resource type is dataset if (this._getObjState("resourceRepresentationType") === 'dataset') { this._showMenuItem("download"); } else { log.warn("Download menu item not show because resource is not 'dataset'"); } this._showDefaultFrontTab(); }; Box.prototype._bindFrameEventListeners = function () { var self = this; this.$el.find(s.FRONT_FACE).find("[data-action]").each(function () { var $this = $(this), action = $this.data("action"), event = self._getEventTopic(action); $this.on("click", {event: event, box: self}, function (e) { e.preventDefault(); log.info("Raise event: " + e.data.event); amplify.publish(event, {target: this, box: e.data.box, state: self.getState()}); }); }); }; Box.prototype._showFrontTab = function (tab, opts, force) { log.info('Show tab ' + tab); log.info(opts); var tabs = this.tabs; //check if it is a valid tab if (!tabs[tab]) { log.error("Error on show tab content: ", tab); log.error({code: ERR.MISSING_TAB}); this._setObjState("error", {code: ERR.MISSING_TAB}); this._setStatus("error"); return; } var currentTab = this._getObjState("tab"), currentOpts = this._getObjState("tabOptions")[currentTab]; //TODO check if currentTab is undefined if (!force && currentTab === tab && _.isEqual(currentOpts, opts)) { log.info("Aborting show tab current tab is equal to selected one"); return; } if (this._getObjState("tabs." + tab + ".suitable") !== true) { log.error("Aborting show tab because selected tab is not suitable with current model"); //TODO find first suitable tab and then raise error return; } log.info("Show '" + tab + "' tab for result id: " + this.id); //if opts is empty get default options if (!opts) { opts = BoxUtils.getNestedProperty("options", this.tabs[tab]) } this._setObjState("tab", tab); this._setObjState("tabOptions." + tab, opts); //hide all tabs and show the selected one this.$el.find(s.CONTENT_READY).attr("data-tab", this._getObjState("tab")); this._showTabContent(); }; Box.prototype._showTabContent = function () { var tabs = this.tabs, tab = this._getObjState("tab"); if (!tabs[tab]) { log.error("Error on show tab content: " + tab); log.error({code: ERR.MISSING_TAB}); this._setObjState("error", {code: ERR.MISSING_TAB}); this._setStatus("error"); return; } this._setObjState("tabs." + tab + ".initialized", true); this._callTabInstanceMethod({tab: tab, method: "show", opt1: this._getObjState("tabOptions")[tab]}); }; Box.prototype._createTabInstance = function (tab) { var state = this._getObjState("tabStates." + tab) || {}, model = $.extend(true, {}, this._getObjState("model")), registry = this.pluginRegistry, language = this._getObjState("lang") || C.lang, //Note that for sync call the argument of require() is not an array but a string Tab = require(this._getPluginPath(registry[tab]) + ".js"), config = $.extend(true, {}, state, { el: this._getTabContainer(tab), box: this, lang: language, model: model, id: tab + "_" + this.id, environment: this._getObjState("environment"), cache: this._getObjState("cache"), serviceProvider: this.serviceProvider, config: this._getObjState("tabConfig")[tab], nls : this._getObjState("nls") }), instance; instance = new Tab(config); //Subscribe to tab events instance.on('filter', _.bind(this._onTabToolbarChangeEvent, this)); instance.on('state', _.bind(this._onTabStateChangeEvent, this, tab)); instance.on('initialized', _.bind(this._onInitializatedEvent, this)); instance.on('title.change', _.bind(this._onTitleChangeEvent, this, tab)); //cache the plugin instance this.front_tab_instances[tab] = instance; return instance; }; Box.prototype._onTitleChangeEvent = function (tab, title) { this._updateBoxTitle(title); }; Box.prototype._getTabInstance = function (tab, face) { return face === 'back' ? this.back_tab_instances[tab] : this.front_tab_instances[tab]; }; Box.prototype._getTabContainer = function (tab) { var $container = this.$el.find(s.FRONT_CONTENT).find("[data-section='" + tab + "']"); if ($container.length === 0) { $container = $('<div data-section="' + tab + '"></div>'); this.$el.find(s.FRONT_CONTENT).append($container) } return $container; }; Box.prototype._checkSuitableTabs = function () { var registeredTabs = this.pluginRegistry, tabsKeys = Object.keys(this.tabs); _.each(tabsKeys, _.bind(function (tab) { var conf = registeredTabs[tab]; if (!conf) { log.error('Registration not found for "' + tab + ' tab".'); } if (conf.path) { this._createTabInstance(tab); this._setObjState("tabs." + tab + ".suitable", this._callTabInstanceMethod({tab: tab, method: 'isSuitable'})); log.info(tab + " tab is suitable? ", this._callTabInstanceMethod({tab: tab, method: 'isSuitable'})) if (this._getObjState("tabs." + tab + ".suitable") === true) { this._showMenuItem(tab); } else { this._hideMenuItem(tab); } } else { log.error('Impossible to find path configuration for "' + tab + ' tab".'); } }, this)); this._syncFrontTabs(); }; Box.prototype._showDefaultFrontTab = function () { var tab = this._getObjState("tab") || C.tab, defaultTabIsSuitable = this._getObjState("tabs." + tab + ".suitable"), options = this._getObjState("tabOptions." + tab); if (defaultTabIsSuitable !== true) { log.warn("Default tab is not suitable. Find first suitable tab to show"); var tabsKeys = Object.keys(this.tabs), found = false; _.each(tabsKeys, _.bind(function (t) { if (!found === this._getObjState("tabs." + t + ".suitable") === true) { tab = t; } }, this)); } this._showFrontTab(tab, options, true); }; Box.prototype._syncFrontTabs = function () { log.info("Send front 'sync' signal"); var tabsKeys = Object.keys(this.tabs); _.each(tabsKeys, _.bind(function (tab) { if (this._getObjState("tabs." + tab + ".suitable") === true) { this._callTabInstanceMethod({ tab: tab, method: 'sync', opt1: this._getObjState("sync") || {} }); } }, this)); }; // Back face Box.prototype._renderBackFace = function () { log.info("Start rendering box back face"); var faces = this._getObjState("faces"); if (!_.contains(faces, 'back') || this.backFaceIsRendered === true) { log.warn("Abort 'front' face rendering. face is already rendered: " + this.backFaceIsRendered + ", confing render face: " + _.contains(faces, 'front')) return; } this.backFaceIsRendered = true; this._hideFilterError(); this._createProcessSteps(); this._renderProcessSteps(); this._bindBackFaceEventListeners(); }; Box.prototype._bindBackFaceEventListeners = function () { var self = this; this.$el.find(s.BTN_SIDEBAR).on("click", _.bind(function () { this.$el.find(s.SIDEBAR).toggleClass('hidden-xs hidden-sm'); }, this)); this.$el.find(s.BACK_FACE).find("[data-action]").each(function () { var $this = $(this), action = $this.data("action"), event = self._getEventTopic(action); $this.on("click", {event: event, box: self}, function (e) { e.preventDefault(); log.info("Raise box event: " + e.data.event); amplify.publish(event, {target: this, box: e.data.box, state: self.getState()}); }); }); }; Box.prototype._createProcessSteps = function () { var filterConfiguration, aggregationConfiguration, mapConfiguration, model; filterConfiguration = this._createBackTabConfiguration("filter"); aggregationConfiguration = this._createBackTabConfiguration("aggregations"); mapConfiguration = this._createBackTabConfiguration("map"); model = $.extend(true, {}, this._getObjState("model")); this.processSteps = []; if (this._stepControlAccess("metadata")) { var title = BoxUtils.getNestedProperty("metadata.title", model) || {}, uid = BoxUtils.getNestedProperty("metadata.uid", model), label = title[this._getObjState("lang")] ? title[this._getObjState("lang")] : uid; this.processSteps.push({ tab: "metadata", id: "metadata", model: model, labels: { title: label } }); } if (this._stepControlAccess("filter")) { this.processSteps.push({ id: "filter", tab: "filter", values: filterConfiguration.values, config: filterConfiguration.config, template: filterConfiguration.template, onReady: filterConfiguration.onReady, labels: { title: this._getObjState("nls")["step_filter"] } }); } if (this._stepControlAccess("aggregations")) { this.processSteps.push({ id: "aggregations", tab: "filter", values: aggregationConfiguration.values, config: aggregationConfiguration.filter, template: aggregationConfiguration.template, labels: { title: this._getObjState("nls")["step_aggregations"] } }); } /*if (this._stepControlAccess("map")) { this.processSteps.push({ id: "map", tab: "filter", values: mapConfiguration.values, config: mapConfiguration.filter, template: mapConfiguration.template, onReady: mapConfiguration.onReady, labels: { title: this._getObjState("nls")["step_map"] } }); }*/ }; Box.prototype._stepControlAccess = function (tab) { var resourceType = this._getObjState("resourceRepresentationType"); switch (tab.toLowerCase()) { case "metadata" : return true; case "filter" : return resourceType === 'dataset'; case "aggregations" : return resourceType === 'dataset'; case "map" : return this._getObjState("tabs.map.suitable"); default : return false; } }; Box.prototype._createBackTabConfiguration = function (tab) { var configuration, filterValues = this._getObjState("backFilter") || {}, mapValues = this._getObjState("backMap") || {}; switch (tab.toLowerCase()) { case 'aggregations': configuration = this._createBackAggregationTabConfiguration(filterValues[tab]); break; case 'filter': configuration = this._createBackFilterTabConfiguration(filterValues[tab]); break; case 'map': configuration = this._createBackMapTabConfiguration(mapValues[tab]); break; default : configuration = {}; } return configuration; }; Box.prototype._createBackFilterTabConfiguration = function (values) { var self = this, forbiddenIds = ["value"], forbiddenSubjects = ["value"]; var columnsFromDsd = BoxUtils.getNestedProperty("metadata.dsd.columns", this._getObjState("model")) || [], cols = columnsFromDsd.filter(function (col) { return !_.contains(forbiddenSubjects, col.subject); }), columns = cols .filter(function (col) { return !_.contains(forbiddenIds, col.id.toLowerCase()); }) .filter(function (c) { return !Utils._endsWith(c.id, "_" + self._getObjState("lang").toUpperCase()) && !Utils._endsWith(c.id,"_EN"); }), config; config = Utils.createConfiguration({ model: this._getObjState("model"), common: { selector: { hideSummary: true }, template: { hideSwitch: true, hideHeader: false } } }); //TODO remove workaround with default lang in createConfiguration() var x = {}; _.each(config, function (value, key) { if ( !Utils._endsWith(key, "_" + self._getObjState("lang").toUpperCase()) && !Utils._endsWith(key,"_EN") ) { x[key] = value; } }); return { values: values, config: x, template: FilterFilterTemplate({columns: columns}), onReady: _.bind(function () { var mySwiper = new Swiper(this.$el.find(s.ROWS_SWIPER), { // Optional parameters //direction: 'vertical', //loop: true, // If we need pagination pagination: this.$el.find(s.ROWS_SWIPER).find('.swiper-pagination'), paginationClickable: true, // Navigation arrows nextButton: this.$el.find(s.ROWS_SWIPER).find('.swiper-button-next'), prevButton: this.$el.find(s.ROWS_SWIPER).find('.swiper-button-prev'), slidesPerView: 'auto', //centeredSlides: true, //spaceBetween: 30, //freeMode: true, simulateTouch: false // And if we need scrollbar //scrollbar: '.swiper-scrollbar', }) }, this) }; }; Box.prototype._createBackMapTabConfiguration = function (values) { return $.extend(true, {}, { values: values, filter: { layers: { selector: { id: "tree", source: _.map(mapEarthstatLayers, function (layer) { var title = layer.Title.replace('area', '').replace('3857', ''); return { label: _str.humanize(title), value: 'earthstat:' + layer.Name }; }), config: {core: {multiple: true}} }, dependencies: { "layergroups": {id: "focus", event: "select"} }, template: { title: "Select layers to show on map", hideSwitch: true, hideRemoveButton: true } } }, template: FilterMapTemplate({layers: []}), onReady: _.bind(function (payload) { }, this) }); }; Box.pr