2017-12-19 13:30:58 +00:00
|
|
|
/*
|
|
|
|
*
|
2019-01-17 13:05:03 +00:00
|
|
|
* (c) Copyright Ascensio System SIA 2010-2019
|
2017-12-19 13:30:58 +00:00
|
|
|
*
|
|
|
|
* This program is a free software product. You can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
|
|
* of any third-party rights.
|
|
|
|
*
|
|
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
*
|
2019-01-17 13:00:34 +00:00
|
|
|
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
|
|
|
|
* street, Riga, Latvia, EU, LV-1050.
|
2017-12-19 13:30:58 +00:00
|
|
|
*
|
|
|
|
* The interactive user interfaces in modified source and object code versions
|
|
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
|
|
* Section 5 of the GNU AGPL version 3.
|
|
|
|
*
|
|
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
|
|
*
|
|
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
|
|
* well as technical writing content are licensed under the terms of the
|
|
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* TreeView.js
|
|
|
|
*
|
|
|
|
* Created by Julia Radzhabova on 12/14/17
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (Common === undefined)
|
|
|
|
var Common = {};
|
|
|
|
|
|
|
|
define([
|
|
|
|
'common/main/lib/component/DataView'
|
|
|
|
], function () {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
Common.UI.TreeViewModel = Common.UI.DataViewModel.extend({
|
|
|
|
defaults: function() {
|
|
|
|
return {
|
|
|
|
id: Common.UI.getId(),
|
|
|
|
name: '',
|
2017-12-21 08:44:06 +00:00
|
|
|
isNotHeader: false,
|
2017-12-19 13:30:58 +00:00
|
|
|
hasSubItems: false,
|
2018-01-23 10:21:27 +00:00
|
|
|
hasParent: false,
|
2017-12-25 09:03:51 +00:00
|
|
|
isEmptyItem: false,
|
2017-12-19 13:30:58 +00:00
|
|
|
isExpanded: true,
|
|
|
|
isVisible: true,
|
|
|
|
selected: false,
|
|
|
|
allowSelected: true,
|
|
|
|
disabled: false,
|
|
|
|
level: 0,
|
|
|
|
index: 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Common.UI.TreeViewStore = Backbone.Collection.extend({
|
|
|
|
model: Common.UI.TreeViewModel,
|
|
|
|
|
2017-12-20 11:09:31 +00:00
|
|
|
expandSubItems: function(record) {
|
2017-12-19 13:30:58 +00:00
|
|
|
var me = this;
|
|
|
|
var _expand_sub_items = function(idx, expanded, level) {
|
|
|
|
for (var i=idx+1; i<me.length; i++) {
|
|
|
|
var item = me.at(i);
|
|
|
|
var item_level = item.get('level');
|
|
|
|
if (item_level>level) {
|
|
|
|
if (expanded)
|
|
|
|
item.set('isVisible', true);
|
|
|
|
if (item.get('hasSubItems'))
|
|
|
|
i = _expand_sub_items(i, item.get('isExpanded'), item_level );
|
|
|
|
} else {
|
|
|
|
return (i-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
record.set('isExpanded', true);
|
|
|
|
_expand_sub_items(record.get('index'), true, record.get('level'));
|
|
|
|
},
|
|
|
|
|
2017-12-20 11:09:31 +00:00
|
|
|
collapseSubItems: function(record) {
|
2017-12-19 13:30:58 +00:00
|
|
|
var start_level = record.get('level'),
|
|
|
|
index = record.get('index');
|
|
|
|
for (var i=index+1; i<this.length; i++) {
|
|
|
|
var item = this.at(i);
|
|
|
|
var item_level = item.get('level');
|
|
|
|
if (item_level>start_level) {
|
|
|
|
item.set('isVisible', false);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-12-20 11:09:31 +00:00
|
|
|
return i-1;
|
|
|
|
},
|
|
|
|
|
|
|
|
expandAll: function() {
|
|
|
|
this.each(function(item) {
|
|
|
|
item.set('isExpanded', true);
|
|
|
|
item.set('isVisible', true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
collapseAll: function() {
|
|
|
|
for (var i=0; i<this.length; i++) {
|
|
|
|
var item = this.at(i);
|
2018-01-23 10:21:27 +00:00
|
|
|
if (!item.get('isNotHeader')) {
|
|
|
|
item.set('isExpanded', false);
|
|
|
|
i = this.collapseSubItems(item);
|
|
|
|
}
|
2017-12-20 11:09:31 +00:00
|
|
|
}
|
2017-12-20 12:34:17 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
expandToLevel: function(expandLevel) {
|
|
|
|
var me = this;
|
|
|
|
var _expand_sub_items = function(idx, level) {
|
|
|
|
var parent = me.at(idx);
|
|
|
|
parent.set('isExpanded', false);
|
|
|
|
for (var i=idx+1; i<me.length; i++) {
|
|
|
|
var item = me.at(i);
|
|
|
|
var item_level = item.get('level');
|
|
|
|
if (item_level>level) {
|
|
|
|
if (item_level<=expandLevel)
|
|
|
|
parent.set('isExpanded', true);
|
|
|
|
item.set('isVisible', item_level<=expandLevel);
|
|
|
|
if (item.get('hasSubItems'))
|
|
|
|
i = _expand_sub_items(i, item_level );
|
|
|
|
} else {
|
|
|
|
return (i-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (var j=0; j<this.length; j++) {
|
|
|
|
var item = this.at(j);
|
2018-01-23 10:21:27 +00:00
|
|
|
if (item.get('level')<=expandLevel || !item.get('hasParent')) {
|
2017-12-20 12:34:17 +00:00
|
|
|
item.set('isVisible', true);
|
2018-01-23 10:21:27 +00:00
|
|
|
if (!item.get('isNotHeader'))
|
|
|
|
j = _expand_sub_items(j, item.get('level'));
|
2017-12-20 12:34:17 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-19 13:30:58 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Common.UI.TreeView = Common.UI.DataView.extend((function() {
|
|
|
|
return {
|
|
|
|
options: {
|
|
|
|
handleSelect: true,
|
|
|
|
showLast: true,
|
|
|
|
allowScrollbar: true,
|
2018-03-16 10:07:18 +00:00
|
|
|
scrollAlwaysVisible: true,
|
2020-09-26 18:01:50 +00:00
|
|
|
emptyItemText: '',
|
|
|
|
keyMoveDirection: 'both'
|
2017-12-19 13:30:58 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
template: _.template([
|
2020-09-27 11:39:29 +00:00
|
|
|
'<div class="treeview inner" style="<%= style %>"></div>'
|
2017-12-19 13:30:58 +00:00
|
|
|
].join('')),
|
|
|
|
|
|
|
|
initialize : function(options) {
|
|
|
|
options.store = options.store || new Common.UI.TreeViewStore();
|
2017-12-21 12:01:37 +00:00
|
|
|
options.emptyItemText = options.emptyItemText || '';
|
|
|
|
options.itemTemplate = options.itemTemplate || _.template([
|
|
|
|
'<div id="<%= id %>" class="tree-item <% if (!isVisible) { %>' + 'hidden' + '<% } %>" style="display: block;padding-left: <%= level*16 + 24 %>px;">',
|
|
|
|
'<% if (hasSubItems) { %>',
|
|
|
|
'<div class="tree-caret img-commonctrl ' + '<% if (!isExpanded) { %>' + 'up' + '<% } %>' + '" style="margin-left: <%= level*16 %>px;"></div>',
|
|
|
|
'<% } %>',
|
2017-12-25 09:20:13 +00:00
|
|
|
'<% if (isNotHeader) { %>',
|
2021-04-26 11:54:55 +00:00
|
|
|
'<div class="name not-header"><%= Common.Utils.String.htmlEncode(name) %></div>',
|
2017-12-25 09:20:13 +00:00
|
|
|
'<% } else if (isEmptyItem) { %>',
|
2017-12-25 09:03:51 +00:00
|
|
|
'<div class="name empty">' + options.emptyItemText + '</div>',
|
2017-12-21 12:01:37 +00:00
|
|
|
'<% } else { %>',
|
2021-04-26 11:54:55 +00:00
|
|
|
'<div class="name"><%= Common.Utils.String.htmlEncode(name) %></div>',
|
2017-12-21 12:01:37 +00:00
|
|
|
'<% } %>',
|
|
|
|
'</div>'
|
|
|
|
].join(''));
|
2017-12-19 13:30:58 +00:00
|
|
|
Common.UI.DataView.prototype.initialize.call(this, options);
|
|
|
|
},
|
|
|
|
|
2018-01-11 08:22:50 +00:00
|
|
|
onAddItem: function(record, store, opts) {
|
2017-12-19 13:30:58 +00:00
|
|
|
var view = new Common.UI.DataViewItem({
|
|
|
|
template: this.itemTemplate,
|
|
|
|
model: record
|
|
|
|
});
|
|
|
|
|
|
|
|
if (view) {
|
2019-07-29 11:24:20 +00:00
|
|
|
var innerEl = (this.$el || $(this.el)).find('.inner').addBack().filter('.inner');
|
2017-12-19 13:30:58 +00:00
|
|
|
if (innerEl) {
|
2018-09-18 13:22:30 +00:00
|
|
|
(this.dataViewItems.length<1) && innerEl.find('.empty-text').remove();
|
2018-01-11 08:22:50 +00:00
|
|
|
|
2021-07-14 16:47:47 +00:00
|
|
|
if (opts && (typeof opts.at==='number')) {
|
2018-01-11 08:22:50 +00:00
|
|
|
var idx = opts.at;
|
|
|
|
var innerDivs = innerEl.find('> div');
|
|
|
|
if (idx > 0)
|
|
|
|
$(innerDivs.get(idx - 1)).after(view.render().el);
|
|
|
|
else {
|
|
|
|
(innerDivs.length > 0) ? $(innerDivs[idx]).before(view.render().el) : innerEl.append(view.render().el);
|
|
|
|
}
|
|
|
|
this.dataViewItems = this.dataViewItems.slice(0, idx).concat(view).concat(this.dataViewItems.slice(idx));
|
|
|
|
} else {
|
|
|
|
innerEl.append(view.render().el);
|
|
|
|
this.dataViewItems.push(view);
|
|
|
|
}
|
2017-12-19 13:30:58 +00:00
|
|
|
|
2022-02-10 19:42:09 +00:00
|
|
|
this.updateTip(view);
|
2017-12-19 13:30:58 +00:00
|
|
|
this.listenTo(view, 'change', this.onChangeItem);
|
|
|
|
this.listenTo(view, 'remove', this.onRemoveItem);
|
|
|
|
this.listenTo(view, 'click', this.onClickItem);
|
|
|
|
this.listenTo(view, 'dblclick', this.onDblClickItem);
|
|
|
|
this.listenTo(view, 'select', this.onSelectItem);
|
|
|
|
this.listenTo(view, 'contextmenu', this.onContextMenuItem);
|
|
|
|
|
|
|
|
if (!this.isSuspendEvents)
|
|
|
|
this.trigger('item:add', this, view, record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onClickItem: function(view, record, e) {
|
|
|
|
var btn = $(e.target);
|
|
|
|
if (btn && btn.hasClass('tree-caret')) {
|
|
|
|
var tip = view.$el.data('bs.tooltip');
|
|
|
|
if (tip) (tip.tip()).remove();
|
|
|
|
|
|
|
|
var isExpanded = !record.get('isExpanded');
|
|
|
|
record.set('isExpanded', isExpanded);
|
2017-12-20 11:09:31 +00:00
|
|
|
this.store[(isExpanded) ? 'expandSubItems' : 'collapseSubItems'](record);
|
2018-03-16 10:07:18 +00:00
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
2017-12-19 13:30:58 +00:00
|
|
|
} else
|
|
|
|
Common.UI.DataView.prototype.onClickItem.call(this, view, record, e);
|
|
|
|
},
|
|
|
|
|
|
|
|
expandAll: function() {
|
2017-12-20 11:09:31 +00:00
|
|
|
this.store.expandAll();
|
2018-03-16 10:07:18 +00:00
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
2017-12-19 13:30:58 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
collapseAll: function() {
|
2017-12-20 11:09:31 +00:00
|
|
|
this.store.collapseAll();
|
2018-03-16 10:07:18 +00:00
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
2017-12-20 12:34:17 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
expandToLevel: function(expandLevel) {
|
|
|
|
this.store.expandToLevel(expandLevel);
|
2018-03-16 10:07:18 +00:00
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
2020-03-10 14:49:42 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
expandRecord: function(record) {
|
|
|
|
if (record) {
|
|
|
|
record.set('isExpanded', true);
|
|
|
|
this.store.expandSubItems(record);
|
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
collapseRecord: function(record) {
|
|
|
|
if (record) {
|
|
|
|
record.set('isExpanded', false);
|
|
|
|
this.store.collapseSubItems(record);
|
|
|
|
this.scroller.update({minScrollbarLength: 40, alwaysVisibleY: this.scrollAlwaysVisible});
|
|
|
|
}
|
2020-09-26 17:16:29 +00:00
|
|
|
},
|
|
|
|
|
2020-09-26 18:01:50 +00:00
|
|
|
onKeyDown: function (e, data) {
|
|
|
|
if ( this.disabled ) return;
|
|
|
|
if (data===undefined) data = e;
|
|
|
|
if (_.indexOf(this.moveKeys, data.keyCode)>-1 || data.keyCode==Common.UI.Keys.RETURN) {
|
|
|
|
data.preventDefault();
|
|
|
|
data.stopPropagation();
|
|
|
|
var rec = this.getSelectedRec();
|
2020-09-26 18:43:53 +00:00
|
|
|
if (this.lastSelectedRec===null)
|
|
|
|
this.lastSelectedRec = rec;
|
2020-09-26 18:01:50 +00:00
|
|
|
if (data.keyCode==Common.UI.Keys.RETURN) {
|
2020-09-26 18:43:53 +00:00
|
|
|
this.lastSelectedRec = null;
|
2020-09-26 18:01:50 +00:00
|
|
|
if (this.selectedBeforeHideRec) // only for ComboDataView menuPicker
|
|
|
|
rec = this.selectedBeforeHideRec;
|
|
|
|
this.trigger('item:click', this, this, rec, e);
|
2020-09-26 18:43:53 +00:00
|
|
|
this.trigger('item:select', this, this, rec, e);
|
|
|
|
this.trigger('entervalue', this, rec, e);
|
2020-09-26 18:01:50 +00:00
|
|
|
if (this.parentMenu)
|
|
|
|
this.parentMenu.hide();
|
|
|
|
} else {
|
|
|
|
var idx = _.indexOf(this.store.models, rec);
|
|
|
|
if (idx<0) {
|
|
|
|
if (data.keyCode==Common.UI.Keys.LEFT) {
|
|
|
|
var target = $(e.target).closest('.dropdown-submenu.over');
|
|
|
|
if (target.length>0) {
|
|
|
|
target.removeClass('over');
|
|
|
|
target.find('> a').focus();
|
|
|
|
} else
|
|
|
|
idx = 0;
|
|
|
|
} else
|
|
|
|
idx = 0;
|
|
|
|
} else if (this.options.keyMoveDirection == 'both') {
|
|
|
|
var hasSubItems = rec.get('hasSubItems');
|
|
|
|
var hasParent = rec.get('hasParent');
|
|
|
|
var isExpanded = rec.get('isExpanded');
|
|
|
|
if (data.keyCode==Common.UI.Keys.LEFT) {
|
|
|
|
if (hasSubItems && isExpanded)
|
|
|
|
this.collapseRecord(rec);
|
|
|
|
} else if (data.keyCode==Common.UI.Keys.RIGHT) {
|
|
|
|
if (hasSubItems && !isExpanded)
|
|
|
|
this.expandRecord(rec);
|
|
|
|
} else {
|
|
|
|
if (data.keyCode==Common.UI.Keys.DOWN) {
|
|
|
|
for (var i=idx+1; i<this.store.length; i++) {
|
|
|
|
if (this.store.at(i).get('isVisible')) {
|
|
|
|
idx=i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (data.keyCode==Common.UI.Keys.UP) {
|
|
|
|
for (var i=idx-1; i>=0; i--) {
|
|
|
|
if (this.store.at(i).get('isVisible')) {
|
|
|
|
idx=i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
idx = (data.keyCode==Common.UI.Keys.UP || data.keyCode==Common.UI.Keys.LEFT)
|
|
|
|
? Math.max(0, idx-1)
|
|
|
|
: Math.min(this.store.length - 1, idx + 1) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx !== undefined && idx>=0) rec = this.store.at(idx);
|
|
|
|
if (rec) {
|
|
|
|
this._fromKeyDown = true;
|
|
|
|
this.selectRecord(rec);
|
|
|
|
this._fromKeyDown = false;
|
|
|
|
this.scrollToRecord(rec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.trigger('item:keydown', this, rec, e);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-09-26 17:16:29 +00:00
|
|
|
focus: function() {
|
|
|
|
this.cmpEl && this.cmpEl.find('.treeview').focus();
|
2022-02-10 19:42:09 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
updateTip: function(item) {
|
|
|
|
var record = item.model,
|
|
|
|
name = record.get('name'),
|
|
|
|
me = this;
|
|
|
|
|
|
|
|
if (name.length > 37 - record.get('level')*2)
|
|
|
|
record.set('tip', name);
|
|
|
|
else
|
|
|
|
record.set('tip', '');
|
|
|
|
|
|
|
|
var el = item.$el || $(item.el);
|
|
|
|
var tip = el.data('bs.tooltip');
|
|
|
|
if (tip) {
|
|
|
|
if (tip.dontShow===undefined)
|
|
|
|
tip.dontShow = true;
|
|
|
|
el.removeData('bs.tooltip');
|
|
|
|
}
|
|
|
|
if (record.get('tip')) {
|
|
|
|
el.attr('data-toggle', 'tooltip');
|
|
|
|
el.tooltip({
|
|
|
|
title : record.get('tip'),
|
|
|
|
placement : 'cursor',
|
|
|
|
zIndex : this.tipZIndex
|
|
|
|
});
|
|
|
|
if (this.delayRenderTips)
|
|
|
|
el.one('mouseenter', function(){
|
|
|
|
el.attr('data-toggle', 'tooltip');
|
|
|
|
el.tooltip({
|
|
|
|
title : record.get('tip'),
|
|
|
|
placement : 'cursor',
|
|
|
|
zIndex : me.tipZIndex
|
|
|
|
});
|
|
|
|
el.mouseenter();
|
|
|
|
});
|
|
|
|
else {
|
|
|
|
el.attr('data-toggle', 'tooltip');
|
|
|
|
el.tooltip({
|
|
|
|
title : record.get('tip'),
|
|
|
|
placement : 'cursor',
|
|
|
|
zIndex : me.tipZIndex
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-12-19 13:30:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})());
|
|
|
|
});
|