fenix-ui-filter
Version:
734 lines (519 loc) • 19.8 kB
JavaScript
define([
"jquery",
"loglevel",
'underscore',
'../../config/errors',
'../../config/events',
'../../config/config',
'../../nls/labels',
'../../html/selectors/tree.hbs',
'../../html/selectors/treeItem.hbs',
"jstree"
], function ($, log, _, ERR, EVT, C, i18n, template, templateItem) {
'use strict';
var defaultOptions = {
summaryRender: function (params, opts) {
return this._summaryRender(params, opts);
}
}, s = {
SELECTORS_CLASS: ".fx-selector",
TREE_CONTAINER: "[data-role='tree']",
FILTER_CONTAINER: "[data-role='filter']",
CLEAR_ALL_CONTAINER: "[data-role='clear']",
SELECT_ALL_CONTAINER: "[data-role='select']",
SUMMARY_CONTAINER: "[data-role='summary']",
AMOUNT_CONTAINER: "[data-role='amount']",
TEMPLATE_TREE: "[data-tree-template]",
TEMPLATE_SUMMARY_ITEM: "[data-summary-item]"
};
function Tree(o) {
$.extend(true, this, defaultOptions, o, {$el: $(o.el)});
this._renderTemplate();
this._initVariables();
this._renderTree();
return this;
}
/**
* getValues method
* Mandatory method
*/
Tree.prototype.getValues = function () {
var result = {values: [], labels: {}};
var instance = this.tree.jstree(true),
selection = instance.get_selected();
//remove empty selection
if (Array.isArray(selection) && selection.length > 0) {
result.values = selection;
_.each(result.values, function (c) {
result.labels[c] = instance.get_node(c).text;
});
}
return result;
};
/**
* Reset method
* Mandatory method
*/
Tree.prototype.reset = function () {
this.printDefaultSelection();
log.info("Selector reset successfully");
};
/**
* printDefaultSelection
* Mandatory method
*/
Tree.prototype.printDefaultSelection = function () {
this.printDefaultSelection();
log.info("Selector printDefaultSelection successfully");
};
/**
* Disposition method
* Mandatory method
*/
Tree.prototype.dispose = function () {
this._dispose();
log.info("Selector disposed successfully");
};
/**
* Enable selector
* Mandatory method
*/
Tree.prototype.enable = function () {
var nodes,
$container = this.$el;
nodes = $container.find(s.TREE_CONTAINER).jstree(true).get_json(null, {flat: true});
_.each(nodes, function (n) {
$container.find(s.TREE_CONTAINER).jstree(true).enable_node(n);
});
//enable filter
$container.find(s.CLEAR_ALL_CONTAINER).removeAttr("disabled");
$container.find(s.SELECT_ALL_CONTAINER).removeAttr("disabled");
//enable filter
$container.find(s.FILTER_CONTAINER).removeAttr("disabled");
this.status.disabled = false;
log.info("Selector enabled : " + this.id);
};
/**
* Disable selector
* Mandatory method
*/
Tree.prototype.disable = function () {
var nodes,
$container = this.$el;
nodes = $container.find(s.TREE_CONTAINER).jstree(true).get_json(null, {flat: true});
_.each(nodes, function (n) {
$container.find(s.TREE_CONTAINER).jstree(true).disable_node(n);
});
//disable clear all btn
$container.find(s.CLEAR_ALL_CONTAINER).attr("disabled", true);
$container.find(s.SELECT_ALL_CONTAINER).attr("disabled", true);
//disable filter
$container.find(s.FILTER_CONTAINER).attr("disabled", true);
this.status.disabled = true;
log.info("Selector disabled : " + this.id);
};
/**
* Return Tree internal status
* return {Object} status
*/
Tree.prototype.getStatus = function () {
return this._getStatus();
};
/**
* Set tree domain
* return {Object} status
*/
Tree.prototype.setDomain = function (data) {
return this._setDomain(data);
};
/**
* setSource method
* Mandatory method
*/
Tree.prototype.setSource = function ( source ) {
var $container = this.$el,
tree = $container.find(s.TREE_CONTAINER).jstree(true);
source = _.map(source, function (d) {
return {
id: d.value || d.id,
text: d.label || d.text,
parent : d.parent || '#'
}
});
tree.settings.core.data = source;
tree.refresh(true);
tree.redraw(true);
};
/**
* deselectAll method
* Mandatory method
*/
Tree.prototype.deselectAll = function (id) {
var $container = this.$el,
self = this;
$container.find(s.TREE_CONTAINER).jstree("deselect_all", true);
self._notifyTreeSelectionChange({
instance: self.tree.jstree(true),
id: id
})
};
/**
* Unset the given value.
* return {null}
*/
Tree.prototype.unsetValue = function (v) {
if (this.status.disabled !== true) {
log.info("Unset tree value: " + v);
this.tree.jstree(true).deselect_node({id: v});
} else {
log.warn("Selector is disabled. Impossible to unset tree value: " + v);
}
this._updateSummary();
};
/**
* Resets the selected items to the given value.
* return {null}
*/
Tree.prototype.setValue = function (v, silent) {
log.info("Set tree value: " + v + ". Silent? " + silent);
if (!Array.isArray(v)) {
v = [v];
}
//clear current selection
this.tree.jstree(true).deselect_all(true);
_.each(v, _.bind(function (value) {
this.tree.jstree(true).select_node({id: value}, silent);
}, this));
};
Tree.prototype._renderTemplate = function () {
var $el = this.$el.find(s.TREE_CONTAINER);
if ($el.length === 0) {
log.info("Injecting template for: " + this.id);
this.$el.append(template($.extend(true, {}, i18n[this.lang.toLowerCase()], this, this.selector)));
}
};
Tree.prototype._initVariables = function () {
this.$summaryItems = $();
//Update status
this.status = {};
this.status.disabled = this.selector.disabled;
this.channels = {};
this.lang = this.lang.toUpperCase();
};
Tree.prototype.printDefaultSelection = function () {
return this._printDefaultSelection();
};
Tree.prototype._getStatus = function () {
return this.status;
};
Tree.prototype._buildTreeModel = function (fxResource, parent) {
var data = this._buildTreeModelFromCodelist(fxResource, parent) || [];
//Merge static static data
if (this.selector.source) {
var staticData = this.selector.source;
if (!Array.isArray(data)) {
log.error(ERR.INVALID_DATA);
} else {
var convertedData = staticData.map(function (i) {
return {id: i.value, text: i.label, parent: i.parent || '#'};
});
data = _.uniq(_.union(data, convertedData), false, function (item) {
return item.id;
});
}
}
if (!!this.selector.sort) {
data = data.sort(
(typeof this.selector.sort === 'function') ? this.selector.sort : function (a, b) {
if (a.text < b.text) return -1;
if (a.text > b.text) return 1;
return 0;
});
}
return data;
};
Tree.prototype._buildTreeModelFromCodelist = function (fxResource, parent, cl) {
var data = [],
selector = this,
selectorConfig = selector.selector || {},
blacklist = selectorConfig.blacklist || [],
bl = blacklist.map(function (item) {
return item.toString()
});
_.each(fxResource, _.bind(function (item) {
if (!_.contains(bl, item.code.toString())) {
data.push({
id: item.code,
text: item.title[selector.lang] || item.title["EN"],
parent: parent || '#'
});
if (Array.isArray(item.children) && item.children.length > 0) {
data = _.uniq(_.union(data, this._buildTreeModelFromCodelist(item.children, item.code, cl)), false, function (item) {
return item.id;
});
}
} else {
log.warn("code [" + item.code + "] excluded from " + this.id);
}
}, this));
data = _.uniq(data, false, function(item){
return item['id'];
});
return data;
};
Tree.prototype._bindEventListeners = function () {
this.tree
//Default selection
.on('ready.jstree', _.bind(function (e, data) {
this.printDefaultSelection();
this.status.ready = true;
//Always async
this._trigger(EVT.SELECTOR_READY, {id: this.id});
}, this))
.on("select_node.jstree", _.bind(function () {
this._updateSummary();
}, this))
.on("deselect_node.jstree", _.bind(function () {
this._updateSummary();
}, this))
.on("changed.jstree ", _.bind(function (e, data) {
if (!isNaN(this.selector.max) && data.selected.length > this.selector.max) {
data.instance.deselect_node(data.node);
log.warn("Max number of selectable item reached. Change 'selector.selector.max' config.");
return;
}
/* TODO uncomment after configuration
if (!data.instance.is_leaf(data.node)) {
data.instance.toggle_node(data.node);
data.instance.deselect_node(data.node, true);
return;
}*/
if (this.status.ready === true) {
this._notifyTreeSelectionChange({
instance: data.instance,
id: this.id
});
}
}, this));
};
Tree.prototype._unbindEventListeners = function () {
this.tree.off();
};
Tree.prototype._dispose = function () {
this._unbindEventListeners();
this._destroyTree();
};
Tree.prototype._renderTree = function () {
var config = this.selector,
$container = this.$el,
tree,
self = this;
tree = $container.find(s.TREE_CONTAINER).jstree($.extend(true, {}, {
core: {
multiple: true,
check_callback: true,
animation: 0,
data: function (node, cb) {
if (node.id === "#") {
self._getRootItems(config, cb);
} else {
self._getChildrenItems(config, node, cb);
}
},
themes: {
icons: false,
stripes: true
}
},
plugins: ['search', 'wholerow', 'checkbox', "unique"],
search: {
show_only_matches: true
}
}, config.config));
this.tree = tree;
this._bindEventListeners();
initBtns($container, this.id);
initFilter($container);
function initFilter($container) {
var to = false;
$container.find(s.FILTER_CONTAINER).on("keyup", function () {
if (to) {
clearTimeout(to);
}
to = setTimeout(function () {
var v = $container.find(s.FILTER_CONTAINER).val().replace(/\s+$/, '');
$container.find(s.TREE_CONTAINER).jstree(true).search(v);
}, 250);
});
}
function initBtns($container, id) {
$container.find(s.CLEAR_ALL_CONTAINER).on('click', function () {
$container.find(s.TREE_CONTAINER).jstree("deselect_all", true);
self._notifyTreeSelectionChange({
instance: self.tree.jstree(true),
id: id
})
});
$container.find(s.SELECT_ALL_CONTAINER).on('click', function () {
$container.find(s.TREE_CONTAINER).jstree("select_all", true);
self._notifyTreeSelectionChange({
instance: self.tree.jstree(true),
id: id
})
});
}
};
Tree.prototype._getRootItems = function (config, cb) {
var data = this._getTreeModel(config);
if (config.lazy === true) {
_.each(data, function (item) {
item.children = true;
})
}
cb(data);
};
Tree.prototype._getChildrenItems = function (config, node, cb) {
var self = this,
body = $.extend(true, {}, this.cl, {
levels: 2,
codes: [node.id],
initialized : true
});
this.controller.getExternalResource(body)
.then(function (data) {
var model = self._buildTreeModel(data[0].children || [], node.id);
cb(model);
});
};
Tree.prototype._getTreeModel = function (config) {
var data = this._buildTreeModel(this.data);
for (var i = config.to; i >= config.from; i--) {
data.push({id: i.toString(), text: i.toString()});
}
return data;
};
Tree.prototype._notifyTreeSelectionChange = function (o) {
var self = this,
payload = [],
selected = o.instance.get_selected();
_.each(selected, function (sel) {
var node = o.instance.get_node(sel),
label = {};
label[self.lang] = node.text;
payload.push({value: node.id, label: label, parent: node.parent})
});
this._updateSummary();
this._trigger(EVT.SELECTOR_SELECTED, $.extend({id: self.id }, self.getValues()) )
};
Tree.prototype._updateSummary = function () {
var values = this.getValues(),
model = [],
opts = { labelonly : this.template.hideSummaryCode || false },
self = this,
instance = this.tree.jstree(true);
_.each(values.labels, function (val, key) {
var $summaryModel = $(self.summaryRender({value: key, label: val}, opts)),
value;
if ($summaryModel.length > 0) {
value = "<span data-value='" + key + "'> " + $summaryModel[0].outerHTML + "</span>"
}
model.push(value);
});
//unbind click listener
this.$summaryItems.children().each(function () {
$(this).off();
});
this.$summaryItems = $(templateItem({values: model}));
var that = this.$summaryItems;
//bind click listener
this.$summaryItems.children().each(function () {
var $this = $(this);
$this.on("click", function () {
if (self.status.disabled !== true) {
instance.deselect_node($this.data("value"));
$this.remove();
}
});
});
this.$el.find(s.SUMMARY_CONTAINER).empty().append(this.$summaryItems);
};
Tree.prototype._summaryRender = function (item, opts) {
var output = (opts.labelonly ?
"<span>" + item.label + "</span>" :
"<span>" + item.label + "<span class='code-brk'>[" + item.value + "]</span></span>");
return output;
//return "<span>" + item.label + "<span class='code-brk'>[" + item.value + "]</span></span>";
};
Tree.prototype._setDomain = function (opts) {
log.info("set tree domain");
log.info(opts);
var data = opts.data,
self = this,
model = [],
$container = this.$el,
tree;
_.each(data, function (item) {
model.push(item);
model.concat(item.children)
});
model = _.unique(model);
if ($container.length > 0 && Array.isArray(model)) {
tree = $container.find(s.TREE_CONTAINER).jstree(true);
var treeData = this._buildTreeModel(model, null);
this.setSource(treeData);
} else {
log.warn("Impossible to find container for: " + this.id);
}
};
Tree.prototype._getEventName = function (evt) {
return this.controller.id + evt;
};
Tree.prototype._printDefaultSelection = function () {
var config = this.selector,
tree = this.tree.jstree(true);
//clear current selection
tree.deselect_all(true);
if (config.default && Array.isArray(config.default) && $.isFunction(tree.select_node) && $.isFunction(tree.deselect_all)) {
_.each(config.default, function (k) {
tree.select_node(k)
});
}
this._updateSummary();
};
Tree.prototype._destroyTree = function () {
var $container = this.$el;
$container.find(s.TREE_CONTAINER).jstree("destroy");
$container.find(s.FILTER_CONTAINER).off();
$container.find(s.CLEAR_ALL_CONTAINER).off();
$container.empty();
log.info("Destroyed tree: " + this.id);
};
// dependency handler
Tree.prototype._dep_parent = function (opts) {
this.setDomain(opts);
};
/**
* pub/sub
* @return {Object} component instance
*/
Tree.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;
};
Tree.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;
};
return Tree;
});