diff --git a/apps/api/documents/api.js b/apps/api/documents/api.js index ee433be10..c5aecaf25 100644 --- a/apps/api/documents/api.js +++ b/apps/api/documents/api.js @@ -11,10 +11,11 @@ # Full # config = { - type: 'desktop or mobile', + type: 'desktop or mobile or embedded', width: '100% by default', height: '100% by default', documentType: 'word' | 'cell' | 'slide',// deprecate 'text' | 'spreadsheet' | 'presentation', + token: encrypted signature document: { title: 'document title', url: 'document url' @@ -51,6 +52,12 @@ } }, editorConfig: { + actionLink: { // open file and scroll to data, used with onMakeActionLink or the onRequestSendNotify event + action: { + type: "bookmark", // or type="comment" + data: // or comment id + } + }, mode: 'view or edit', lang: , location: , @@ -141,7 +148,8 @@ mentionShare : true // customize tooltip for mention, macros: true // can run macros in document plugins: true // can run plugins in document - macrosMode: 'warn' // warn about automatic macros, 'enable', 'disable', 'warn' + macrosMode: 'warn' // warn about automatic macros, 'enable', 'disable', 'warn', + trackChanges: undefined // true/false - open editor with track changes mode on/off }, plugins: { autostart: ['asc.{FFE1F462-1EA2-4391-990D-4CC84940B754}'], @@ -155,9 +163,30 @@ }, events: { 'onAppReady': , - 'onBack': , 'onDocumentStateChange': 'onDocumentReady': + 'onRequestEditRights': , + 'onRequestHistory': ,// must call refreshHistory method + 'onRequestHistoryData': ,// must call setHistoryData method + 'onRequestRestore': , + 'onRequestHistoryClose': , + 'onError': , + 'onWarning': , + 'onInfo': ,// send view or edit mode + 'onOutdatedVersion': ,// send when previous version is opened + 'onDownloadAs': ,// send url of downloaded file as a response for downloadAs method + 'onRequestSaveAs': , + 'onCollaborativeChanges': ,// send when other user co-edit document + 'onRequestRename': , + 'onMetaChange': // send when meta information changed + 'onRequestClose': , + 'onMakeActionLink': ,// must call setActionLink method + 'onRequestUsers': ,// must call setUsers method + 'onRequestSendNotify': //send when user is mentioned in a comment, + 'onRequestInsertImage': ,// must call insertImage method + 'onRequestCompareFile': ,// must call setRevisedFile method + 'onRequestSharingSettings': ,// must call setSharingSettings method + 'onRequestCreateNew': , } } @@ -361,7 +390,7 @@ if (typeof _config.document.fileType === 'string' && _config.document.fileType != '') { _config.document.fileType = _config.document.fileType.toLowerCase(); - var type = /^(?:(xls|xlsx|ods|csv|xlst|xlsy|gsheet|xlsm|xlt|xltm|xltx|fods|ots)|(pps|ppsx|ppt|pptx|odp|pptt|ppty|gslides|pot|potm|potx|ppsm|pptm|fodp|otp)|(doc|docx|doct|odt|gdoc|txt|rtf|pdf|mht|htm|html|epub|djvu|xps|docm|dot|dotm|dotx|fodt|ott))$/ + var type = /^(?:(xls|xlsx|ods|csv|xlst|xlsy|gsheet|xlsm|xlt|xltm|xltx|fods|ots)|(pps|ppsx|ppt|pptx|odp|pptt|ppty|gslides|pot|potm|potx|ppsm|pptm|fodp|otp)|(doc|docx|doct|odt|gdoc|txt|rtf|pdf|mht|htm|html|epub|djvu|xps|docm|dot|dotm|dotx|fodt|ott|fb2))$/ .exec(_config.document.fileType); if (!type) { window.alert("The \"document.fileType\" parameter for the config object is invalid. Please correct it."); @@ -850,7 +879,7 @@ iframe.allowFullscreen = true; iframe.setAttribute("allowfullscreen",""); // for IE11 iframe.setAttribute("onmousewheel",""); // for Safari on Mac - iframe.setAttribute("allow", "autoplay"); + iframe.setAttribute("allow", "autoplay; camera; microphone; display-capture"); if (config.type == "mobile") { diff --git a/apps/common/main/lib/component/Calendar.js b/apps/common/main/lib/component/Calendar.js index 2304ab1f5..0ae99149c 100644 --- a/apps/common/main/lib/component/Calendar.js +++ b/apps/common/main/lib/component/Calendar.js @@ -237,7 +237,7 @@ define([ me.yearPicker.store.reset(arrYears); me.enableKeyEvents && _.delay(function() { - me.monthPicker.cmpEl.find('.dataview').focus(); + me.monthPicker.focus(); }, 10); }, @@ -312,7 +312,7 @@ define([ me.monthsPicker.store.reset(arrMonths); me.enableKeyEvents && _.delay(function() { - me.monthPicker.cmpEl.find('.dataview').focus(); + me.monthPicker.focus(); }, 10); }, @@ -435,7 +435,7 @@ define([ me.monthPicker.store.reset(arrDays); me.enableKeyEvents && _.delay(function() { - me.monthPicker.cmpEl.find('.dataview').focus(); + me.monthPicker.focus(); }, 10); }, diff --git a/apps/common/main/lib/component/ColorButton.js b/apps/common/main/lib/component/ColorButton.js index 15b02c0bb..cdbbb0002 100644 --- a/apps/common/main/lib/component/ColorButton.js +++ b/apps/common/main/lib/component/ColorButton.js @@ -61,7 +61,7 @@ define([ var me = this; options.menu = me.getMenu(options); me.on('render:after', function(btn) { - me.getPicker(options.color); + me.getPicker(options.color, options.colors); }); } @@ -88,12 +88,13 @@ define([ span.css({'background-color': (color=='transparent') ? color : ((typeof(color) == 'object') ? '#'+color.color : '#'+color)}); }, - getPicker: function(color) { + getPicker: function(color, colors) { if (!this.colorPicker) { this.colorPicker = new Common.UI.ThemeColorPalette({ el: this.cmpEl.find('#' + this.menu.id + '-color-menu'), transparent: this.options.transparent, - value: color + value: color, + colors: colors }); this.colorPicker.on('select', _.bind(this.onColorSelect, this)); this.cmpEl.find('#' + this.menu.id + '-color-new').on('click', _.bind(this.addNewColor, this)); @@ -104,13 +105,14 @@ define([ getMenu: function(options) { if (typeof this.menu !== 'object') { options = options || this.options; + var height = options.paletteHeight || 216; var id = Common.UI.getId(), menu = new Common.UI.Menu({ id: id, cls: 'shifted-left', additionalAlign: options.additionalAlign, items: (options.additionalItems ? options.additionalItems : []).concat([ - { template: _.template('
') }, + { template: _.template('
') }, { template: _.template('' + this.textNewColor + '') } ]) }); @@ -122,7 +124,7 @@ define([ setMenu: function (m) { m = m || this.getMenu(); Common.UI.Button.prototype.setMenu.call(this, m); - this.getPicker(this.options.color); + this.getPicker(this.options.color, this.options.colors); }, addNewColor: function() { diff --git a/apps/common/main/lib/component/ComboBox.js b/apps/common/main/lib/component/ComboBox.js index c50a88aff..14a0a185a 100644 --- a/apps/common/main/lib/component/ComboBox.js +++ b/apps/common/main/lib/component/ComboBox.js @@ -86,7 +86,8 @@ define([ displayField: 'displayValue', valueField : 'value', search : false, - scrollAlwaysVisible: false + scrollAlwaysVisible: false, + takeFocusOnClose: false }, template: _.template([ @@ -323,6 +324,9 @@ define([ $list.scrollTop(height); } setTimeout(function(){$selected.find('a').focus();}, 1); + } else { + var me = this; + setTimeout(function(){me.cmpEl.find('ul li:first a').focus();}, 1); } if (this.scroller) @@ -343,10 +347,19 @@ define([ this.cmpEl.find('.dropdown-toggle').blur(); this.trigger('hide:after', this, e, isFromInputControl); Common.NotificationCenter.trigger('menu:hide', this, isFromInputControl); + if (this.options.takeFocusOnClose) { + var me = this; + setTimeout(function(){me.focus();}, 1); + } }, onAfterKeydownMenu: function(e) { - if (e.keyCode == Common.UI.Keys.RETURN) { + if (e.keyCode == Common.UI.Keys.DOWN && !this.editable && !this.isMenuOpen()) { + this.openMenu(); + this.onAfterShowMenu(); + return false; + } + else if (e.keyCode == Common.UI.Keys.RETURN && (this.editable || this.isMenuOpen())) { $(e.target).click(); var me = this; if (this.rendered) { @@ -666,6 +679,10 @@ define([ wheelSpeed: 10, alwaysVisibleY: this.scrollAlwaysVisible }, this.options.scroller)); + }, + + focus: function() { + this._input && this._input.focus(); } } })()); @@ -687,6 +704,10 @@ define([ Common.UI.ComboBox.prototype.selectRecord.call(this, record); if (this.options.updateFormControl) this.options.updateFormControl.call(this, this._selectedItem); + }, + + focus: function() { + this.cmpEl && this.cmpEl.find('.form-control').focus(); } }, Common.UI.ComboBoxCustom || {})); }); \ No newline at end of file diff --git a/apps/common/main/lib/component/ComboBoxFonts.js b/apps/common/main/lib/component/ComboBoxFonts.js index 8d0092329..7840c57ee 100644 --- a/apps/common/main/lib/component/ComboBoxFonts.js +++ b/apps/common/main/lib/component/ComboBoxFonts.js @@ -105,14 +105,17 @@ define([ render : function(parentEl) { var oldRawValue = null; + var oldTabindex = ''; if (!_.isUndefined(this._input)) { oldRawValue = this._input.val(); + oldTabindex = this._input.attr('tabindex'); } Common.UI.ComboBox.prototype.render.call(this, parentEl); this.setRawValue(oldRawValue); + this._input.attr('tabindex', oldTabindex); this._input.on('keyup', _.bind(this.onInputKeyUp, this)); this._input.on('keydown', _.bind(this.onInputKeyDown, this)); @@ -128,6 +131,8 @@ define([ if ($(e.target).closest('input').length) { // enter in input field if (this.lastValue !== this._input.val()) this._input.trigger('change'); + else + return true; } else { // enter in dropdown list $(e.target).click(); if (this.rendered) { @@ -136,7 +141,7 @@ define([ else this._input.blur(); } - } + } return false; } else if (e.keyCode == Common.UI.Keys.ESC && this.isMenuOpen()) { this._input.val(this.lastValue); @@ -199,7 +204,7 @@ define([ me.closeMenu(); me.onAfterHideMenu(e); }, 10); - } else if (e.keyCode != Common.UI.Keys.RETURN && e.keyCode != Common.UI.Keys.CTRL && e.keyCode != Common.UI.Keys.SHIFT && e.keyCode != Common.UI.Keys.ALT){ + } else if (e.keyCode != Common.UI.Keys.RETURN && e.keyCode != Common.UI.Keys.CTRL && e.keyCode != Common.UI.Keys.SHIFT && e.keyCode != Common.UI.Keys.ALT && e.keyCode != Common.UI.Keys.TAB){ if (!this.isMenuOpen() && !e.ctrlKey) this.openMenu(); @@ -301,7 +306,7 @@ define([ if (callback) { this.spriteThumbs = new Image(); this.spriteThumbs.onload = callback; - this.spriteThumbs.src = (window.devicePixelRatio > 1) ? thumbPath2x : thumbPath; + this.spriteThumbs.src = isRetina ? thumbPath2x : thumbPath; } }, @@ -338,16 +343,21 @@ define([ onApiChangeFont: function(font) { var me = this; - setTimeout(function () { - me.onApiChangeFontInternal(font); - }, 100); + var name = (_.isFunction(font.get_Name) ? font.get_Name() : font.asc_getFontName()); + if (this.__name !== name) { + if (!this.__nameId) { + this.__nameId = setTimeout(function () { + me.onApiChangeFontInternal(name); + me.__nameId = null; + }, 100); + } + } }, - onApiChangeFontInternal: function(font) { + onApiChangeFontInternal: function(name) { if (this.inFormControl) return; - var name = (_.isFunction(font.get_Name) ? font.get_Name() : font.asc_getFontName()); - + this.__name = name; if (this.getRawValue() !== name) { var record = this.store.findWhere({ name: name diff --git a/apps/common/main/lib/component/DataView.js b/apps/common/main/lib/component/DataView.js index 723389239..9a44423f0 100644 --- a/apps/common/main/lib/component/DataView.js +++ b/apps/common/main/lib/component/DataView.js @@ -242,6 +242,7 @@ define([ 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; @@ -678,7 +679,7 @@ define([ if (this.enableKeyEvents && this.handleSelect) { var el = $(this.el).find('.inner').addBack().filter('.inner'); el.addClass('canfocused'); - el.attr('tabindex', '0'); + el.attr('tabindex', this.tabindex.toString()); el.on((this.parentMenu && this.useBSKeydown) ? 'dataview:keydown' : 'keydown', _.bind(this.onKeyDown, this)); } }, @@ -763,6 +764,10 @@ define([ onResize: function() { this._layoutParams = undefined; + }, + + focus: function() { + this.cmpEl && this.cmpEl.find('.dataview').focus(); } }); @@ -798,6 +803,8 @@ define([ 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; @@ -1120,7 +1127,7 @@ define([ if (this.enableKeyEvents && this.handleSelect) { var el = $(this.el).find('.inner').addBack().filter('.inner'); el.addClass('canfocused'); - el.attr('tabindex', '0'); + el.attr('tabindex', this.tabindex.toString()); el.on((this.parentMenu && this.useBSKeydown) ? 'dataview:keydown' : 'keydown', _.bind(this.onKeyDown, this)); } }, @@ -1192,6 +1199,10 @@ define([ onResize: function() { this._layoutParams = undefined; + }, + + focus: function() { + this.cmpEl && this.cmpEl.find('.dataview').focus(); } }); diff --git a/apps/common/main/lib/component/FocusManager.js b/apps/common/main/lib/component/FocusManager.js new file mode 100644 index 000000000..0dfa0baea --- /dev/null +++ b/apps/common/main/lib/component/FocusManager.js @@ -0,0 +1,176 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2020 + * + * 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 + * + */ +/** + * FocusManager.js + * + * Created by Julia Radzhabova on 24.09.2020 + * Copyright (c) 2020 Ascensio System SIA. All rights reserved. + * + */ + + +if (Common === undefined) + var Common = {}; + +if (Common.UI === undefined) { + Common.UI = {}; +} + +Common.UI.FocusManager = new(function() { + var _tabindex = 1, + _windows = [], + _count = 0; + + var register = function(fields) { + var arr = []; + if (!fields.forEach) { + fields = [fields]; + } + fields.forEach(function(field) { + if (field) { + var item = (field.cmp && typeof field.selector == 'string') ? field : {cmp: field, selector: '.form-control'}; + item.el = (item.cmp.$el || $(item.cmp.el || item.cmp)).find(item.selector).addBack().filter(item.selector); + item.el && item.el.attr && item.el.attr('tabindex', _tabindex.toString()); + arr.push(item); + } + }); + return arr; + }; + + var addTraps = function(current) { + if (!current || current.traps || !current.fields || current.fields.length<1) return; + + var trapFirst = $(''); + trapFirst.on('focus', function() { + if (current.hidden) return; + var fields = current.fields; + for (var i=0; i