/* * * (c) Copyright Ascensio System SIA 2010-2019 * * 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 * * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha * street, Riga, Latvia, EU, LV-1050. * * 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 * */ /** * DataView.js * * A mechanism for displaying data using custom layout templates and formatting. * * Created by Alexander Yuzhin on 1/24/14 * Copyright (c) 2018 Ascensio System SIA. All rights reserved. * */ /** * The View uses an template as its internal templating mechanism, and is bound to an * {@link Common.UI.DataViewStore} so that as the data in the store changes the view is automatically updated * to reflect the changes. * * The example below binds a View to a {@link Common.UI.DataViewStore} and renders it into an el. * * new Common.UI.DataView({ * el: $('#id'), * store: new Common.UI.DataViewStore([{value: 1, value: 2}]), * itemTemplate: _.template(['
  • <%= value %>
  • '].join('')) * }); * * * @property {Object} el * Backbone el * * * @property {Object} store * The Store class encapsulates a client side cache of Model objects. * * * @property {String} emptyText * The text to display in the view when there is no data to display. * * * @cfg {Object} itemTemplate * The inner portion of the item template to be rendered. * */ if (Common === undefined) var Common = {}; define([ 'common/main/lib/component/BaseView', 'common/main/lib/component/Scroller' ], function () { 'use strict'; Common.UI.DataViewGroupModel = Backbone.Model.extend({ defaults: function() { return { id: Common.UI.getId(), caption: '', inline: false, headername: undefined } } }); Common.UI.DataViewGroupStore = Backbone.Collection.extend({ model: Common.UI.DataViewGroupModel }); Common.UI.DataViewModel = Backbone.Model.extend({ defaults: function() { return { id: Common.UI.getId(), selected: false, allowSelected: true, value: null, disabled: false } } }); Common.UI.DataViewStore = Backbone.Collection.extend({ model: Common.UI.DataViewModel }); Common.UI.DataViewItem = Common.UI.BaseView.extend({ options : { }, template: _.template([ '
    <%= value %>
    ' ].join('')), initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); var me = this; me.template = me.options.template || me.template; me.listenTo(me.model, 'change', me.render); me.listenTo(me.model, 'change:selected', me.onSelectChange); me.listenTo(me.model, 'remove', me.remove); }, render: function () { if (_.isUndefined(this.model.id)) return this; var el = this.$el || $(this.el); el.html(this.template(this.model.toJSON())); el.addClass('item'); el.toggleClass('selected', this.model.get('selected') && this.model.get('allowSelected')); el.off('click dblclick contextmenu'); el.on({ 'click': _.bind(this.onClick, this), 'dblclick': _.bind(this.onDblClick, this), 'contextmenu': _.bind(this.onContextMenu, this) }); el.toggleClass('disabled', !!this.model.get('disabled')); if (!_.isUndefined(this.model.get('cls'))) el.addClass(this.model.get('cls')); var tip = el.data('bs.tooltip'); if (tip) { if (tip.dontShow===undefined && el.is(':hover')) tip.dontShow = true; } this.trigger('change', this, this.model); return this; }, remove: function() { this.stopListening(this.model); this.trigger('remove', this, this.model); Common.UI.BaseView.prototype.remove.call(this); }, onClick: function(e) { if (this.model.get('disabled')) return false; this.trigger('click', this, this.model, e); }, onDblClick: function(e) { if (this.model.get('disabled')) return false; this.trigger('dblclick', this, this.model, e); }, onContextMenu: function(e) { this.trigger('contextmenu', this, this.model, e); }, onSelectChange: function(model, selected) { this.trigger('select', this, model, selected); } }); Common.UI.DataView = Common.UI.BaseView.extend({ options : { multiSelect: false, handleSelect: true, enableKeyEvents: true, keyMoveDirection: 'both', // 'vertical', 'horizontal' restoreHeight: 0, emptyText: '', listenStoreEvents: true, allowScrollbar: true, scrollAlwaysVisible: false, showLast: true, useBSKeydown: false, cls: '' }, template: _.template([ '
    ', '<% _.each(groups, function(group) { %>', '<% if (group.headername !== undefined) { %>', '
    <%= group.headername %>
    ', '<% } %>', '
    ', '<% if (!_.isEmpty(group.caption)) { %>', '
    ', '<%= group.caption %>', '
    ', '<% } %>', '
    ', '
    ', '
    ', '<% }); %>', '
    ' ].join('')), initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); var me = this; me.template = me.options.template || me.template; me.store = me.options.store || new Common.UI.DataViewStore(); me.groups = me.options.groups || null; me.itemTemplate = me.options.itemTemplate || null; me.multiSelect = me.options.multiSelect; me.handleSelect = me.options.handleSelect; me.parentMenu = me.options.parentMenu; me.enableKeyEvents= me.options.enableKeyEvents; me.useBSKeydown = me.options.useBSKeydown; // only with enableKeyEvents && parentMenu me.showLast = me.options.showLast; me.style = me.options.style || ''; me.cls = me.options.cls || ''; me.emptyText = me.options.emptyText || ''; me.listenStoreEvents= (me.options.listenStoreEvents!==undefined) ? me.options.listenStoreEvents : true; me.allowScrollbar = (me.options.allowScrollbar!==undefined) ? me.options.allowScrollbar : true; me.scrollAlwaysVisible = me.options.scrollAlwaysVisible || false; me.tabindex = me.options.tabindex || 0; if (me.parentMenu) me.parentMenu.options.restoreHeight = (me.options.restoreHeight>0); me.rendered = false; me.dataViewItems = []; if (me.options.keyMoveDirection=='vertical') me.moveKeys = [Common.UI.Keys.UP, Common.UI.Keys.DOWN]; else if (me.options.keyMoveDirection=='horizontal') me.moveKeys = [Common.UI.Keys.LEFT, Common.UI.Keys.RIGHT]; else me.moveKeys = [Common.UI.Keys.UP, Common.UI.Keys.DOWN, Common.UI.Keys.LEFT, Common.UI.Keys.RIGHT]; if (me.options.el) me.render(); }, render: function (parentEl) { var me = this; this.trigger('render:before', this); if (parentEl) { this.setElement(parentEl, false); this.cmpEl = $(this.template({ groups: me.groups ? me.groups.toJSON() : null, style: me.style, cls: me.cls })); parentEl.html(this.cmpEl); } else { this.cmpEl = me.$el || $(this.el); this.cmpEl.html(this.template({ groups: me.groups ? me.groups.toJSON() : null, style: me.style, cls: me.cls })); } var modalParents = this.cmpEl.closest('.asc-window'); if (modalParents.length < 1) modalParents = this.cmpEl.closest('[id^="menu-container-"]'); // context menu if (modalParents.length > 0) { this.tipZIndex = parseInt(modalParents.css('z-index')) + 10; } if (!this.rendered) { if (this.listenStoreEvents) { this.listenTo(this.store, 'add', this.onAddItem); this.listenTo(this.store, 'reset', this.onResetItems); } this.onResetItems(); if (this.parentMenu) { this.cmpEl.closest('li').css('height', '100%'); this.cmpEl.css('height', '100%'); this.parentMenu.on('show:after', _.bind(this.alignPosition, this)); } if (this.enableKeyEvents && this.parentMenu && this.handleSelect) { if (!me.showLast) this.parentMenu.on('show:before', function(menu) { me.deselectAll(); }); this.parentMenu.on('show:after', function(menu) { if (me.showLast) me.showLastSelected(); Common.NotificationCenter.trigger('dataview:focus'); _.delay(function() { menu.cmpEl.find('.dataview').focus(); }, 10); }).on('hide:after', function() { Common.NotificationCenter.trigger('dataview:blur'); }); } } if (_.isUndefined(this.scroller) && this.allowScrollbar) { this.scroller = new Common.UI.Scroller({ el: $(this.el).find('.inner').addBack().filter('.inner'), useKeyboard: this.enableKeyEvents && !this.handleSelect, minScrollbarLength : 40, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible }); } this.rendered = true; this.cmpEl.on('click', function(e){ if (/dataview/.test(e.target.className)) return false; }); this.trigger('render:after', this); return this; }, setStore: function(store) { if (store) { this.stopListening(this.store); this.store = store; if (this.listenStoreEvents) { this.listenTo(this.store, 'add', this.onAddItem); this.listenTo(this.store, 'reset', this.onResetItems); } } }, selectRecord: function(record, suspendEvents) { if (!this.handleSelect) return; if (suspendEvents) this.suspendEvents(); if (!this.multiSelect) { _.each(this.store.where({selected: true}), function(rec){ rec.set({selected: false}); }); if (record) record.set({selected: true}); } else { if (record) record.set({selected: !record.get('selected')}); } if (suspendEvents) this.resumeEvents(); return record; }, selectByIndex: function(index, suspendEvents) { if (this.store.length > 0 && index > -1 && index < this.store.length) { return this.selectRecord(this.store.at(index), suspendEvents); } }, deselectAll: function(suspendEvents) { if (suspendEvents) this.suspendEvents(); _.each(this.store.where({selected: true}), function(record){ record.set({selected: false}); }); if (suspendEvents) this.resumeEvents(); }, getSelectedRec: function() { return (this.multiSelect) ? this.store.where({selected: true}) : this.store.findWhere({selected: true}); }, onAddItem: function(record, store, opts) { var view = new Common.UI.DataViewItem({ template: this.itemTemplate, model: record }); if (view) { var innerEl = $(this.el).find('.inner').addBack().filter('.inner'); if (this.groups && this.groups.length > 0) { var group = this.groups.findWhere({id: record.get('group')}); if (group) { innerEl = innerEl.find('#' + group.id + ' ' + '.group-items-container'); } } if (innerEl) { if (opts && opts.at == 0) innerEl.prepend(view.render().el); else innerEl.append(view.render().el); (this.dataViewItems.length<1) && innerEl.find('.empty-text').remove(); var idx = _.indexOf(this.store.models, record); this.dataViewItems = this.dataViewItems.slice(0, idx).concat(view).concat(this.dataViewItems.slice(idx)); if (record.get('tip')) { var view_el = $(view.el); view_el.attr('data-toggle', 'tooltip'); view_el.tooltip({ title : record.get('tip'), placement : 'cursor', zIndex : this.tipZIndex }); } 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); } } }, onResetItems: function() { _.each(this.dataViewItems, function(item) { var tip = item.$el.data('bs.tooltip'); if (tip) { if (tip.dontShow===undefined) tip.dontShow = true; (tip.tip()).remove(); } }, this); $(this.el).html(this.template({ groups: this.groups ? this.groups.toJSON() : null, style: this.style, cls: this.cls })); if (!_.isUndefined(this.scroller)) { this.scroller.destroy(); delete this.scroller; } if (this.store.length < 1 && this.emptyText.length > 0) $(this.el).find('.inner').addBack().filter('.inner').append('
    ' + this.emptyText + '
    '); _.each(this.dataViewItems, function(item) { this.stopListening(item); item.stopListening(item.model); }, this); this.dataViewItems = []; this.store.each(this.onAddItem, this); if (this.allowScrollbar) { this.scroller = new Common.UI.Scroller({ el: $(this.el).find('.inner').addBack().filter('.inner'), useKeyboard: this.enableKeyEvents && !this.handleSelect, minScrollbarLength : 40, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible }); } if (this.disabled) this.setDisabled(this.disabled); this.attachKeyEvents(); this.lastSelectedRec = null; this._layoutParams = undefined; }, onChangeItem: function(view, record) { if (!this.isSuspendEvents) { this.trigger('item:change', this, view, record); } }, onRemoveItem: function(view, record) { var tip = view.$el.data('bs.tooltip'); if (tip) { if (tip.dontShow===undefined) tip.dontShow = true; (tip.tip()).remove(); } this.stopListening(view); view.stopListening(); if (this.store.length < 1 && this.emptyText.length > 0) { var el = $(this.el).find('.inner').addBack().filter('.inner'); if ( el.find('.empty-text').length<=0 ) el.append('
    ' + this.emptyText + '
    '); } for (var i=0; i < this.dataViewItems.length; i++) { if (_.isEqual(view, this.dataViewItems[i]) ) { this.dataViewItems.splice(i, 1); break; } } if (!this.isSuspendEvents) { this.trigger('item:remove', this, view, record); } }, onClickItem: function(view, record, e) { if ( this.disabled ) return; window._event = e; // for FireFox only if (this.showLast) this.selectRecord(record); this.lastSelectedRec = null; var tip = view.$el.data('bs.tooltip'); if (tip) (tip.tip()).remove(); if (!this.isSuspendEvents) { this.trigger('item:click', this, view, record, e); } }, onDblClickItem: function(view, record, e) { if ( this.disabled ) return; window._event = e; // for FireFox only if (this.showLast) this.selectRecord(record); this.lastSelectedRec = null; if (!this.isSuspendEvents) { this.trigger('item:dblclick', this, view, record, e); } }, onSelectItem: function(view, record, selected) { if (!this.isSuspendEvents) { this.trigger(selected ? 'item:select' : 'item:deselect', this, view, record, this._fromKeyDown); } }, onContextMenuItem: function(view, record, e) { if (!this.isSuspendEvents) { this.trigger('item:contextmenu', this, view, record, e); } }, scrollToRecord: function (record, force) { if (!record) return; var innerEl = $(this.el).find('.inner'), inner_top = innerEl.offset().top, idx = _.indexOf(this.store.models, record), div = (idx>=0 && this.dataViewItems.length>idx) ? $(this.dataViewItems[idx].el) : innerEl.find('#' + record.get('id')); if (div.length<=0) return; var div_top = div.offset().top, div_first = $(this.dataViewItems[0].el), div_first_top = (div_first.length>0) ? div_first[0].clientTop : 0; if (force || div_top < inner_top + div_first_top || div_top+div.outerHeight()*0.9 > inner_top + div_first_top + innerEl.height()) { if (this.scroller && this.allowScrollbar) { this.scroller.scrollTop(innerEl.scrollTop() + div_top - inner_top - div_first_top, 0); } else { innerEl.scrollTop(innerEl.scrollTop() + div_top - inner_top - div_first_top); } } }, 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(); if (this.lastSelectedRec===null) this.lastSelectedRec = rec; if (data.keyCode==Common.UI.Keys.RETURN) { this.lastSelectedRec = null; if (this.selectedBeforeHideRec) // only for ComboDataView menuPicker rec = this.selectedBeforeHideRec; this.trigger('item:click', this, this, rec, e); this.trigger('item:select', this, this, rec, e); this.trigger('entervalue', this, rec, e); 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') { if (this._layoutParams === undefined) this.fillIndexesArray(); var topIdx = this.dataViewItems[idx].topIdx, leftIdx = this.dataViewItems[idx].leftIdx; idx = undefined; if (data.keyCode==Common.UI.Keys.LEFT) { while (idx===undefined) { leftIdx--; if (leftIdx<0) { var target = $(e.target).closest('.dropdown-submenu.over'); if (target.length>0) { target.removeClass('over'); target.find('> a').focus(); break; } else leftIdx = this._layoutParams.columns-1; } idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else if (data.keyCode==Common.UI.Keys.RIGHT) { while (idx===undefined) { leftIdx++; if (leftIdx>this._layoutParams.columns-1) leftIdx = 0; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else if (data.keyCode==Common.UI.Keys.UP) { while (idx===undefined) { topIdx--; if (topIdx<0) topIdx = this._layoutParams.rows-1; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else { while (idx===undefined) { topIdx++; if (topIdx>this._layoutParams.rows-1) topIdx = 0; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } } 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.scrollToRecord(rec); this._fromKeyDown = false; } } } else { this.trigger('item:keydown', this, rec, e); } }, attachKeyEvents: function() { if (this.enableKeyEvents && this.handleSelect) { var el = $(this.el).find('.inner').addBack().filter('.inner'); el.addClass('canfocused'); el.attr('tabindex', this.tabindex.toString()); el.on((this.parentMenu && this.useBSKeydown) ? 'dataview:keydown' : 'keydown', _.bind(this.onKeyDown, this)); } }, showLastSelected: function() { if ( this.lastSelectedRec) { this.selectRecord(this.lastSelectedRec, true); this.scrollToRecord(this.lastSelectedRec); this.lastSelectedRec = null; } else { this.scrollToRecord(this.getSelectedRec()); } }, setDisabled: function(disabled) { this.disabled = disabled; $(this.el).find('.inner').addBack().filter('.inner').toggleClass('disabled', disabled); }, isDisabled: function() { return this.disabled; }, setEmptyText: function(emptyText) { this.emptyText = emptyText; }, alignPosition: function() { var menuRoot = (this.parentMenu.cmpEl.attr('role') === 'menu') ? this.parentMenu.cmpEl : this.parentMenu.cmpEl.find('[role=menu]'), docH = Common.Utils.innerHeight()-10, innerEl = $(this.el).find('.inner').addBack().filter('.inner'), parent = innerEl.parent(), margins = parseInt(parent.css('margin-top')) + parseInt(parent.css('margin-bottom')) + parseInt(menuRoot.css('margin-top')), paddings = parseInt(menuRoot.css('padding-top')) + parseInt(menuRoot.css('padding-bottom')), menuH = menuRoot.outerHeight(), top = parseInt(menuRoot.css('top')), props = {minScrollbarLength : 40}; this.scrollAlwaysVisible && (props.alwaysVisibleY = this.scrollAlwaysVisible); if (top + menuH > docH ) { innerEl.css('max-height', (docH - top - paddings - margins) + 'px'); if (this.allowScrollbar) this.scroller.update(props); } else if ( top + menuH < docH && innerEl.height() < this.options.restoreHeight ) { innerEl.css('max-height', (Math.min(docH - top - paddings - margins, this.options.restoreHeight)) + 'px'); if (this.allowScrollbar) this.scroller.update(props); } }, fillIndexesArray: function() { if (this.dataViewItems.length<=0) return; this._layoutParams = { itemsIndexes: [], columns: 0, rows: 0 }; var el = $(this.dataViewItems[0].el), itemW = el.outerWidth() + parseInt(el.css('margin-left')) + parseInt(el.css('margin-right')), offsetLeft = this.$el.offset().left, offsetTop = el.offset().top, prevtop = -1, topIdx = 0, leftIdx = 0; for (var i=0; iprevtop) { prevtop = top; this._layoutParams.itemsIndexes.push([]); topIdx = this._layoutParams.itemsIndexes.length-1; } this._layoutParams.itemsIndexes[topIdx][leftIdx] = i; this.dataViewItems[i].topIdx = topIdx; this.dataViewItems[i].leftIdx = leftIdx; if (this._layoutParams.columns', '<% _.each(items, function(item) { %>', '<% if (!item.id) item.id = Common.UI.getId(); %>', '
    data-toggle="tooltip" <% } %> ><%= itemTemplate(item) %>
    ', '<% }) %>', '' ].join('')), initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); var me = this; me.template = me.options.template || me.template; me.store = me.options.store || new Common.UI.DataViewStore(); me.itemTemplate = me.options.itemTemplate || null; me.handleSelect = me.options.handleSelect; me.parentMenu = me.options.parentMenu; me.enableKeyEvents= me.options.enableKeyEvents; me.useBSKeydown = me.options.useBSKeydown; // only with enableKeyEvents && parentMenu me.style = me.options.style || ''; me.scrollAlwaysVisible = me.options.scrollAlwaysVisible || false; me.tabindex = me.options.tabindex || 0; if (me.parentMenu) me.parentMenu.options.restoreHeight = (me.options.restoreHeight>0); me.rendered = false; if (me.options.keyMoveDirection=='vertical') me.moveKeys = [Common.UI.Keys.UP, Common.UI.Keys.DOWN]; else if (me.options.keyMoveDirection=='horizontal') me.moveKeys = [Common.UI.Keys.LEFT, Common.UI.Keys.RIGHT]; else me.moveKeys = [Common.UI.Keys.UP, Common.UI.Keys.DOWN, Common.UI.Keys.LEFT, Common.UI.Keys.RIGHT]; if (me.options.el) me.render(); }, render: function (parentEl) { var me = this; this.trigger('render:before', this); if (parentEl) { this.setElement(parentEl, false); this.cmpEl = $(this.template({ items: me.store.toJSON(), itemTemplate: me.itemTemplate, style: me.style })); parentEl.html(this.cmpEl); } else { this.cmpEl = me.$el || $(this.el); this.cmpEl.html(this.template({ items: me.store.toJSON(), itemTemplate: me.itemTemplate, style: me.style })); } var modalParents = this.cmpEl.closest('.asc-window'); if (modalParents.length < 1) modalParents = this.cmpEl.closest('[id^="menu-container-"]'); // context menu if (modalParents.length > 0) { this.tipZIndex = parseInt(modalParents.css('z-index')) + 10; } if (!this.rendered) { if (this.parentMenu) { this.cmpEl.closest('li').css('height', '100%'); this.cmpEl.css('height', '100%'); this.parentMenu.on('show:after', _.bind(this.alignPosition, this)); this.parentMenu.on('show:after', _.bind(this.onAfterShowMenu, this)); } else if (this.store.length>0) this.onAfterShowMenu(); if (this.enableKeyEvents && this.parentMenu && this.handleSelect) { this.parentMenu.on('show:before', function(menu) { me.deselectAll(); }); this.parentMenu.on('show:after', function(menu) { Common.NotificationCenter.trigger('dataview:focus'); _.delay(function() { menu.cmpEl.find('.dataview').focus(); }, 10); }).on('hide:after', function() { Common.NotificationCenter.trigger('dataview:blur'); }); } this.attachKeyEvents(); this.cmpEl.on( "click", "div.item", _.bind(me.onClickItem, me)); } if (_.isUndefined(this.scroller)) { this.scroller = new Common.UI.Scroller({ el: $(this.el).find('.inner').addBack().filter('.inner'), useKeyboard: this.enableKeyEvents && !this.handleSelect, minScrollbarLength : 40, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible }); } this.rendered = true; this.cmpEl.on('click', function(e){ if (/dataview/.test(e.target.className)) return false; }); this.trigger('render:after', this); return this; }, selectRecord: function(record, suspendEvents) { if (!this.handleSelect) return; if (suspendEvents) this.suspendEvents(); this.deselectAll(suspendEvents); if (record) { record.set({selected: true}); var idx = _.indexOf(this.store.models, record); if (idx>=0 && this.dataViewItems && this.dataViewItems.length>idx) { this.dataViewItems[idx].el.addClass('selected'); } } if (suspendEvents) this.resumeEvents(); return record; }, selectByIndex: function(index, suspendEvents) { if (this.store.length > 0 && index > -1 && index < this.store.length) { return this.selectRecord(this.store.at(index), suspendEvents); } }, deselectAll: function(suspendEvents) { if (suspendEvents) this.suspendEvents(); _.each(this.store.where({selected: true}), function(record){ record.set({selected: false}); }); this.cmpEl.find('.item.selected').removeClass('selected'); if (suspendEvents) this.resumeEvents(); }, getSelectedRec: function() { return this.store.findWhere({selected: true}); }, onResetItems: function() { this.dataViewItems && _.each(this.dataViewItems, function(item) { var tip = item.el.data('bs.tooltip'); if (tip) { if (tip.dontShow===undefined) tip.dontShow = true; (tip.tip()).remove(); } }, this); this.dataViewItems = null; var template = _.template([ '<% _.each(items, function(item) { %>', '<% if (!item.id) item.id = Common.UI.getId(); %>', '
    data-toggle="tooltip" <% } %> ><%= itemTemplate(item) %>
    ', '<% }) %>' ].join('')); this.cmpEl && this.cmpEl.find('.inner').html(template({ items: this.store.toJSON(), itemTemplate: this.itemTemplate, style : this.style })); if (!_.isUndefined(this.scroller)) { this.scroller.destroy(); delete this.scroller; } this.scroller = new Common.UI.Scroller({ el: $(this.el).find('.inner').addBack().filter('.inner'), useKeyboard: this.enableKeyEvents && !this.handleSelect, minScrollbarLength : 40, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible }); if (!this.parentMenu && this.store.length>0) this.onAfterShowMenu(); this._layoutParams = undefined; }, setStore: function(store) { if (store) { this.store = store; this.onResetItems(); } }, onClickItem: function(e) { if ( this.disabled ) return; window._event = e; // for FireFox only var index = $(e.currentTarget).closest('div.item').index(), record = (index>=0) ? this.store.at(index) : null, view = (index>=0) ? this.dataViewItems[index] : null; if (!record || !view) return; record.set({selected: true}); var tip = view.el.data('bs.tooltip'); if (tip) (tip.tip()).remove(); if (!this.isSuspendEvents) { this.trigger('item:click', this, view.el, record, e); } }, onAfterShowMenu: function(e) { if (!this.dataViewItems) { var me = this; this.dataViewItems = []; _.each(this.cmpEl.find('div.item'), function(item, index) { var $item = $(item), rec = me.store.at(index); me.dataViewItems.push({el: $item}); if (rec.get('tip')) { $item.tooltip({ title : rec.get('tip'), placement : 'cursor', zIndex : me.tipZIndex }); } }); } }, scrollToRecord: function (record) { if (!record) return; var innerEl = $(this.el).find('.inner'), inner_top = innerEl.offset().top, idx = _.indexOf(this.store.models, record), div = (idx>=0 && this.dataViewItems.length>idx) ? this.dataViewItems[idx].el : innerEl.find('#' + record.get('id')); if (div.length<=0) return; var div_top = div.offset().top, div_first = this.dataViewItems[0].el, div_first_top = (div_first.length>0) ? div_first[0].offsetTop : 0; if (div_top < inner_top + div_first_top || div_top+div.outerHeight() > inner_top + innerEl.height()) { if (this.scroller) { this.scroller.scrollTop(innerEl.scrollTop() + div_top - inner_top - div_first_top, 0); } else { innerEl.scrollTop(innerEl.scrollTop() + div_top - inner_top - div_first_top); } } }, 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(); if (data.keyCode==Common.UI.Keys.RETURN) { if (this.selectedBeforeHideRec) // only for ComboDataView menuPicker rec = this.selectedBeforeHideRec; this.trigger('item:click', this, this, rec, e); 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') { if (this._layoutParams === undefined) this.fillIndexesArray(); var topIdx = this.dataViewItems[idx].topIdx, leftIdx = this.dataViewItems[idx].leftIdx; idx = undefined; if (data.keyCode==Common.UI.Keys.LEFT) { while (idx===undefined) { leftIdx--; if (leftIdx<0) { var target = $(e.target).closest('.dropdown-submenu.over'); if (target.length>0) { target.removeClass('over'); target.find('> a').focus(); break; } else leftIdx = this._layoutParams.columns-1; } idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else if (data.keyCode==Common.UI.Keys.RIGHT) { while (idx===undefined) { leftIdx++; if (leftIdx>this._layoutParams.columns-1) leftIdx = 0; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else if (data.keyCode==Common.UI.Keys.UP) { while (idx===undefined) { topIdx--; if (topIdx<0) topIdx = this._layoutParams.rows-1; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } else { while (idx===undefined) { topIdx++; if (topIdx>this._layoutParams.rows-1) topIdx = 0; idx = this._layoutParams.itemsIndexes[topIdx][leftIdx]; } } } 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.scrollToRecord(rec); this._fromKeyDown = false; } } } else { this.trigger('item:keydown', this, rec, e); } }, attachKeyEvents: function() { if (this.enableKeyEvents && this.handleSelect) { var el = $(this.el).find('.inner').addBack().filter('.inner'); el.addClass('canfocused'); el.attr('tabindex', this.tabindex.toString()); el.on((this.parentMenu && this.useBSKeydown) ? 'dataview:keydown' : 'keydown', _.bind(this.onKeyDown, this)); } }, setDisabled: function(disabled) { this.disabled = disabled; $(this.el).find('.inner').addBack().filter('.inner').toggleClass('disabled', disabled); }, isDisabled: function() { return this.disabled; }, alignPosition: function() { var menuRoot = (this.parentMenu.cmpEl.attr('role') === 'menu') ? this.parentMenu.cmpEl : this.parentMenu.cmpEl.find('[role=menu]'), docH = Common.Utils.innerHeight()-10, innerEl = $(this.el).find('.inner').addBack().filter('.inner'), parent = innerEl.parent(), margins = parseInt(parent.css('margin-top')) + parseInt(parent.css('margin-bottom')) + parseInt(menuRoot.css('margin-top')), paddings = parseInt(menuRoot.css('padding-top')) + parseInt(menuRoot.css('padding-bottom')), menuH = menuRoot.outerHeight(), top = parseInt(menuRoot.css('top')), props = {minScrollbarLength : 40}; this.scrollAlwaysVisible && (props.alwaysVisibleY = this.scrollAlwaysVisible); if (top + menuH > docH ) { innerEl.css('max-height', (docH - top - paddings - margins) + 'px'); this.scroller.update(props); } else if ( top + menuH < docH && innerEl.height() < this.options.restoreHeight ) { innerEl.css('max-height', (Math.min(docH - top - paddings - margins, this.options.restoreHeight)) + 'px'); this.scroller.update(props); } }, fillIndexesArray: function() { if (this.dataViewItems.length<=0) return; this._layoutParams = { itemsIndexes: [], columns: 0, rows: 0 }; var el = this.dataViewItems[0].el, itemW = el.outerWidth() + parseInt(el.css('margin-left')) + parseInt(el.css('margin-right')), offsetLeft = this.$el.offset().left, offsetTop = el.offset().top, prevtop = -1, topIdx = 0, leftIdx = 0; for (var i=0; iprevtop) { prevtop = top; this._layoutParams.itemsIndexes.push([]); topIdx = this._layoutParams.itemsIndexes.length-1; } this._layoutParams.itemsIndexes[topIdx][leftIdx] = i; item.topIdx = topIdx; item.leftIdx = leftIdx; if (this._layoutParams.columns