fenix-ui-filter
Version:
1,100 lines (780 loc) • 30 kB
JavaScript
define([
'loglevel',
'jquery',
'underscore',
'../../config/errors',
'../../config/events',
'../../config/config',
'../../html/selector.hbs',
'../../html/nls.hbs',
'../../nls/labels',
"fenix-ui-bridge",
'q',
'validate.js'
], function (log, $, _, ERR, EVT, C, templateSelector, templateNls, i18nLabels, Bridge, Q, ValidateJs) {
'use strict';
var s = {
SWITCH: "input[data-role='switch'][name='disable-selector']",
REMOVE_BTN: "[data-role='remove']"
};
function Selector(obj) {
$.extend(true, this, C, {initial: obj || {}, $filterEl: $(obj.el)});
this._initVariables();
this._render();
return this;
}
/**
* disposition
* @return {Object}
*/
Selector.prototype.printDefaultSelection = function () {
_.each(this.selectors, _.bind(function (obj) {
obj.instance.printDefaultSelection();
}, this));
};
/**
* disposition
* @return {Object}
*/
Selector.prototype.dispose = function () {
this._unbindEventListeners();
_.each(this.selectors, _.bind(function (obj) {
obj.instance.dispose()
}, this));
this.$el.remove();
};
/**
* reset
* @return {Object}
*/
Selector.prototype.reset = function () {
_.each(this.selectors, _.bind(function (obj) {
obj.instance.reset()
}, this));
};
/**
* disposition
* @return {Object}
*/
Selector.prototype.getValues = function (format) {
var result = {
values: {},
labels: {},
valid: true,
errors: {}
}, v, valid, values = {}, labels = {};
if (this.nls) {
if (!this.internalNls) {
_.each(this.languages, _.bind(function (l) {
var selector = this.selectors[this.id + "_" + l],
instance;
if (selector) {
this.untag(selector.$el);
instance = selector.instance;
v = instance.getValues(format);
values[l] = v.values;
labels[l] = v.labels;
valid = this._validateSelection(v.values);
if (valid !== true) {
result.errors[this.id] = valid;
result.valid = false;
this.tagAsInvalid(selector.$el, valid);
} else {
this.tagAsValid(selector.$el);
}
} else {
log.warn(l + " skip language");
}
}, this));
}
else {
v = this.mainSelector.instance.getValues(format);
values[this.lang.toUpperCase()] = v.values;
labels[this.lang.toUpperCase()] = v.labels;
valid = this._validateSelection(v.values);
this.untag(this.mainSelector.$el);
if (valid !== true) {
result.errors[this.id] = valid;
result.valid = false;
this.tagAsInvalid(this.mainSelector.$el, valid);
} else {
this.tagAsValid(this.mainSelector.$el);
}
}
} else {
if (this.mainSelector.instance === undefined ) {
log.warn('Instance is undefined.')
} else {
v = this.mainSelector.instance.getValues(format);
values = v.values;
labels = v.labels;
this.untag(this.mainSelector.instance.$el);
valid = this._validateSelection(v.values);
if (valid !== true) {
result.errors[this.mainSelector.id] = valid;
result.valid = false;
this.tagAsInvalid(this.mainSelector.instance.$el, valid);
} else {
this.tagAsValid(this.mainSelector.instance.$el);
}
}
}
result.values = values;
result.labels = labels;
return result;
};
Selector.prototype._validateSelection = function (s) {
var values = process(s);
log.info("Selection constraints:");
log.info(this.constraints);
log.info("applied to:");
log.info(values);
return ValidateJs.single(values, this.constraints) || true;
function process(v) {
if (Array.isArray(v)) {
return cleanArray(v).length === 1 ? cleanArray(v)[0] : cleanArray(v);
}
var cleaned = {};
_.each(v, function (obj, key) {
cleaned[key] = Array.isArray(obj) ? cleanArray(obj).length === 1 ? cleanArray(obj)[0] : cleanArray(obj) : cleanObject(obj);
});
return cleaned;
function cleanArray(actual) {
var newArray = [];
for (var i = 0; i < actual.length; i++) {
if (actual[i] && actual[i] !== "") {
newArray.push(actual[i]);
}
}
return newArray;
}
function cleanObject(actual) {
var newObj = {};
_.each(actual, function (value, key) {
newObj[key] = cleanArray(value);
});
return newObj;
}
}
};
/**
* set srouces
* @return {Object}
*/
Selector.prototype.setSource = function (values) {
if (this.nls) {
if (Array.isArray(values)) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.setSource(values);
}
}, this))
}
if (typeof values === "object") {
_.each(values, _.bind(function (v, lang) {
var selector = this.selectors[this.selectorId + "_" + lang];
if (selector && typeof selector.instance === "object") {
selector.instance.setSource(v);
}
}, this))
}
} else {
if (Array.isArray(values)) {
this.mainSelector.instance.setSource(values);
}
if (typeof values === "object" && _.contains(Object.keys(values), this.lang.toUpperCase())) {
this.mainSelector.instance.setSource(values[this.lang.toUpperCase()]);
}
}
};
/**
* set value
* @return {Object}
*/
Selector.prototype.setValue = function (values) {
if (this.nls) {
if (Array.isArray(values)) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.setValue(values);
}
}, this))
}
if (typeof values === "object") {
_.each(values, _.bind(function (v, lang) {
var selector = this.selectors[this.selectorId + "_" + lang];
if (selector && typeof selector.instance === "object") {
selector.instance.setValue(v);
}
}, this))
}
} else {
if (Array.isArray(values)) {
this.mainSelector.instance.setValue(values);
}
if (typeof values === "object" && _.contains(Object.keys(values), this.lang.toUpperCase())) {
this.mainSelector.instance.setValue(values[this.lang.toUpperCase()]);
}
}
};
/**
* set value
* @return {Object}
*/
Selector.prototype.enableReadOnly = function () {
if (this.nls) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.enableReadOnly();
}
}, this))
} else {
this.mainSelector.instance.enableReadOnly();
}
};
/**
* set value
* @return {Object}
*/
Selector.prototype.update = function (opts) {
if (this.nls) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.update(opts);
}
}, this))
} else {
this.mainSelector.instance.update(opts);
}
};
/**
* set value
* @return {Object}
*/
Selector.prototype.disableReadOnly = function () {
if (this.nls) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.disableReadOnly();
}
}, this))
} else {
this.mainSelector.instance.disableReadOnly();
}
};
/**
* unset value
* @return {Object}
*/
Selector.prototype.unsetValue = function (values) {
if (this.nls) {
if (Array.isArray(values)) {
_.each(this.selectors, _.bind(function (selector) {
if (selector && typeof selector.instance === "object") {
selector.instance.unsetValue(values);
}
}, this))
}
if (typeof values === "object") {
_.each(values, _.bind(function (v, lang) {
var selector = this.selectors[this.selectorId + "_" + lang];
if (selector && typeof selector.instance === "object") {
selector.instance.unsetValue(v);
}
}, this))
}
} else {
if (Array.isArray(values)) {
this.mainSelector.instance.unsetValue(values);
}
if (typeof values === "object" && _.contains(Object.keys(values), this.lang.toUpperCase())) {
this.mainSelector.instance.unsetValue(values[this.lang.toUpperCase()]);
}
}
};
/**
* get Selector status
* @return {Object}
*/
Selector.prototype.getStatus = function () {
return this.mainSelector.instance.getStatus();
};
/**
* enable
* @return {Object}
*/
Selector.prototype.enable = function (silent) {
_.each(this.selectors, _.bind(function (obj) {
if (obj.instance) obj.instance.enable(silent);
}, this));
this.$el.find(s.SWITCH).prop("checked", true);
if (!silent) {
this._trigger(EVT.SELECTOR_ENABLED, {id: this.id});
}
};
/**
* disable
* @return {Object}
*/
Selector.prototype.disable = function (silent) {
_.each(this.selectors, _.bind(function (obj) {
if (obj.instance) obj.instance.disable(silent)
}, this));
this.$el.find(s.SWITCH).prop("checked", false);
if (!silent) {
this._trigger(EVT.SELECTOR_DISABLED, {id: this.id});
}
};
/**
* mark selector container
* */
Selector.prototype._render = function () {
this.ready = false;
this.selectorsReady = 0; //used for "ready" event
var externalResource = {};
if (this.cl) {
externalResource.obj = this.cl;
}
if (this.enumeration) {
externalResource.obj = this.enumeration;
externalResource.type = "enumeration";
}
if (this.distinct) {
externalResource.obj = this.distinct;
externalResource.type = "distinct";
}
this._createSelectors();
this._attach();
this._attachNls();
this.getExternalResource(externalResource.obj, externalResource.type).then(_.bind(function (rawCl) {
_.each(this.selectors, function (sel) {
sel.data = rawCl;
});
_.each(this.selectors, _.bind(function (obj) {
this._renderSelector(obj)
}, this));
this._activateCurrentLang();
this._bindEventListeners();
}, this), _.bind(this._onGetExternalResourceError, this));
};
/**
* tag selector container
* */
Selector.prototype.tagAsInvalid = function (el, message) {
var $el = el || this.$el,
text = message.join(", ");
$el.find(".form-group").addClass("has-error");
$el.find(".help-block.error").html(ValidateJs.capitalize(text));
};
/**
* tag selector container
* */
Selector.prototype.tagAsValid = function (el) {
var $el = el || this.$el;
$el.find(".help-block.error").empty();
};
/**
* untag selector container
* */
Selector.prototype.untag = function (el) {
var $el = el || this.$el;
$el.find(".form-group").removeClass("has-error");
$el.find(".help-block.error").empty();
};
Selector.prototype._activateCurrentLang = function () {
this.$el.find('[data-lang="' + this.lang.toUpperCase() + '"]').tab('show');
log.info("Lang shown: " + this.lang);
};
Selector.prototype._createSelectors = function () {
var result = {};
if (this.nls && !this.internalNls) {
_.each(this.languages, _.bind(function (l) {
result[this.id + "_" + l] = {
id: this.id + "_" + l,
controller: this.controller,
lang: this.lang,
template: this.template,
selector: this.selector
};
}, this));
}
else {
result[this.id] = {
id: this.id,
controller: this.controller,
lang: this.lang,
template: this.template,
selector: this.selector
};
}
this.selectors = result;
var key = Object.keys(this.selectors)[0];
this.mainSelector = this.selectors[key];
return result;
};
Selector.prototype._initVariables = function () {
this.channels = {};
this.selectorId = this.initial.selector.id;
this.id = this.initial.id;
this.controller = this.initial.controller;
this.template = this.initial.template;
this.selector = this.initial.selector;
this.lang = this.initial.lang;
this.cl = this.initial.cl;
this.enumeration = this.initial.enumeration;
this.distinct = this.initial.distinct;
this.plugins = this.initial.plugins;
this.environment = this.initial.environment;
this.cache = this.initial.cache;
this.values = this.initial.values;
this.serviceProvider = this.initial.serviceProvider;
this.classNames = this.initial.classNames || "";
this.constraints = this.initial.constraints;
this.nls = !!this.initial.nls;
this.internalNls = typeof this.initial.nls === "object" && this.initial.nls.gui === false;
this.languages = this.nls ? this.initial.languages.map(function (l) {
return l.toUpperCase();
}) : [this.lang];
this.cache_db = {};
this.bridge = new Bridge({
environment: this.environment,
cache: this.cache,
serviceProvider : this.serviceProvider
});
};
Selector.prototype._renderSelector = function (obj) {
var $container = this._getSelectorContainer(obj.id),
$el = $container.find("[data-el]").length > 0 ? $container.find("[data-el]") : $container;
var Plugin = this._getSelector(),
model = $.extend(true, obj, {
el: $el,
controller: this,
cl: this.cl,
selector: {
default: this.values
}
});
var instance = new Plugin(model);
instance.on(EVT.SELECTOR_READY, _.bind(this._onSelectorReady, this));
instance.on(EVT.SELECTOR_SELECTED, _.bind(this._onSelectorSelected, this));
this.selectors[obj.id].instance = instance;
};
Selector.prototype._onSelectorSelected = function (payload) {
this._trigger(EVT.SELECTOR_SELECTED, payload);
};
Selector.prototype._onSelectorReady = function () {
this.selectorsReady++;
log.info("Ready event listened from selector: " + this.id);
if (this.selectorsReady === Object.keys(this.selectors).length) {
this._onReady();
}
};
Selector.prototype._onReady = function () {
log.info("Selector is ready: " + this.id);
this._trigger(EVT.SELECTOR_READY, {id: this.id});
};
Selector.prototype._getSelector = function () {
return this.plugins[this.selectorId];
};
Selector.prototype._attach = function () {
this.$el = this._getSelectorContainer(this.id);
};
Selector.prototype._getSelectorContainer = function (id) {
var $cont = this.$filterEl.find('[data-selector="' + id + '"]'),
conf = this;
if ($cont.length === 0) {
log.warn("Impossible to find selector container: " + id);
$cont = $("<div data-selector='" + id + "' class='" + (conf.classNames || '') + "'></div>");
if (this.direction === "append") {
this.$filterEl.append($cont);
} else {
this.$filterEl.prepend($cont);
}
} else {
$cont.addClass(this.classNames);
}
if (conf.templateIsInitialized !== true) {
conf.templateIsInitialized = true;
$cont.append(this._createSelectorContainer(id));
}
return $cont;
};
Selector.prototype._createSelectorContainer = function (id) {
log.info("Create container for selector: " + id);
var classNames = "",
obj = this.template,
conf = $.extend(true, {id: id}, i18nLabels[this.lang], obj),
$html;
_.each(conf, function (value, key) {
if (value === true) {
classNames = classNames.concat(" " + key);
}
});
$html = $(templateSelector($.extend(true, {}, conf)));
return $html;
};
Selector.prototype._attachNls = function () {
var model = {
nls: this.nls,
selector: this.id,
lang: this.lang,
languages: this.languages,
id: this.controller.id,
internalNls: this.internalNls
};
this.$el.append(templateNls(model));
};
Selector.prototype._unbindEventListeners = function () {
this.$el.find(s.SWITCH).off();
this.$el.find(s.REMOVE_BTN).off();
};
Selector.prototype._bindEventListeners = function () {
this.$el.find(s.SWITCH).on("click", _.bind(this._onSwitchClick, this));
this.$el.find(s.REMOVE_BTN).on("click", _.bind(this._onRemoveClick, this));
};
Selector.prototype._onSwitchClick = function (e) {
var checked = $(e.target).is(":checked");
if (!!checked) {
this.enable();
} else {
this.disable();
}
};
Selector.prototype._onRemoveClick = function () {
this.dispose();
this._trigger(EVT.SELECTOR_REMOVED, {id: this.id});
};
Selector.prototype._getEventName = function (evt) {
return this.controller.id + evt;
};
// Preload scripts and external resources
Selector.prototype.getExternalResource = function (obj, type) {
if (!obj) {
log.info("No external resource needed");
return Q.Promise(function (resolve) {
resolve();
});
}
if (!obj.initialized && this.selector.lazy === true) {
log.warn("Lazy loading for selector: " + this.id);
obj.levels = 1;
obj.level = 1;
}
delete obj.initialized;
//Check if codelist is cached otherwise query
var stored = this._getStoredCodelist(obj);
if (stored === undefined || stored.length < 2) {
log.info(this._getCodelistCacheKey(obj) + " not in session storage.");
return this._createPromise(obj, type);
} else {
log.info(this._getCodelistCacheKey(obj) + " read from session storage.");
return Q.Promise(function (resolve) {
//console.log(stored)
resolve(stored);
});
}
};
Selector.prototype._onGetExternalResourceError = function (e) {
log.error("Resources load: error");
log.error(e)
};
Selector.prototype._createPromise = function (obj, type) {
var self = this,
body = obj,
key = this._getCodelistCacheKey(obj);
return this._getPromise(body, type)
.then(function (result) {
var data = [];
if (!Array.isArray(result)) {
_.each(result, function (obj, key) {
data.push({
code: key,
title: $.extend(true, {}, obj)
})
});
} else {
data = result;
}
log.info("Code List loaded successfully for: " + key);
self._storeCodelist(body, data);
//format values
if (type === "distinct") {
var dataType = obj.columnDataType;
switch (dataType.toLowerCase()) {
case "number" :
data = _.map(data, function (d) {
var title = {},
result = {};
title[self.lang.toUpperCase()] = d.toString();
result.code = d;
result.title = title;
return result
});
break;
// extend with other cases
}
}
return data;
}, function (r) {
log.error(r);
});
};
Selector.prototype._storeCodelist = function (obj, cl) {
this.cache_db[this._getCodelistCacheKey(obj)] = cl;
return this.cache_db[this._getCodelistCacheKey(obj)];
};
Selector.prototype._getStoredCodelist = function (obj) {
return this.cache_db[this._getCodelistCacheKey(obj)];
};
Selector.prototype._getCodelistCacheKey = function (o) {
var obj = typeof o === 'object' ? o : {},
key = "_",
keys = Object.keys(obj).sort();
for (var i = 0; i < keys.length; i++) {
key += "_" + keys[i] + ":" + obj[keys[i]];
}
return key;
};
Selector.prototype._getPromise = function (body, type) {
var promise,
t = type || "";
switch (t.toLowerCase()) {
case "enumeration" :
promise = this.bridge.getEnumeration({
uid: body.uid
});
break;
case "distinct" :
promise = this.bridge.getColumnDistinctValues({
uid: body.uid,
version: body.version,
columnId: body.columnId
});
break;
default :
promise = this.bridge.getCodeList({
body: body
});
break;
}
return promise;
};
//pub/sub
/**
* pub/sub
* @return {Object} component instance
*/
Selector.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;
};
Selector.prototype._trigger = function (channel) {
if (!this.channels[channel]) {
return false;
}
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0, l = this.channels[channel].length; i < l; i++) {
var subscription = this.channels[channel][i];
subscription.callback.apply(subscription.context, args);
}
return this;
};
//dependencies
Selector.prototype._dep_min = function (data) {
_.each(this.selectors, function (selector) {
selector.instance._dep_min(data);
});
};
Selector.prototype._dep_parent = function (c) {
this._getPromise(c).then(
_.bind(function (data) {
var source = [];
_.each(data, function (s) {
source = source.concat(s.children);
});
source = _.uniq(source);
_.each(this.selectors, function (selector) {
selector.instance._dep_parent({data: source});
});
}, this),
function (r) {
log.error(r);
}
)
};
Selector.prototype._dep_process = function (process) {
var self = this;
this.bridge.getProcessedResource(process).then(
_.bind(function (result) {
var data = Array.isArray(result.data) ? result.data : [],
source = [];
_.each(data, function (s) {
source.push({
value: s[process.indexValueColumn || 0],
label: s[process.indexLabelColumn || 1]
});
});
source = _.uniq(source);
_.each(self.selectors, function (selector) {
selector.instance._dep_process(source);
});
}, this),
function (r) {
log.error(r);
}
)
};
Selector.prototype._dep_ensure_unset = function (data) {
_.each(this.selectors, function (selector) {
selector.instance._dep_ensure_unset(data);
});
};
Selector.prototype._dep_disable = function (data) {
_.each(this.selectors, function (selector) {
selector.instance._dep_disable(data);
});
};
Selector.prototype._dep_readOnlyIfNotValue = function (payload, o) {
log.info("_dep_readOnlyIfNotValue invokation");
log.info(o);
var forbiddenValue = o.args.value,
selectedValues = payload.values || [],
group = this.groups[o.group] || {},
target = group[o.target] || {},
instance = target.instance;
if (instance) {
if (_.contains(selectedValues, forbiddenValue)) {
instance.disableReadOnly()
} else {
instance.enableReadOnly()
}
}
};
Selector.prototype._dep_mandatoryIfOtherValue = function (payload, o) {
//alert('in 2')
log.info("_dep_mandatoryIfOtherValue invokation");
log.info(o);
var forbiddenValue = o.args.value,
selectedValues = payload.values || [],
selector = this.selectors[o.target] || {},
instance = selector.instance;
if (instance) {
if (_.contains(selectedValues, forbiddenValue)) {
if((instance.constraints!=null)&&(typeof instance.constraints!='undefined'))
instance.constraints["presence"] = true;
else
instance.constraints = {"presence": true};
instance.getValues();
} else {
if((instance.constraints!=null)&&(typeof instance.constraints!='undefined'))
delete instance.constraints["presence"];
instance.getValues();
}
}
};
return Selector;
});