diff --git a/apps/api/documents/api.js b/apps/api/documents/api.js
index ee1428a8b..aa509db4f 100644
--- a/apps/api/documents/api.js
+++ b/apps/api/documents/api.js
@@ -782,7 +782,14 @@
if (config.frameEditorId)
params += "&frameEditorId=" + config.frameEditorId;
-
+
+ if (config.editorConfig && config.editorConfig.mode == 'view' ||
+ config.document && config.document.permissions && (config.document.permissions.edit === false && !config.document.permissions.review ))
+ params += "&mode=view";
+
+ if (config.editorConfig && config.editorConfig.customization && !!config.editorConfig.customization.compactHeader)
+ params += "&compact=true";
+
return params;
}
diff --git a/apps/common/main/lib/component/Calendar.js b/apps/common/main/lib/component/Calendar.js
new file mode 100644
index 000000000..71b282a13
--- /dev/null
+++ b/apps/common/main/lib/component/Calendar.js
@@ -0,0 +1,491 @@
+/*
+ *
+ * (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
+ *
+*/
+if (Common === undefined)
+ var Common = {};
+
+define([
+ 'common/main/lib/component/BaseView',
+ 'common/main/lib/util/utils'
+], function () {
+ 'use strict';
+
+ Common.UI.Calendar = Common.UI.BaseView.extend(_.extend({
+
+ template :
+ _.template([
+ '
',
+ '
',
+ '
',
+ '',
+ '',
+ '',
+ '
',
+ '
',
+ '
',
+ '
',
+ '',
+ '
'
+ ].join('')),
+
+ options: {
+ date: undefined,
+ firstday: 0 // 0 - sunday, 1 - monday
+ },
+
+ initialize : function(options) {
+ Common.UI.BaseView.prototype.initialize.call(this, options);
+
+ var me = this;
+
+ this.monthNames = [this.textJanuary, this.textFebruary, this.textMarch, this.textApril, this.textMay, this.textJune, this.textJuly, this.textAugust, this.textSeptember, this.textOctober, this.textNovember, this.textDecember];
+ this.dayNamesShort = [this.textShortSunday, this.textShortMonday, this.textShortTuesday, this.textShortWednesday, this.textShortThursday, this.textShortFriday, this.textShortSaturday];
+ this.monthShortNames = [this.textShortJanuary, this.textShortFebruary, this.textShortMarch, this.textShortApril, this.textShortMay, this.textShortJune, this.textShortJuly, this.textShortAugust, this.textShortSeptember, this.textShortOctober, this.textShortNovember, this.textShortDecember];
+
+ me.options.date = options.date;
+ if (!_.isUndefined(options.firstday) && (options.firstday === 0 || options.firstday === 1)) {
+ me.options.firstday = options.firstday;
+ }
+
+ me.enableKeyEvents= me.options.enableKeyEvents;
+
+ me._state = undefined; // 0 - month, 1 - months, 2 - years
+
+ me.render();
+ },
+
+ render: function () {
+ var me = this;
+ me.cmpEl = me.$el || $(this.el);
+ me.cmpEl.html(this.template());
+
+ me.currentDate = me.options.date || new Date();
+
+ me.btnPrev = new Common.UI.Button({
+ cls: '',
+ iconCls: 'arrow-prev img-commonctrl'
+ });
+ me.btnPrev.render(me.cmpEl.find('#prev-arrow'));
+ me.btnPrev.on('click', _.bind(me.onClickPrev, me));
+
+ me.btnNext = new Common.UI.Button({
+ cls: '',
+ iconCls: 'arrow-next img-commonctrl'
+ });
+ me.btnNext.render(me.cmpEl.find('#next-arrow'));
+ me.btnNext.on('click', _.bind(me.onClickNext, me));
+
+ me.cmpEl.on('keydown', function(e) {
+ me.trigger('calendar:keydown', me, e);
+ });
+
+ me.renderMonth(me.currentDate);
+
+ this.trigger('render:after', this);
+ return this;
+ },
+
+ onClickPrev: function () {
+ var me = this;
+ if (me._state === 0) {
+ var d = new Date(me.currentDate);
+ d.setMonth(d.getMonth() - 1);
+ if (d.getFullYear() > 0) {
+ me.renderMonth(d);
+ }
+ } else if (me._state === 1) {
+ var d = new Date(me.currentDate);
+ d.setFullYear(d.getFullYear() - 1);
+ if (d.getFullYear() > 0) {
+ me.renderMonths(d);
+ }
+ } else if (me._state === 2) {
+ var year = me.currentDate.getFullYear(),
+ newYear;
+ if (year % 10 !== 0) {
+ newYear = String(year);
+ newYear = Number(newYear.slice(0, -1) + '0') - 1;
+ } else {
+ newYear = year - 1;
+ }
+ if (newYear > 0) {
+ me.currentDate.setFullYear(newYear);
+ me.renderYears(newYear);
+ }
+ }
+ },
+
+ onClickNext: function () {
+ var me = this;
+ if (me._state === 0) {
+ var d = new Date(me.currentDate);
+ d.setMonth(d.getMonth() + 1);
+ if (d.getFullYear() > 0) {
+ me.renderMonth(d);
+ }
+ } else if (me._state === 1) {
+ var d = new Date(me.currentDate);
+ d.setFullYear(d.getFullYear() + 1);
+ if (d.getFullYear() > 0) {
+ me.renderMonths(d);
+ }
+ } else if (me._state === 2) {
+ var year = me.currentDate.getFullYear(),
+ newYear;
+ if (year % 10 !== 9) {
+ newYear = String(year);
+ newYear = Number(newYear.slice(0, -1) + '9') + 1;
+ } else {
+ newYear = year + 1;
+ }
+ if (newYear > 0) {
+ me.currentDate.setFullYear(newYear);
+ me.renderYears(newYear);
+ }
+ }
+ },
+
+ renderYears: function (year) {
+ var me = this,
+ year = _.isNumber(year) ? year : (me.currentDate ? me.currentDate.getFullYear() : new Date().getFullYear());
+
+ me._state = 2;
+
+ var firstYear = year,
+ lastYear = year;
+ if ((firstYear % 10) !== 0) {
+ var strYear = String(year);
+ firstYear = Number(strYear.slice(0, -1) + '0');
+ }
+ if ((lastYear % 10) !== 9) {
+ var strYear = String(year);
+ lastYear = Number(strYear.slice(0, -1) + '9');
+ }
+
+ me.topTitle = _.template([
+ ''
+ ].join(''));
+ me.cmpEl.find('.calendar-header .title').html(me.topTitle);
+
+ me.bottomTitle = _.template([
+ ''
+ ].join(''));
+ me.cmpEl.find('.calendar-header .bottom-row').html(me.bottomTitle);
+
+ var arrYears = [];
+ var tmpYear = firstYear - 3;
+
+ for (var i = 0; i < 16; i++) {
+ arrYears.push({
+ year: (tmpYear > 0) ? tmpYear : '',
+ isCurrentDecade: ((tmpYear >= firstYear) && (tmpYear <= lastYear)) ? true : false,
+ disabled: (tmpYear > 0) ? false : true,
+ selected: (_.isDate(me.selectedDate)) ?
+ (tmpYear === me.selectedDate.getFullYear()) :
+ (tmpYear === new Date().getFullYear())
+ });
+ tmpYear++;
+ }
+
+ if (!me.yearPicker) {
+ me.yearPicker = new Common.UI.DataView({
+ el: me.cmpEl.find('.calendar-content'),
+ store: new Common.UI.DataViewStore(arrYears),
+ itemTemplate: _.template('
<%= year %>
')
+ });
+ me.yearPicker.on('item:click', function (picker, item, record, e) {
+ var year = record.get('year'),
+ date = new Date();
+ date.setFullYear(year);
+ me.renderMonths(date);
+ });
+ me.enableKeyEvents && this.yearPicker.on('item:keydown', function(view, record, e) {
+ if (e.keyCode==Common.UI.Keys.ESC) {
+ Common.NotificationCenter.trigger('dataview:blur');
+ }
+ });
+ } else
+ me.yearPicker.store.reset(arrYears);
+
+ me.enableKeyEvents && _.delay(function() {
+ me.monthPicker.cmpEl.find('.dataview').focus();
+ }, 10);
+ },
+
+ renderMonths: function (date) {
+ var me = this,
+ curDate = (_.isDate(date)) ? date : (me.currentDate ? me.currentDate : new Date()),
+ year = curDate.getFullYear();
+
+ me._state = 1;
+ me.currentDate = curDate;
+
+ // Number of year
+ me.topTitle = _.template([
+ ''
+ ].join(''));
+ me.cmpEl.find('.calendar-header .title').html(me.topTitle);
+ me.cmpEl.find('.calendar-header .title').off();
+ me.cmpEl.find('.calendar-header .title').on('click', _.bind(me.renderYears, me));
+
+ me.bottomTitle = _.template([
+ ''
+ ].join(''));
+ me.cmpEl.find('.calendar-header .bottom-row').html(me.bottomTitle);
+
+ var arrMonths = [];
+ var today = new Date();
+
+ for (var ind = 0; ind < 12; ind++) {
+ arrMonths.push({
+ indexMonth: ind,
+ nameMonth: me.monthShortNames[ind],
+ year: year,
+ curYear: true,
+ isCurrentMonth: (ind === curDate.getMonth()),
+ selected: (_.isDate(me.selectedDate)) ?
+ (ind === me.selectedDate.getMonth() && year === me.selectedDate.getFullYear()) :
+ (ind === today.getMonth() && year === today.getFullYear())
+ });
+ }
+ year = year + 1;
+ for (var ind = 0; ind < 4; ind++) {
+ arrMonths.push({
+ indexMonth: ind,
+ nameMonth: me.monthShortNames[ind],
+ year: year,
+ curYear: false,
+ selected: (_.isDate(me.selectedDate)) ?
+ (ind === me.selectedDate.getMonth() && year === me.selectedDate.getFullYear()) :
+ (ind === today.getMonth() && year === today.getFullYear())
+ });
+ }
+
+ if (!me.monthsPicker) {
+ me.monthsPicker = new Common.UI.DataView({
+ el: me.cmpEl.find('.calendar-content'),
+ store: new Common.UI.DataViewStore(arrMonths),
+ itemTemplate: _.template('
<%= nameMonth %>
')
+ });
+ me.monthsPicker.on('item:click', function (picker, item, record, e) {
+ var month = record.get('indexMonth'),
+ year = record.get('year'),
+ date = new Date();
+ date.setFullYear(year, month);
+ me.renderMonth(date);
+ });
+ me.enableKeyEvents && this.monthsPicker.on('item:keydown', function(view, record, e) {
+ if (e.keyCode==Common.UI.Keys.ESC) {
+ Common.NotificationCenter.trigger('dataview:blur');
+ }
+ });
+ } else
+ me.monthsPicker.store.reset(arrMonths);
+
+ me.enableKeyEvents && _.delay(function() {
+ me.monthPicker.cmpEl.find('.dataview').focus();
+ }, 10);
+ },
+
+ renderMonth: function (date) {
+ var me = this;
+ me._state = 0;
+ var firstDay = me.options.firstday;
+
+ // Current date
+ var curDate = date || new Date(),
+ curMonth = curDate.getMonth(),
+ curIndexDayInWeek = curDate.getDay(),
+ curNumberDayInMonth = curDate.getDate(),
+ curYear = curDate.getFullYear();
+
+ me.currentDate = curDate;
+
+ // Name month
+ me.topTitle = _.template([
+ '
');
+ documentHolderView.cmpEl.append(controlsContainer);
+ }
+
+ Common.UI.Menu.Manager.hideAll();
+
+ controlsContainer.css({left: x, top : y});
+ controlsContainer.show();
+
+ if (!this.cmpCalendar) {
+ this.cmpCalendar = new Common.UI.Calendar({
+ el: documentHolderView.cmpEl.find('#id-document-calendar-control'),
+ enableKeyEvents: true,
+ firstday: 1
+ });
+ this.cmpCalendar.on('date:click', function (cmp, date) {
+ specProps.put_FullDate(new Date(date));
+ me.api.asc_SetContentControlProperties(props, id);
+ controlsContainer.hide();
+ me.api.asc_UncheckContentControlButtons();
+ });
+ this.cmpCalendar.on('calendar:keydown', function (cmp, e) {
+ if (e.keyCode==Common.UI.Keys.ESC) {
+ controlsContainer.hide();
+ me.api.asc_UncheckContentControlButtons();
+ }
+ });
+ }
+ this.cmpCalendar.setDate(new Date(specProps ? specProps.get_FullDate() : undefined));
+
+ // align
+ var offset = controlsContainer.offset(),
+ docW = Common.Utils.innerWidth(),
+ docH = Common.Utils.innerHeight() - 10, // Yep, it's magic number
+ menuW = this.cmpCalendar.cmpEl.outerWidth(),
+ menuH = this.cmpCalendar.cmpEl.outerHeight(),
+ buttonOffset = 22,
+ left = offset.left - menuW,
+ top = offset.top;
+ if (top + menuH > docH) {
+ top = docH - menuH;
+ left -= buttonOffset;
+ }
+ if (top < 0)
+ top = 0;
+ if (left + menuW > docW)
+ left = docW - menuW;
+ this.cmpCalendar.cmpEl.css({left: left, top : top});
+
+ documentHolderView._preventClick = true;
+ },
+
+ onShowListActions: function(obj, x, y) {
+ var type = obj.type,
+ props = obj.pr,
+ id = props.get_InternalId(),
+ specProps = (type == Asc.c_oAscContentControlSpecificType.ComboBox) ? props.get_ComboBoxPr() : props.get_DropDownListPr(),
+ menu = this.view.listControlMenu,
+ documentHolderView = this.getApplication().getController('DocumentHolder').documentHolder,
+ menuContainer = menu ? documentHolderView.cmpEl.find(Common.Utils.String.format('#menu-container-{0}', menu.id)) : null,
+ me = this;
+
+ this._fromShowContentControls = true;
+ Common.UI.Menu.Manager.hideAll();
+
+ if (!menu) {
+ this.view.listControlMenu = menu = new Common.UI.Menu({
+ menuAlign: 'tr-bl',
+ items: []
+ });
+ menu.on('item:click', function(menu, item) {
+ setTimeout(function(){
+ me.api.asc_SelectContentControlListItem(item.value, id);
+ }, 1);
+ });
+
+ // Prepare menu container
+ if (!menuContainer || menuContainer.length < 1) {
+ menuContainer = $(Common.Utils.String.format('
', menu.id));
+ documentHolderView.cmpEl.append(menuContainer);
+ }
+
+ menu.render(menuContainer);
+ menu.cmpEl.attr({tabindex: "-1"});
+ menu.on('hide:after', function(){
+ me.view.listControlMenu.removeAll();
+ if (!me._fromShowContentControls)
+ me.api.asc_UncheckContentControlButtons();
+ });
+ }
+ if (specProps) {
+ var count = specProps.get_ItemsCount();
+ for (var i=0; iPlease enter a numeric value between 1 and 100',
diff --git a/apps/documenteditor/main/app/template/ControlSettingsDialog.template b/apps/documenteditor/main/app/template/ControlSettingsDialog.template
new file mode 100644
index 000000000..70df12159
--- /dev/null
+++ b/apps/documenteditor/main/app/template/ControlSettingsDialog.template
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/documenteditor/main/app/template/ImageSettings.template b/apps/documenteditor/main/app/template/ImageSettings.template
index 8abc9eaf0..9fae4b54e 100644
--- a/apps/documenteditor/main/app/template/ImageSettings.template
+++ b/apps/documenteditor/main/app/template/ImageSettings.template
@@ -14,17 +14,17 @@