diff --git a/apps/api/documents/api.js b/apps/api/documents/api.js index 74667b038..59985afe8 100644 --- a/apps/api/documents/api.js +++ b/apps/api/documents/api.js @@ -128,7 +128,8 @@ compactHeader: false, toolbarNoTabs: false, toolbarHideFileName: false, - reviewDisplay: 'original' + reviewDisplay: 'original', + spellcheck: true }, plugins: { autostart: ['asc.{FFE1F462-1EA2-4391-990D-4CC84940B754}'], @@ -735,8 +736,16 @@ : config.type === "embedded" ? "embed" : "main"; - path += "/index.html"; + var index = "/index.html"; + if (config.editorConfig) { + var customization = config.editorConfig.customization; + if ( typeof(customization) == 'object' && ( customization.toolbarNoTabs || + (config.editorConfig.targetApp!=='desktop') && (customization.loaderName || customization.loaderLogo))) { + index = "/index_loader.html"; + } + } + path += index; return path; } @@ -753,9 +762,17 @@ params += "&customer=ONLYOFFICE"; if ( (typeof(config.editorConfig.customization) == 'object') && config.editorConfig.customization.loaderLogo) { if (config.editorConfig.customization.loaderLogo !== '') params += "&logo=" + config.editorConfig.customization.loaderLogo; + } else if ( (typeof(config.editorConfig.customization) == 'object') && config.editorConfig.customization.logo) { + if (config.type=='embedded' && config.editorConfig.customization.logo.imageEmbedded) + params += "&headerlogo=" + config.editorConfig.customization.logo.imageEmbedded; + else if (config.type!='embedded' && config.editorConfig.customization.logo.image) + params += "&headerlogo=" + config.editorConfig.customization.logo.image; } } + if (config.editorConfig && (config.editorConfig.mode == 'editdiagram' || config.editorConfig.mode == 'editmerge')) + params += "&internal=true"; + if (config.frameEditorId) params += "&frameEditorId=" + config.frameEditorId; diff --git a/apps/common/locale.js b/apps/common/locale.js index 3e3bdda3b..9cf5cbf96 100644 --- a/apps/common/locale.js +++ b/apps/common/locale.js @@ -36,27 +36,34 @@ if (Common === undefined) { Common.Locale = new(function() { "use strict"; - var l10n = {}; + var l10n = null; + var loadcallback, + apply = false; - var _applyLocalization = function() { + var _applyLocalization = function(callback) { try { - for (var prop in l10n) { - var p = prop.split('.'); - if (p && p.length > 2) { + callback && (loadcallback = callback); + if (l10n) { + for (var prop in l10n) { + var p = prop.split('.'); + if (p && p.length > 2) { - var obj = window; - for (var i = 0; i < p.length - 1; ++i) { - if (obj[p[i]] === undefined) { - obj[p[i]] = new Object(); + var obj = window; + for (var i = 0; i < p.length - 1; ++i) { + if (obj[p[i]] === undefined) { + obj[p[i]] = new Object(); + } + obj = obj[p[i]]; } - obj = obj[p[i]]; - } - if (obj) { - obj[p[p.length - 1]] = l10n[prop]; + if (obj) { + obj[p[p.length - 1]] = l10n[prop]; + } } } - } + loadcallback && loadcallback(); + } else + apply = true; } catch (e) { } @@ -64,7 +71,7 @@ Common.Locale = new(function() { var _get = function(prop, scope) { var res = ''; - if (scope && scope.name) { + if (l10n && scope && scope.name) { res = l10n[scope.name + '.' + prop]; } @@ -99,10 +106,12 @@ Common.Locale = new(function() { throw new Error('loaded'); } }).then(function(json) { - if ( !!json ) l10n = json; + l10n = json || {}; + apply && _applyLocalization(); }).catch(function(e) { + l10n = l10n || {}; + apply && _applyLocalization(); if ( e.message == 'loaded' ) { - } else console.log('fetch error: ' + e); }); @@ -110,7 +119,13 @@ Common.Locale = new(function() { if ( !window.fetch ) { /* use fetch polifill if native method isn't supported */ - require(['../vendor/fetch/fetch.umd'], _requireLang); + var polyfills = ['../vendor/fetch/fetch.umd']; + if ( !window.Promise ) { + require(['../vendor/es6-promise/es6-promise.auto.min'], + function () { + require(polyfills, _requireLang); + }); + } else require(polyfills, _requireLang); } else _requireLang(); return { diff --git a/apps/common/main/lib/component/Button.js b/apps/common/main/lib/component/Button.js index 816c88988..e097848f9 100644 --- a/apps/common/main/lib/component/Button.js +++ b/apps/common/main/lib/component/Button.js @@ -319,7 +319,7 @@ define([ me.trigger('render:before', me); - me.cmpEl = $(me.el); + me.cmpEl = me.$el || $(me.el); if (parentEl) { me.setElement(parentEl, false); @@ -386,6 +386,18 @@ define([ if (modalParents.length > 0) { me.btnEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10); me.btnMenuEl && me.btnMenuEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10); + var onModalClose = function(dlg) { + if (modalParents[0] !== dlg.$window[0]) return; + var tip = me.btnEl.data('bs.tooltip'); + if (tip) { + if (tip.dontShow===undefined) + tip.dontShow = true; + + tip.hide(); + } + Common.NotificationCenter.off({'modal:close': onModalClose}); + }; + Common.NotificationCenter.on({'modal:close': onModalClose}); } } diff --git a/apps/common/main/lib/component/CheckBox.js b/apps/common/main/lib/component/CheckBox.js index 5aaa8a112..0d3d35c75 100644 --- a/apps/common/main/lib/component/CheckBox.js +++ b/apps/common/main/lib/component/CheckBox.js @@ -104,27 +104,25 @@ define([ }, render: function (parentEl) { - var me = this, - el = $(this.el); + var me = this; if (!me.rendered) { if (parentEl) { this.setElement(parentEl, false); parentEl.html(this.template({ labelText: this.options.labelText })); - el = $(this.el); } else { - el.html(this.template({ + me.$el.html(this.template({ labelText: this.options.labelText })); } - this.$chk = el.find('input[type=button]'); - this.$label = el.find('label'); - this.$chk.on('click', _.bind(this.onItemCheck, this)); - } + this.$chk = me.$el.find('input[type=button]'); + this.$label = me.$el.find('label'); + this.$chk.on('click', this.onItemCheck.bind(this)); - this.rendered = true; + this.rendered = true; + } if (this.options.disabled) this.setDisabled(this.options.disabled); diff --git a/apps/common/main/lib/component/ColorPalette.js b/apps/common/main/lib/component/ColorPalette.js index 106cfb761..ba35ca857 100644 --- a/apps/common/main/lib/component/ColorPalette.js +++ b/apps/common/main/lib/component/ColorPalette.js @@ -91,16 +91,14 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + me.$el.html(this.cmpEl); } } else { - this.cmpEl = $(this.el); + this.cmpEl = me.$el || $(this.el); } if (!me.rendered) { - var el = this.cmpEl; - - el.on('click', 'span.color-item', _.bind(this.itemClick, this)); + me.cmpEl.on('click', 'span.color-item', me.itemClick.bind(me)); } me.rendered = true; diff --git a/apps/common/main/lib/component/ColorPaletteExt.js b/apps/common/main/lib/component/ColorPaletteExt.js index 98eadf69d..a6b7e9c9a 100644 --- a/apps/common/main/lib/component/ColorPaletteExt.js +++ b/apps/common/main/lib/component/ColorPaletteExt.js @@ -108,12 +108,12 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + this.$el.html(this.cmpEl); } - this.cmpEl.on('click', _.bind(this.handleClick, this)); + this.cmpEl.on('click', me.handleClick.bind(me)); } else { - this.cmpEl = $(this.el); + this.cmpEl = me.$el || $(this.el); } me.rendered = true; diff --git a/apps/common/main/lib/component/ComboBox.js b/apps/common/main/lib/component/ComboBox.js index 8b24e3caf..5b7a7f4cc 100644 --- a/apps/common/main/lib/component/ComboBox.js +++ b/apps/common/main/lib/component/ComboBox.js @@ -104,8 +104,7 @@ define([ initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; this.id = me.options.id || Common.UI.getId(); this.cls = me.options.cls; @@ -158,10 +157,10 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + this.$el.html(this.cmpEl); } } else { - this.cmpEl = $(this.el); + this.cmpEl = me.$el || $(this.el); } if (!me.rendered) { @@ -194,6 +193,18 @@ define([ var modalParents = el.closest('.asc-window'); if (modalParents.length > 0) { el.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10); + var onModalClose = function(dlg) { + if (modalParents[0] !== dlg.$window[0]) return; + var tip = el.data('bs.tooltip'); + if (tip) { + if (tip.dontShow===undefined) + tip.dontShow = true; + + tip.hide(); + } + Common.NotificationCenter.off({'modal:close': onModalClose}); + }; + Common.NotificationCenter.on({'modal:close': onModalClose}); } el.find('.dropdown-menu').on('mouseenter', function(){ // hide tooltip when mouse is over menu @@ -241,7 +252,6 @@ define([ this.scroller = new Common.UI.Scroller(_.extend({ el: $('.dropdown-menu', this.cmpEl), minScrollbarLength: 40, - scrollYMarginOffset: 30, includePadding: true, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible @@ -266,7 +276,6 @@ define([ this.scroller = new Common.UI.Scroller(_.extend({ el: $('.dropdown-menu', this.cmpEl), minScrollbarLength: 40, - scrollYMarginOffset: 30, includePadding: true, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible @@ -631,7 +640,6 @@ define([ this.scroller = new Common.UI.Scroller(_.extend({ el: $('.dropdown-menu', this.cmpEl), minScrollbarLength : 40, - scrollYMarginOffset: 30, includePadding : true, wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible diff --git a/apps/common/main/lib/component/ComboDataView.js b/apps/common/main/lib/component/ComboDataView.js index c10d000ca..ff31ae0aa 100644 --- a/apps/common/main/lib/component/ComboDataView.js +++ b/apps/common/main/lib/component/ComboDataView.js @@ -148,7 +148,7 @@ define([ me.trigger('render:before', me); - me.cmpEl = $(me.el); + me.cmpEl = me.$el || $(me.el); var templateEl = me.template({ id : me.id, @@ -414,10 +414,11 @@ define([ } } - me.fieldPicker.store.reset([]); // remove all + var indexRec = store.indexOf(record); + if (indexRec < 0) + return; - var indexRec = store.indexOf(record), - countRec = store.length, + var countRec = store.length, maxViewCount = Math.floor(Math.max(fieldPickerEl.width(), me.minWidth) / (me.itemWidth + (me.itemMarginLeft || 0) + (me.itemMarginRight || 0) + (me.itemPaddingLeft || 0) + (me.itemPaddingRight || 0) + (me.itemBorderLeft || 0) + (me.itemBorderRight || 0))), newStyles = []; @@ -425,9 +426,6 @@ define([ if (fieldPickerEl.height() / me.itemHeight > 2) maxViewCount *= Math.floor(fieldPickerEl.height() / me.itemHeight); - if (indexRec < 0) - return; - indexRec = Math.floor(indexRec / maxViewCount) * maxViewCount; if (countRec - indexRec < maxViewCount) indexRec = Math.max(countRec - maxViewCount, 0); @@ -435,7 +433,7 @@ define([ newStyles.push(store.at(index)); } - me.fieldPicker.store.add(newStyles); + me.fieldPicker.store.reset(newStyles); } if (forceSelect) { diff --git a/apps/common/main/lib/component/DataView.js b/apps/common/main/lib/component/DataView.js index 67a027902..dc309e1d1 100644 --- a/apps/common/main/lib/component/DataView.js +++ b/apps/common/main/lib/component/DataView.js @@ -135,7 +135,7 @@ define([ if (_.isUndefined(this.model.id)) return this; - var el = $(this.el); + var el = this.$el || $(this.el); el.html(this.template(this.model.toJSON())); el.addClass('item'); @@ -262,7 +262,6 @@ define([ this.trigger('render:before', this); - this.cmpEl = $(this.el); if (parentEl) { this.setElement(parentEl, false); this.cmpEl = $(this.template({ @@ -272,6 +271,7 @@ define([ 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 @@ -766,6 +766,435 @@ define([ } }); + Common.UI.DataViewSimple = Common.UI.BaseView.extend({ + options : { + handleSelect: true, + enableKeyEvents: true, + keyMoveDirection: 'both', // 'vertical', 'horizontal' + restoreHeight: 0, + scrollAlwaysVisible: false, + useBSKeydown: false + }, + + template: _.template([ + '
', + '<% _.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; + 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._fromKeyDown = false; + this.scrollToRecord(rec); + } + } + } 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', '0'); + 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 docH) { menuRoot.css('max-height', (docH - top) + 'px'); (!this.scroller) && (this.scroller = new Common.UI.Scroller({ - el: $(this.el).find('.dropdown-menu '), + el: this.$el.find('.dropdown-menu '), minScrollbarLength: 30, suppressScrollX: true, alwaysVisibleY: this.scrollAlwaysVisible @@ -584,4 +588,447 @@ define([ })() }) })(); + + Common.UI.MenuSimple = Common.UI.BaseView.extend({ + options : { + cls : '', + style : '', + itemTemplate: null, + items : [], + menuAlign : 'tl-bl', + menuAlignEl : null, + offset : [0, 0], + cyclic : true, + search : false, + scrollAlwaysVisible: true + }, + + template: _.template([ + '' + ].join('')), + + initialize : function(options) { + Common.UI.BaseView.prototype.initialize.call(this, options); + + var me = this; + + this.id = this.options.id || Common.UI.getId(); + this.itemTemplate = this.options.itemTemplate || _.template([ + ' style="<%= style %>" <% } %>', + '<% if(typeof canFocused !== "undefined") { %> tabindex="-1" type="menuitem" <% } %>', + '<% if(typeof stopPropagation !== "undefined") { %> data-stopPropagation="true" <% } %>', + 'class="<% if (checked) { %> checked <% } %>" >', + '<% if (typeof iconCls !== "undefined") { %>', + '', + '<% } %>', + '<%= caption %>', + '' + ].join('')); + this.rendered = false; + this.items = this.options.items || []; + this.offset = [0, 0]; + this.menuAlign = this.options.menuAlign; + this.menuAlignEl = this.options.menuAlignEl; + this.scrollAlwaysVisible = this.options.scrollAlwaysVisible; + this.search = this.options.search; + + if (this.options.restoreHeight) { + this.options.restoreHeight = (typeof (this.options.restoreHeight) == "number") ? this.options.restoreHeight : (this.options.maxHeight ? this.options.maxHeight : 100000); + !this.options.maxHeight && (this.options.maxHeight = this.options.restoreHeight); + } + + if (!this.options.cyclic) this.options.cls += ' no-cyclic'; + + if (this.options.el) + this.render(); + + Common.UI.Menu.Manager.register(this); + }, + + remove: function() { + Common.UI.Menu.Manager.unregister(this); + Common.UI.BaseView.prototype.remove.call(this); + }, + + render: function(parentEl) { + var me = this; + + this.trigger('render:before', this); + + this.cmpEl = me.$el || $(this.el); + + parentEl && this.setElement(parentEl, false); + + if (!me.rendered) { + this.cmpEl = $(this.template({ + items: me.items, + itemTemplate: me.itemTemplate, + options : me.options + })); + + parentEl ? parentEl.append(this.cmpEl) : this.$el.append(this.cmpEl); + } + + var rootEl = this.cmpEl.parent(), + menuRoot = (rootEl.attr('role') === 'menu') ? rootEl : rootEl.find('[role=menu]'); + this.menuRoot = menuRoot; + + if (menuRoot) { + if (!me.rendered) { + menuRoot.on( "click", "li", _.bind(me.onItemClick, me)); + menuRoot.on( "mousedown", "li", _.bind(me.onItemMouseDown, me)); + } + + if (this.options.maxHeight) { + menuRoot.css({'max-height': me.options.maxHeight}); + this.scroller = new Common.UI.Scroller({ + el: me.$el.find('.dropdown-menu '), + minScrollbarLength: 30, + suppressScrollX: true, + alwaysVisibleY: this.scrollAlwaysVisible + }); + } + + menuRoot.css({ + position : 'fixed', + right : 'auto', + left : -1000, + top : -1000 + }); + + this.parentEl = menuRoot.parent(); + + this.parentEl.on('show.bs.dropdown', _.bind(me.onBeforeShowMenu, me)); + this.parentEl.on('shown.bs.dropdown', _.bind(me.onAfterShowMenu, me)); + this.parentEl.on('hide.bs.dropdown', _.bind(me.onBeforeHideMenu, me)); + this.parentEl.on('hidden.bs.dropdown', _.bind(me.onAfterHideMenu, me)); + this.parentEl.on('keydown.after.bs.dropdown', _.bind(me.onAfterKeydownMenu, me)); + + menuRoot.hover( + function(e) { me.isOver = true;}, + function(e) { me.isOver = false; } + ); + } + + this.rendered = true; + + this.trigger('render:after', this); + + return this; + }, + + resetItems: function(items) { + this.items = items || []; + this.$items = null; + var template = _.template([ + '<% _.each(items, function(item) { %>', + '<% if (!item.id) item.id = Common.UI.getId(); %>', + '<% item.checked = item.checked || false; %>', + '
  • <%= itemTemplate(item) %>
  • ', + '<% }) %>' + ].join('')); + this.cmpEl && this.cmpEl.html(template({ + items: this.items, + itemTemplate: this.itemTemplate, + options : this.options + })); + }, + + isVisible: function() { + return this.rendered && (this.cmpEl.is(':visible')); + }, + + show: function() { + if (this.rendered && this.parentEl && !this.parentEl.hasClass('open')) { + this.cmpEl.dropdown('toggle'); + } + }, + + hide: function() { + if (this.rendered && this.parentEl) { + if ( this.parentEl.hasClass('open') ) + this.cmpEl.dropdown('toggle'); + else if (this.parentEl.hasClass('over')) + this.parentEl.removeClass('over'); + } + }, + + onItemClick: function(e) { + if (e.which != 1 && e.which !== undefined) + return false; + + var index = $(e.currentTarget).closest('li').index(), + item = (index>=0) ? this.items[index] : null; + if (!item) return; + + if (item.disabled) + return false; + + if (item.checkable && !item.checked) + this.setChecked(index, !item.checked); + + this.isOver = false; + if (item.stopPropagation) { + e.stopPropagation(); + var me = this; + _.delay(function(){ + me.$el.parent().parent().find('[data-toggle=dropdown]').focus(); + }, 10); + return; + } + this.trigger('item:click', this, item, e); + }, + + onItemMouseDown: function(e) { + if (e.which != 1) { + e.preventDefault(); + e.stopPropagation(); + + return false; + } + e.stopPropagation(); + }, + + setChecked: function(index, check, suppressEvent) { + this.toggle(index, check, suppressEvent); + }, + + toggle: function(index, toggle, suppressEvent) { + var state = !!toggle; + var item = this.items[index]; + + this.clearAll(); + + if (item && item.checkable) { + item.checked = state; + + if (this.rendered) { + var itemEl = item.el || this.cmpEl.find('#'+item.id); + if (itemEl) { + itemEl.toggleClass('checked', item.checked); + if (!_.isEmpty(item.iconCls)) { + itemEl.css('background-image', 'none'); + } + } + } + + if (!suppressEvent) + this.trigger('item:toggle', this, item, state); + } + }, + + setDisabled: function(disabled) { + this.disabled = !!disabled; + + if (this.rendered) + this.cmpEl.toggleClass('disabled', this.disabled); + }, + + isDisabled: function() { + return this.disabled; + }, + + onBeforeShowMenu: function(e) { + Common.NotificationCenter.trigger('menu:show'); + this.trigger('show:before', this, e); + this.alignPosition(); + }, + + onAfterShowMenu: function(e) { + this.trigger('show:after', this, e); + if (this.scroller) { + this.scroller.update({alwaysVisibleY: this.scrollAlwaysVisible}); + var menuRoot = this.menuRoot, + $selected = menuRoot.find('> li .checked'); + if ($selected.length) { + var itemTop = $selected.position().top, + itemHeight = $selected.height(), + listHeight = menuRoot.height(); + if (itemTop < 0 || itemTop + itemHeight > listHeight) { + menuRoot.scrollTop(menuRoot.scrollTop() + itemTop + itemHeight - (listHeight/2)); + } + setTimeout(function(){$selected.focus();}, 1); + } + } + this._search = {}; + if (this.search && !this.$items) { + var me = this; + this.$items = this.menuRoot.find('> li').find('> a'); + _.each(this.$items, function(item, index) { + me.items[index].el = $(item); + }); + } + }, + + onBeforeHideMenu: function(e) { + this.trigger('hide:before', this, e); + + if (Common.UI.Scroller.isMouseCapture()) + e.preventDefault(); + }, + + onAfterHideMenu: function(e, isFromInputControl) { + this.trigger('hide:after', this, e, isFromInputControl); + Common.NotificationCenter.trigger('menu:hide', this, isFromInputControl); + }, + + onAfterKeydownMenu: function(e) { + if (e.keyCode == Common.UI.Keys.RETURN) { + var li = $(e.target).closest('li'); + if (li.length<=0) li = $(e.target).parent().find('li .dataview'); + if (li.length>0) li.click(); + if (!li.hasClass('dropdown-submenu')) + Common.UI.Menu.Manager.hideAll(); + if ( $(e.currentTarget).closest('li').hasClass('dropdown-submenu')) { + e.stopPropagation(); + return false; + } + } else if (e.keyCode == Common.UI.Keys.UP || e.keyCode == Common.UI.Keys.DOWN) { + this.fromKeyDown = true; + } else if (e.keyCode == Common.UI.Keys.ESC) { +// Common.NotificationCenter.trigger('menu:afterkeydown', e); +// return false; + } else if (this.search && e.keyCode > 64 && e.keyCode < 91 && e.key){ + var me = this; + clearTimeout(this._search.timer); + this._search.timer = setTimeout(function () { me._search = {}; }, 1000); + + (!this._search.text) && (this._search.text = ''); + (!this._search.char) && (this._search.char = e.key); + (this._search.char !== e.key) && (this._search.full = true); + this._search.text += e.key; + if (this._search.index===undefined) { + this._search.index = this.$items.index(this.$items.filter(':focus')); + } + this.selectCandidate(); + } + }, + + selectCandidate: function() { + var index = this._search.index || 0, + re = new RegExp('^' + ((this._search.full) ? this._search.text : this._search.char), 'i'), + itemCandidate, idxCandidate; + + for (var i=0; iindex) { + itemCandidate = item; + idxCandidate = i; + break; + } + } + } + + if (itemCandidate) { + this._search.index = idxCandidate; + var item = itemCandidate.el; + if (this.scroller) { + this.scroller.update({alwaysVisibleY: this.scrollAlwaysVisible}); + var itemTop = item.position().top, + itemHeight = item.height(), + listHeight = this.menuRoot.height(); + if (itemTop < 0 || itemTop + itemHeight > listHeight) { + this.menuRoot.scrollTop(this.menuRoot.scrollTop() + itemTop + itemHeight - (listHeight/2)); + } + } + item.focus(); + } + }, + + setOffset: function(offsetX, offsetY) { + this.offset[0] = _.isUndefined(offsetX) ? this.offset[0] : offsetX; + this.offset[1] = _.isUndefined(offsetY) ? this.offset[1] : offsetY; + this.alignPosition(); + }, + + getOffset: function() { + return this.offset; + }, + + alignPosition: function(fixedAlign, fixedOffset) { + var menuRoot = this.menuRoot, + menuParent = this.menuAlignEl || menuRoot.parent(), + m = this.menuAlign.match(/^([a-z]+)-([a-z]+)/), + offset = menuParent.offset(), + docW = Common.Utils.innerWidth(), + docH = Common.Utils.innerHeight() - 10, // Yep, it's magic number + menuW = menuRoot.outerWidth(), + menuH = menuRoot.outerHeight(), + parentW = menuParent.outerWidth(), + parentH = menuParent.outerHeight(); + + var posMenu = { + 'tl': [0, 0], + 'bl': [0, menuH], + 'tr': [menuW, 0], + 'br': [menuW, menuH] + }; + var posParent = { + 'tl': [0, 0], + 'tr': [parentW, 0], + 'bl': [0, parentH], + 'br': [parentW, parentH] + }; + var left = offset.left - posMenu[m[1]][0] + posParent[m[2]][0] + this.offset[0]; + var top = offset.top - posMenu[m[1]][1] + posParent[m[2]][1] + this.offset[1]; + + if (left + menuW > docW) + if (menuParent.is('li.dropdown-submenu')) { + left = offset.left - menuW + 2; + } else { + left = docW - menuW; + } + + if (this.options.restoreHeight) { + if (typeof (this.options.restoreHeight) == "number") { + if (top + menuH > docH) { + menuRoot.css('max-height', (docH - top) + 'px'); + (!this.scroller) && (this.scroller = new Common.UI.Scroller({ + el: this.$el.find('.dropdown-menu '), + minScrollbarLength: 30, + suppressScrollX: true, + alwaysVisibleY: this.scrollAlwaysVisible + })); + } else if ( top + menuH < docH && menuRoot.height() < this.options.restoreHeight) { + menuRoot.css('max-height', (Math.min(docH - top, this.options.restoreHeight)) + 'px'); + } + } + } else { + if (top + menuH > docH) { + if (fixedAlign && typeof fixedAlign == 'string') { // how to align if menu height > window height + m = fixedAlign.match(/^([a-z]+)-([a-z]+)/); + top = offset.top - posMenu[m[1]][1] + posParent[m[2]][1] + this.offset[1] + (fixedOffset || 0); + } else + top = docH - menuH; + } + + if (top < 0) + top = 0; + } + + if (this.options.additionalAlign) + this.options.additionalAlign.call(this, menuRoot, left, top); + else + menuRoot.css({left: Math.ceil(left), top: Math.ceil(top)}); + }, + + clearAll: function() { + this.cmpEl && this.cmpEl.find('li > a.checked').removeClass('checked'); + _.each(this.items, function(item){ + item.checked = false; + }); + } + }); + }); \ No newline at end of file diff --git a/apps/common/main/lib/component/MenuItem.js b/apps/common/main/lib/component/MenuItem.js index 7b49b77e2..c54501185 100644 --- a/apps/common/main/lib/component/MenuItem.js +++ b/apps/common/main/lib/component/MenuItem.js @@ -119,8 +119,7 @@ define([ initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; this.id = me.options.id || Common.UI.getId(); this.cls = me.options.cls; @@ -138,7 +137,7 @@ define([ this.hint = me.options.hint; this.rendered = false; - if (this.menu !== null && !(this.menu instanceof Common.UI.Menu)) { + if (this.menu !== null && !(this.menu instanceof Common.UI.Menu) && !(this.menu instanceof Common.UI.MenuSimple)) { this.menu = new Common.UI.Menu(_.extend({}, me.options.menu)); } @@ -148,7 +147,7 @@ define([ render: function() { var me = this, - el = $(this.el); + el = me.$el || $(this.el); me.trigger('render:before', me); @@ -159,7 +158,7 @@ define([ el.off('click'); Common.UI.ToggleManager.unregister(me); - $(this.el).html(this.template({ + el.html(this.template({ id : me.id, caption : me.caption, iconCls : me.iconCls, @@ -170,7 +169,7 @@ define([ if (me.menu) { el.addClass('dropdown-submenu'); - me.menu.render($(this.el)); + me.menu.render(el); el.mouseenter(_.bind(me.menu.alignPosition, me.menu)); // el.focusin(_.bind(me.onFocusItem, me)); el.focusout(_.bind(me.onBlurItem, me)); @@ -214,7 +213,7 @@ define([ } if (this.disabled) - $(this.el).toggleClass('disabled', this.disabled); + el.toggleClass('disabled', this.disabled); el.on('click', _.bind(this.onItemClick, this)); el.on('mousedown', _.bind(this.onItemMouseDown, this)); @@ -223,7 +222,7 @@ define([ } } - me.cmpEl = $(this.el); + me.cmpEl = el; me.rendered = true; me.trigger('render:after', me); diff --git a/apps/common/main/lib/component/MetricSpinner.js b/apps/common/main/lib/component/MetricSpinner.js index e64b61522..584db251d 100644 --- a/apps/common/main/lib/component/MetricSpinner.js +++ b/apps/common/main/lib/component/MetricSpinner.js @@ -128,7 +128,7 @@ define([ Common.UI.BaseView.prototype.initialize.call(this, options); var me = this, - el = $(this.el); + el = me.$el || $(this.el); el.addClass('spinner'); @@ -165,7 +165,7 @@ define([ this.setRawValue(this.value); if (this.options.width) { - $(this.el).width(this.options.width); + el.width(this.options.width); } if (this.options.defaultValue===undefined) @@ -176,7 +176,7 @@ define([ }, render: function () { - var el = $(this.el); + var el = this.$el || $(this.el); el.html(this.template); this.$input = el.find('.form-control'); @@ -189,7 +189,7 @@ define([ }, setDisabled: function(disabled) { - var el = $(this.el); + var el = this.$el || $(this.el); if (disabled !== this.disabled) { el.find('button').toggleClass('disabled', disabled); el.toggleClass('disabled', disabled); diff --git a/apps/common/main/lib/component/RadioBox.js b/apps/common/main/lib/component/RadioBox.js index 8b7ff4b6f..9e8e2d159 100644 --- a/apps/common/main/lib/component/RadioBox.js +++ b/apps/common/main/lib/component/RadioBox.js @@ -71,13 +71,12 @@ define([ disabled : false, rendered : false, - template : _.template(''), + template : _.template(''), initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; this.name = this.options.name || Common.UI.getId(); @@ -94,13 +93,14 @@ define([ }, render: function () { - var el = $(this.el); + var el = this.$el || $(this.el); el.html(this.template({ labelText: this.options.labelText, name: this.name })); this.$radio = el.find('input[type=button]'); + this.$label = el.find('label'); this.rendered = true; return this; @@ -145,6 +145,10 @@ define([ getValue: function() { return this.$radio.hasClass('checked'); + }, + + setCaption: function(text) { + this.$label.find('span').text(text); } }); }); \ No newline at end of file diff --git a/apps/common/main/lib/component/Scroller.js b/apps/common/main/lib/component/Scroller.js index 53e3783dd..db564e0d4 100644 --- a/apps/common/main/lib/component/Scroller.js +++ b/apps/common/main/lib/component/Scroller.js @@ -78,7 +78,7 @@ define([ render: function() { var me = this; - me.cmpEl = $(this.el); + me.cmpEl = me.$el || $(this.el); if (!me.rendered) { me.cmpEl.perfectScrollbar(_.extend({}, me.options)); diff --git a/apps/common/main/lib/component/Slider.js b/apps/common/main/lib/component/Slider.js index d11430466..0ee059b7e 100644 --- a/apps/common/main/lib/component/Slider.js +++ b/apps/common/main/lib/component/Slider.js @@ -104,8 +104,7 @@ define([ initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; me.width = me.options.width; me.minValue = me.options.minValue; @@ -131,10 +130,10 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + me.$el.html(this.cmpEl); } } else { - this.cmpEl = $(this.el); + this.cmpEl = me.$el; } this.cmpEl.find('.track-center').width(me.options.width - 14); @@ -299,8 +298,7 @@ define([ initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; me.width = me.options.width; me.minValue = me.options.minValue; @@ -326,10 +324,10 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + this.$el.html(this.cmpEl); } } else { - this.cmpEl = $(this.el); + this.cmpEl = this.$el; } var el = this.cmpEl; diff --git a/apps/common/main/lib/component/Switcher.js b/apps/common/main/lib/component/Switcher.js index e393011d2..a474e40ae 100644 --- a/apps/common/main/lib/component/Switcher.js +++ b/apps/common/main/lib/component/Switcher.js @@ -65,8 +65,7 @@ define([ initialize : function(options) { Common.UI.BaseView.prototype.initialize.call(this, options); - var me = this, - el = $(this.el); + var me = this; me.width = me.options.width; me.thumbWidth = me.options.thumbWidth; @@ -89,10 +88,10 @@ define([ this.setElement(parentEl, false); parentEl.html(this.cmpEl); } else { - $(this.el).html(this.cmpEl); + this.$el.html(this.cmpEl); } } else { - this.cmpEl = $(this.el); + this.cmpEl = this.$el; } this.thumb = this.cmpEl.find('.thumb'); diff --git a/apps/common/main/lib/component/Tab.js b/apps/common/main/lib/component/Tab.js index 29e93b32c..b3b5644ef 100644 --- a/apps/common/main/lib/component/Tab.js +++ b/apps/common/main/lib/component/Tab.js @@ -51,7 +51,7 @@ define([ this.active = false; this.label = 'Tab'; this.cls = ''; - this.template = _.template(['
  • ', + this.template = _.template(['
  • ', '<%- label %>', '
  • '].join('')); @@ -82,6 +82,10 @@ define([ this.$el.addClass('active'); }, + isSelected: function() { + return this.$el.hasClass('selected'); + }, + deactivate: function(){ this.$el.removeClass('active'); }, @@ -110,6 +114,11 @@ define([ this.$el.removeClass(cls); }, + toggleClass: function(cls) { + if (cls.length) + this.$el.toggleClass(cls); + }, + hasClass: function(cls) { return this.$el.hasClass(cls); }, diff --git a/apps/common/main/lib/component/TabBar.js b/apps/common/main/lib/component/TabBar.js index 0c6868add..27d120875 100644 --- a/apps/common/main/lib/component/TabBar.js +++ b/apps/common/main/lib/component/TabBar.js @@ -69,12 +69,28 @@ define([ }; StateManager.prototype.attach = function (tab) { - tab.changeState = $.proxy(function () { - this.trigger('tab:change', tab); - this.bar.$el.find('ul > li.active').removeClass('active'); - tab.activate(); + 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.bar.trigger('tab:changed', this.bar, this.bar.tabs.indexOf(tab), tab); + } }, this); var dragHelper = new (function() { @@ -278,17 +294,91 @@ define([ 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 () { - if (!tab.disabled && !tab.$el.hasClass('active')) { - if (tab.control == 'manual') { - this.bar.trigger('tab:manual', this.bar, this.bar.tabs.indexOf(tab), tab); - } else { - tab.changeState(); + 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); @@ -297,12 +387,16 @@ define([ 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.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) { - dragHelper.setHook(e, this.bar, tab); + if (this.bar.selectTabs.length > 1) { + dragHelper.setHookTabs(e, this.bar, this.bar.selectTabs); + } else { + dragHelper.setHook(e, this.bar, tab); + } } } }, this) @@ -322,6 +416,7 @@ define([ tabs: [], template: _.template('