/* * * (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 * */ /** * TabBar.js * * Created by Maxim Kadushkin on 28 March 2014 * Copyright (c) 2018 Ascensio System SIA. All rights reserved. * */ define([ 'common/main/lib/component/BaseView', 'common/main/lib/component/Tab' ], function () { 'use strict'; var Events = { bind: function () { if (!this.o) this.o = $({}); this.o.on.apply(this.o, arguments); }, unbind: function () { if (this.o) this.o.off.apply(this.o, arguments); }, trigger: function () { if (!this.o) this.o = $({}); this.o.trigger.apply(this.o, arguments); } }; var StateManager = function (options) { this.initialize.call(this, options); }; _.extend(StateManager.prototype, Events); StateManager.prototype.initialize = function (options) { this.bar = options.bar; }; StateManager.prototype.attach = function (tab) { tab.changeState = $.proxy(function (select) { if (select) { tab.toggleClass('selected'); var selectTab = _.find(this.bar.selectTabs, function (item) {return item.sheetindex === tab.sheetindex;}); if (selectTab) { this.bar.selectTabs = _.without(this.bar.selectTabs, selectTab); } else { this.bar.selectTabs.push(tab); } } else { if (!tab.isSelected()) { this.bar.$el.find('ul > li.selected').removeClass('selected'); tab.addClass('selected'); this.bar.selectTabs.length = 0; this.bar.selectTabs.push(tab); } this.trigger('tab:change', tab); this.bar.$el.find('ul > li.active').removeClass('active'); tab.activate(); this.bar.trigger('tab:changed', this.bar, this.bar.tabs.indexOf(tab), tab); } }, this); var dragHelper = new (function() { return { bounds: [], drag: undefined, calculateBounds: function () { var me = this, length = me.bar.tabs.length, barBounds = me.bar.$bar.get(0).getBoundingClientRect(); if (barBounds) { me.bounds = []; me.scrollLeft = me.bar.$bar.scrollLeft(); me.bar.scrollX = this.scrollLeft; for (var i = 0; i < length; ++i) { this.bounds.push(me.bar.tabs[i].$el.get(0).getBoundingClientRect()); } me.tabBarLeft = me.bounds[0].left; me.tabBarRight = me.bounds[length - 1].right; me.tabBarRight = Math.min(me.tabBarRight, barBounds.right - 1); } }, setAbsTabs: function () { var me = this, tab = null, length = this.bounds.length; for (var i = 0; i < length; ++i) { tab = me.bar.tabs[i].$el; tab.css('position', 'absolute'); tab.css('left', (me.bounds[i].left - me.tabBarLeft - this.scrollLeft) + 'px'); if (tab.hasClass('active')) { tab.css('top', '1px'); } else { tab.css('top', '0px'); } } }, updatePositions: function () { this.drag.place = undefined; var i, tabBound, center, place = -1, next = -this.scrollLeft, tabsCount = this.bounds.length, dragBound = this.drag.tab.$el.get(0).getBoundingClientRect(); if (this.drag.moveX - this.drag.mouseX > 0) { for (i = tabsCount - 1; i >= 0; --i) { tabBound = this.bounds[i]; center = (tabBound.right + tabBound.left) * 0.5; if (dragBound.left < center && center < dragBound.right) { place = i; break; } } if (-1 === place) { for (i = tabsCount - 1; i >= 0; --i) { tabBound = dragBound; center = (tabBound.right + tabBound.left) * 0.5; if (this.bounds[i].left < center && center < this.bounds[i].right) { place = i; break; } } } } else { for (i = 0; i < tabsCount; ++i) { tabBound = this.bounds[i]; center = (tabBound.right + tabBound.left) * 0.5; if (dragBound.left < center && center < dragBound.right) { place = i; break; } } if (-1 === place) { for (i = 0; i < tabsCount; ++i) { tabBound = dragBound; center = (tabBound.right + tabBound.left) * 0.5; if (this.bounds[i].left < center && center < this.bounds[i].right) { place = i; break; } } } } if (-1 !== place) { this.drag.place = place; for (i = 0; i < tabsCount; ++i) { if (i === place) { if (place < this.drag.index) { next += this.drag.tabWidth; } } if (place > this.drag.index) { if (i === place + 1) { next += this.drag.tabWidth; } } if (i !== this.drag.index) { this.bar.tabs[i].$el.css('left', next + 'px'); } else { if (this.drag.index === place) { next += this.drag.tabWidth; } continue; } next += this.bounds[i].width; } } }, setHook: function(e, bar, tab) { var me = this; function dragComplete() { if (!_.isUndefined(me.drag)) { me.drag.tab.removeClass('dragged'); me.drag.tab.$el.css('z-index', ''); me.bar.dragging = false; var tab = null; for (var i = me.bar.tabs.length - 1; i >= 0; --i) { tab = me.bar.tabs[i].$el; if (tab) { tab.css('top', ''); tab.css('position', ''); tab.css('left', ''); } } if (!_.isUndefined(me.drag.place)) { me.bar.trigger('tab:move', me.drag.index, me.drag.place); me.bar.$bar.scrollLeft(me.scrollLeft); me.bar.scrollX = undefined; } else { me.bar.trigger('tab:move', me.drag.index); me.bar.$bar.scrollLeft(me.scrollLeft); me.bar.scrollX = undefined; } me.drag = undefined; } } function dragMove (e) { if (!_.isUndefined(me.drag)) { me.drag.moveX = e.clientX*Common.Utils.zoom(); var leftPos = Math.max(me.drag.moveX - me.drag.anchorX - me.tabBarLeft - me.scrollLeft, 0); leftPos = Math.min(leftPos, me.tabBarRight - me.tabBarLeft - me.drag.tabWidth - me.scrollLeft); me.drag.tab.$el.css('left', leftPos + 'px'); me.drag.tab.$el.css('z-index','100'); me.updatePositions(); } } function dragDropText (e) { // disable firefox drag&drop e.preventDefault(); } if (!_.isUndefined(bar) && !_.isUndefined(tab) && bar.tabs.length > 1) { tab.addClass('dragged'); var index = bar.tabs.indexOf(tab), _clientX = e.clientX*Common.Utils.zoom(); me.bar = bar; me.drag = {tab: tab, index: index}; bar.dragging = true; this.calculateBounds(); this.setAbsTabs(); me.drag.moveX = _clientX; me.drag.mouseX = _clientX; me.drag.anchorX = _clientX - this.bounds[index].left; me.drag.tabWidth = this.bounds[index].width; document.addEventListener('dragstart',dragDropText); $(document).on('mousemove.tabbar', dragMove); $(document).on('mouseup.tabbar', function (e) { dragComplete(e); $(document).off('mouseup.tabbar'); $(document).off('mousemove.tabbar'); document.removeEventListener('dragstart',dragDropText); }); } }, setHookTabs: function (e, bar, tabs) { var me = this; function dragComplete() { if (!_.isUndefined(me.drag)) { bar.dragging = false; bar.$el.find('li.mousemove').removeClass('mousemove right'); var arrSelectIndex = []; tabs.forEach(function (item) { arrSelectIndex.push(item.sheetindex); }); if (!_.isUndefined(me.drag.place)) { me.bar.trigger('tab:move', arrSelectIndex, me.drag.place); me.bar.$bar.scrollLeft(me.scrollLeft); me.bar.scrollX = undefined; } else { me.bar.trigger('tab:move', arrSelectIndex); me.bar.$bar.scrollLeft(me.scrollLeft); me.bar.scrollX = undefined; } me.drag = undefined; } } function dragMove (event) { if (!_.isUndefined(me.drag)) { me.drag.moveX = event.clientX*Common.Utils.zoom(); if (me.drag.moveX > me.tabBarRight) { bar.tabs[bar.tabs.length - 1].$el.addClass('mousemove right'); me.drag.place = bar.tabs.length; } else { $(event.target).parent().parent().find('li.mousemove').removeClass('mousemove right'); $(event.target).parent().addClass('mousemove'); var name = event.target.parentElement.dataset.label, currentTab = _.findWhere(bar.tabs, {label: name}); if (!_.isUndefined(currentTab)) { me.drag.place = currentTab.sheetindex; } } } } if (!_.isUndefined(bar) && !_.isUndefined(tabs) && bar.tabs.length > 1) { me.bar = bar; me.drag = {tabs: tabs}; bar.dragging = true; this.calculateBounds(); $(document).on('mousemove.tabbar', dragMove); $(document).on('mouseup.tabbar', function (e) { dragComplete(e); $(document).off('mouseup.tabbar'); $(document).off('mousemove.tabbar', dragMove); }); } } } }); tab.$el.on({ click: $.proxy(function (event) { if (!tab.disabled) { if (event.ctrlKey || event.metaKey) { tab.changeState(true); } else if (event.shiftKey) { this.bar.$el.find('ul > li.selected').removeClass('selected'); this.bar.selectTabs.length = 0; var $active = this.bar.$el.find('ul > li.active'), indexAct = $active.index(), indexCur = this.bar.tabs.indexOf(tab); var startIndex = (indexCur > indexAct) ? indexAct : indexCur, endIndex = (indexCur > indexAct) ? indexCur : indexAct; for (var i = startIndex; i <= endIndex; i++) { this.bar.tabs[i].changeState(true); } } else if (!tab.$el.hasClass('active')) { if (this.bar.tabs.length === this.bar.selectTabs.length) { this.bar.$el.find('ul > li.selected').removeClass('selected'); this.bar.selectTabs.length = 0; } if (tab.control == 'manual') { this.bar.trigger('tab:manual', this.bar, this.bar.tabs.indexOf(tab), tab); } else { tab.changeState(); } } } !tab.disabled && Common.NotificationCenter.trigger('edit:complete', this.bar); }, this), dblclick: $.proxy(function() { this.trigger('tab:dblclick', this, this.tabs.indexOf(tab), tab); }, this.bar), contextmenu: $.proxy(function () { this.trigger('tab:contextmenu', this, this.tabs.indexOf(tab), tab, this.selectTabs); }, this.bar), mousedown: $.proxy(function (e) { if (this.bar.options.draggable && !_.isUndefined(dragHelper) && (3 !== e.which)) { if (!tab.isLockTheDrag) { if (this.bar.selectTabs.length > 1) { dragHelper.setHookTabs(e, this.bar, this.bar.selectTabs); } else { dragHelper.setHook(e, this.bar, tab); } } } }, this) }); }; StateManager.prototype.detach = function (tab) { tab.$el.off(); }; Common.UI.TabBar = Common.UI.BaseView.extend({ config: { placement : 'top', items : [], draggable : false }, tabs: [], template: _.template('