From 00acb4641119e5c56b03c75ac62657f6acadb04b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 13 Jun 2018 14:00:39 +0200 Subject: [PATCH] Update to TinyMCE 4.7.13, refactor image selector dialog, add style for Elastic skin --- CHANGELOG | 1 + jsdeps.json | 6 +- program/include/rcmail.php | 8 +- program/js/editor.js | 153 ++--- program/resources/tinymce/browser.css | 87 ++- skins/classic/mail.css | 9 + skins/elastic/meta.json | 3 +- skins/elastic/styles/styles.less | 1 + skins/elastic/styles/widgets/editor.less | 700 +++++++++++++++++++++++ skins/elastic/styles/widgets/forms.less | 116 +--- skins/elastic/ui.js | 44 +- skins/larry/mail.css | 3 +- skins/larry/styles.css | 12 + skins/larry/templates/compose.html | 2 +- 14 files changed, 863 insertions(+), 282 deletions(-) create mode 100644 skins/elastic/styles/widgets/editor.less diff --git a/CHANGELOG b/CHANGELOG index 89c5a8df9..6b5e56917 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ CHANGELOG Roundcube Webmail - Support for IMAP folders that cannot contain both folders and messages (#5057) - Update to jQuery-3.3.1 - Update to jQuery-minicolors 2.2.6 +- Update to TinyMCE 4.7.13 - Remove sample PHP configuration from .htaccess and .user.ini files (#5850) - Use Masterminds/HTML5 parser for better HTML5 support (#5761) - Add More actions button in Contacts toolbar with Copy/Move actions (#6081) diff --git a/jsdeps.json b/jsdeps.json index 476056bc7..bf8770b5c 100644 --- a/jsdeps.json +++ b/jsdeps.json @@ -36,10 +36,10 @@ }, { "lib": "tinymce", - "version": "4.5.8", + "version": "4.7.13", "url": "http://download.ephox.com/tinymce/community/tinymce_$v.zip", "dest": "program/js", - "sha1": "08b0757264adb86066940bbafb7aa9ec0c7c6685", + "sha1": "7f988f3899aebee6d49bd55e981331da07eee6c5", "license": "LGPL", "copyright": "Copyright (c) 1999-2015 Ephox Corp. All rights reserved", "rm": "program/js/tinymce", @@ -56,7 +56,7 @@ }, { "lib": "tinymce-langs", - "version": "4.5.8", + "version": "4.7.13", "url": "https://tinymce-services.azurewebsites.net/1/i18n/download?langs=ar,hy,az,eu,be,bs,bg_BG,ca,zh_CN,zh_TW,hr,cs,cs_CZ,da,nl,en_CA,en_GB,eo,et,fo,fi,fr_FR,fr_CH,gd,gl,ka_GE,de,de_AT,el,he_IL,hi_IN,hu_HU,is_IS,id,ga,it,ja,kab,km_KH,ko_KR,ku,ku_IQ,lv,lt,lb,mk_MK,ml_IN,nb_NO,oc,fa,fa_IR,pl,pt_BR,pt_PT,ro,ru,sk,sl_SI,es,es_MX,sv_SE,tg,ta,ta_IN,tt,th_TH,tr,tr_TR,ug,uk,uk_UA,vi,vi_VN,cy", "dest": "program/js/tinymce" }, diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 761e496d4..be44cf4cc 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -1998,7 +1998,13 @@ class rcmail extends rcube $this->output->add_label('selectimage', 'addimage', 'selectmedia', 'addmedia'); $this->output->set_env('editor_config', $config); - $this->output->include_css('program/resources/tinymce/browser.css'); + + if ($path = $this->config->get('media_browser_css_location', 'program/resources/tinymce/browser.css')) { + if ($path != 'none') { + $this->output->include_css($path); + } + } + $this->output->include_script('tinymce/tinymce.min.js'); $this->output->include_script('editor.js'); } diff --git a/program/js/editor.js b/program/js/editor.js index 1144347bf..a0e220bf3 100644 --- a/program/js/editor.js +++ b/program/js/editor.js @@ -39,7 +39,7 @@ function rcube_text_editor(config, id) abs_url = location.href.replace(/[?#].*$/, '').replace(/\/$/, ''), conf = { selector: '#' + ($('#' + id).is('.mce_editor') ? id : 'fake-editor-id'), - cache_suffix: 's=4050800', + cache_suffix: 's=4071300', theme: 'modern', language: config.lang, content_css: rcmail.assets_path('program/resources/tinymce/content.css'), @@ -55,7 +55,9 @@ function rcube_text_editor(config, id) image_description: false, paste_webkit_style: "color font-size font-family", paste_data_images: true, - browser_spellcheck: true + browser_spellcheck: true, + anchor_bottom: false, + anchor_top: false }; // register spellchecker for plain text editor @@ -146,6 +148,9 @@ function rcube_text_editor(config, id) return false; } }); + ed.on('focus blur', function(e) { + $(ed.getContainer()).toggleClass('focused'); + }); }; rcmail.triggerEvent('editor-init', {config: conf, ref: ref}); @@ -648,21 +653,27 @@ function rcube_text_editor(config, id) // image selector this.file_browser_callback = function(field_name, url, type) { - var i, elem, cancel, dialog, fn, list = []; + var i, button, elem, cancel, dialog, fn, hint, list = [], + form = $('.upload-form').clone(); // open image selector dialog - dialog = this.editor.windowManager.open({ + this.editor.windowManager.open({ title: rcmail.get_label('select' + type), width: 500, - height: 300, - html: '
' - + '
', + html: '
', buttons: [{text: 'Cancel', onclick: function() { ref.file_browser_close(); }}] }); rcmail.env.file_browser_field = field_name; rcmail.env.file_browser_type = type; + dialog = $('#image-selector'); + + if (form.length) + button = dialog.prepend(form).find('button,a.button') + .text(rcmail.get_label('add' + type)) + .focus(); + // fill images list with available images for (i in rcmail.env.attachments) { if (elem = ref.file_browser_entry(i, rcmail.env.attachments[i])) { @@ -670,47 +681,44 @@ function rcube_text_editor(config, id) } } - if (list.length) { - $('#image-selector-list > ul').append(list).find('li:first').focus(); - } + cancel = dialog.parent().parent().find('button:last').parent(); - // add hint about max file size (in dialog footer) - $('div.mce-abs-end', dialog.getEl()).append($('
') - .text($('div.hint', rcmail.gui_objects.uploadform).text())); + // Add custom Tab key handlers, tabindex does not work + list = $('#image-selector-list').append(list).on('keydown', 'li', function(e) { + if (e.which == 9) { + if (rcube_event.get_modifier(e) == SHIFT_KEY) { + if (!$(this).prev().focus().length) { + button.focus(); + } + } + else if (!$(this).next().focus().length) { + cancel.focus(); + } - // init upload button - elem = $('#image-upload-button').append($('').text(rcmail.get_label('add' + type))); - cancel = elem.parents('.mce-panel').find('button:last').parent(); + return false; + } + }); - // we need custom Tab key handlers, until we find out why - // tabindex do not work here as expected - elem.keydown(function(e) { + button.keydown(function(e) { if (e.which == 9) { - // on Tab + Shift focus first file - if (rcube_event.get_modifier(e) == SHIFT_KEY) - $('#image-selector-list li:last').focus(); - // on Tab focus Cancel button - else + if (rcube_event.get_modifier(e) == SHIFT_KEY || !list.find('li:first').focus().length) { cancel.focus(); + } return false; } }); + cancel.keydown(function(e) { if (e.which == 9) { - // on Tab + Shift focus upload button - if (rcube_event.get_modifier(e) == SHIFT_KEY) - elem.focus(); - else - $('#image-selector-list li:first').focus(); + if (rcube_event.get_modifier(e) != SHIFT_KEY || !list.find('li:last').focus().length) { + button.focus(); + } return false; } }); - // enable (smart) upload button - this.hack_file_input(elem, rcmail.gui_objects.uploadform); - // enable drag-n-drop area if ((window.XMLHttpRequest && XMLHttpRequest.prototype && XMLHttpRequest.prototype.sendAsBinary) || window.FormData) { if (!rcmail.env.filedrop) { @@ -720,7 +728,7 @@ function rcube_text_editor(config, id) rcmail.env.old_file_drop = rcmail.gui_objects.filedrop; } - rcmail.gui_objects.filedrop = $('#image-selector-form'); + rcmail.gui_objects.filedrop = $('#image-selector'); rcmail.gui_objects.filedrop.addClass('droptarget') .on('dragover dragleave', function(e) { e.preventDefault(); @@ -736,7 +744,7 @@ function rcube_text_editor(config, id) rcmail.addEventListener('fileuploaded', function(attr) { var elem; if (elem = ref.file_browser_entry(attr.name, attr.attachment)) { - $('#image-selector-list > ul').prepend(elem); + list.prepend(elem); elem.focus(); } }); @@ -781,7 +789,7 @@ function rcube_text_editor(config, id) case 'media': rx = /^video\//i; - img_src = 'program/resources/tinymce/video.png'; + img_src = rcmail.assets_path('program/resources/tinymce/video.png'); break; default: @@ -803,82 +811,7 @@ function rcube_text_editor(config, id) if (e.which == 13) { ref.file_browser_close($(this).data('url')); } - // we need custom Tab key handlers, until we find out why - // tabindex do not work here as expected - else if (e.which == 9) { - if (rcube_event.get_modifier(e) == SHIFT_KEY) { - if (!$(this).prev().focus().length) - $('#image-upload-button').parents('.mce-panel').find('button:last').parent().focus(); - } - else { - if (!$(this).next().focus().length) - $('#image-upload-button').focus(); - } - - return false; - } }); } }; - - // create smart files upload button - this.hack_file_input = function(elem, clone_form) - { - var offset, link = $(elem), - file = $('').attr('name', '_file[]'), - form = $('
').attr({method: 'post', enctype: 'multipart/form-data'}); - - // clone existing upload form - if (clone_form) { - file.attr('name', $('input[type="file"]', clone_form).attr('name')); - form.attr('action', $(clone_form).attr('action')) - .append($('').attr({type: 'hidden', name: '_token', value: rcmail.env.request_token})); - } - - function move_file_input(e) { - if (!offset) offset = link.offset(); - file.css({top: (e.pageY - offset.top - 10) + 'px', left: (e.pageX - offset.left - 10) + 'px'}); - } - - file.attr({type: 'file', multiple: 'multiple', size: 5, title: '', tabindex: -1}) - .change(function() { rcmail.upload_file(form, 'upload'); }) - .click(function() { setTimeout(function() { link.mouseleave(); }, 20); }) - // opacity:0 does the trick, display/visibility doesn't work - .css({opacity: 0, cursor: 'pointer', position: 'relative', outline: 'none'}) - .appendTo(form); - - // In FF and IE we need to move the browser file-input's button under the cursor - // Thanks to the size attribute above we know the length of the input field - if (navigator.userAgent.match(/Firefox|MSIE/)) - file.css({marginLeft: '-80px'}); - - // Note: now, I observe problem with cursor style on FF < 4 only - link.css({overflow: 'hidden', cursor: 'pointer'}) - .mouseenter(function() { this.__active = true; }) - // place button under the cursor - .mousemove(function(e) { - if (this.__active) - move_file_input(e); - // move the input away if button is disabled - else - $(this).mouseleave(); - }) - .mouseleave(function() { - file.css({top: '-10000px', left: '-10000px'}); - this.__active = false; - }) - .click(function(e) { - // forward click if mouse-enter event was missed - if (!this.__active) { - this.__active = true; - move_file_input(e); - file.trigger(e); - } - }) - .keydown(function(e) { - if (e.which == 13) file.trigger('click'); - }) - .mouseleave() - .append(form); - }; } diff --git a/program/resources/tinymce/browser.css b/program/resources/tinymce/browser.css index 0993af845..079077237 100644 --- a/program/resources/tinymce/browser.css +++ b/program/resources/tinymce/browser.css @@ -1,45 +1,54 @@ /* This file contains the CSS data for media file selector of TinyMCE */ -#image-selector-list { - position: absolute; - top: 0; - left: 0; - right: 152px; - height: 100%; - overflow-x: hidden; - overflow-y: auto; + +#image-selector { + margin: 10px; + margin-bottom: 30px; + padding-bottom: 85px; +} + +#image-selector.droptarget.hover, +#image-selector.droptarget.active { + border: 1px solid #019bc6; + box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); +} + +#image-selector.droptarget.hover { + background-color: #d9ecf4; + box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); } -#image-selector-form { +#image-selector form { position: absolute; top: 0; - bottom: 0; - right: 0; - width: 150px; - border: 0; - border: 1px solid #FFF; - border-left: 1px solid #DDD; +} + +#image-selector .hint { + line-height: 25px; + color: #666; + font-size: 11px; text-align: center; - padding-top: 10px; } -#image-upload-button { - width: 80%; - height: 30px; +#image-selector a.button { + color: #525252; + text-decoration: none; + font-size: 11px; } -#image-upload-button span { - position: absolute; - width: 100%; - text-align: center; - line-height: 30px; +#image-selector-list { + overflow-x: hidden; + overflow-y: auto; + margin-left: 0; + height: 250px; } #image-selector-list li { line-height: 80px; - padding: 2px 0 2px 3px; + padding: 2px; cursor: pointer; overflow: hidden; text-overflow: ellipsis; + background: none; } #image-selector-list li:hover, @@ -47,18 +56,18 @@ background-color: #F0F0F0; } -#image-selector-list ul li img { +#image-selector-list li img { vertical-align: middle; max-height: 80px; } -#image-selector-list ul li span.name { +#image-selector-list li span.name { vertical-align: middle; font-weight: bold; padding-left: 10px; } -#image-selector-list ul li span.img { +#image-selector-list li span.img { height: 80px; width: 80px; text-align: center; @@ -66,25 +75,3 @@ overflow: hidden; line-height: 80px; } - -#image-selector-form.droptarget.hover, -#image-selector-form.droptarget.active { - border: 1px solid #019bc6; - box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); - -moz-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); - -webkit-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5); -} - -#image-selector-form.droptarget.hover { - background-color: #d9ecf4; - box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); - -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); - -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); -} - -div.mce-abs-end div.hint { - line-height: 50px; - padding-left: 10px; - color: #999; - text-shadow: 0 1px 1px #FFF; -} diff --git a/skins/classic/mail.css b/skins/classic/mail.css index 4f4c4b028..3ea340692 100644 --- a/skins/classic/mail.css +++ b/skins/classic/mail.css @@ -1416,6 +1416,11 @@ div.hide-headers #compose-body-div .mce-tinymce { border: 0 !important; + width: 100% !important; +} + +.mce-top-part::before { + box-shadow: none !important; } #compose-div .boxlistcontent @@ -1759,6 +1764,10 @@ input.from_address position: absolute; } +#image-selector { + padding-bottom: 0 !important; +} + /**** Styles for widescreen (3-column) view ****/ .widescreen #mailview-top { diff --git a/skins/elastic/meta.json b/skins/elastic/meta.json index ced1beef3..a4cf87034 100644 --- a/skins/elastic/meta.json +++ b/skins/elastic/meta.json @@ -6,6 +6,7 @@ "config": { "layout": "widescreen", "jquery_ui_colors_theme": "bootstrap", - "embed_css_location": "/styles/embed.css" + "embed_css_location": "/styles/embed.css", + "media_browser_css_location": "none" } } diff --git a/skins/elastic/styles/styles.less b/skins/elastic/styles/styles.less index 778401c0d..3063d2692 100644 --- a/skins/elastic/styles/styles.less +++ b/skins/elastic/styles/styles.less @@ -127,6 +127,7 @@ a { @import "widgets/searchbar"; @import "widgets/lists"; @import "widgets/forms"; +@import "widgets/editor"; @import "widgets/mail"; diff --git a/skins/elastic/styles/widgets/editor.less b/skins/elastic/styles/widgets/editor.less new file mode 100644 index 000000000..2798df123 --- /dev/null +++ b/skins/elastic/styles/widgets/editor.less @@ -0,0 +1,700 @@ +/** + * Roundcube webmail styles for the Elastic skin + * + * Copyright (c) 2017-2018, The Roundcube Dev Team + * + * The contents are subject to the Creative Commons Attribution-ShareAlike + * License. It is allowed to copy, distribute, transmit and to adapt the work + * by keeping credits to the original authors in the README.md file. + * See http://creativecommons.org/licenses/by-sa/3.0/ for details. + */ + + +/*** Text Editor widget (and TinyMCE editor) ***/ + +.mce-tinymce { + &.mce-container.mce-panel { + border-radius: .25rem; + border-color: @color-input-border; + overflow: hidden; + } + + .mce-btn, + .mce-panel { + background-color: @color-input-addon-background; + } + + .mce-panel { + border-color: @color-input-border; + } +} + +.mce-top-part::before, +.mce-tinymce, +.mce-window { + box-shadow: none !important; +} + +.mce-window { + &.mce-container { + border: 0; + + & :not(.mce-ico) { + .font-family; + } + } + + + .mce-container-body { + &.mce-abs-layout { + overflow: unset; + } + + .mce-abs-end { + display: none; + } + } + + .mce-window-head { + height: @layout-header-height; + border-bottom: 1px solid @color-dialog-header-border; + padding: 0; + + .mce-title { + line-height: @layout-header-height; + font-size: 1.25rem; + padding: 0 3rem 0 1rem; + color: @color-dialog-header; + } + + .mce-close { + border: 0; + color: @color-dialog-header; + background: transparent; + right: 0; + top: 0; + position: absolute; + height: (@layout-header-height - .7rem); + width: 1.25em; + margin: .25rem; + padding: .1rem .75rem; + cursor: pointer; + + &:before { + &:extend(.font-icon-class); + content: @fa-var-times; + margin: 0; + } + + i { + display: none; + } + } + } + + .mce-foot { + border: 0; + height: @layout-header-height !important; + position: relative; + + @media screen and (max-width: @screen-width-xs) { + border-top: 1px solid @color-dialog-header-border; + } + + .mce-container-body { + height: 100% !important; + display: flex; + align-items: center; + justify-content: end; + + .mce-btn { + position: initial; + margin-right: .5rem; + line-height: 1; + width: auto !important; + + .mce-txt { + line-height: 1.5; + vertical-align: unset; + } + + button:before { + &:extend(.font-icon-class); + display: inline; + float: none; + } + } + + .mce-abs-end { + position: initial; + width: 1rem; + order: 9; + } + } + + .mce-btn:last-child button:before { + content: @fa-var-times; + } + + .mce-btn.mce-primary button:before { + content: @fa-var-check; + } + + .mce-search-foot { + div:nth-of-type(2) button:before { + content: @fa-var-search; + } + div:nth-of-type(3) button:before, + div:nth-of-type(4) button:before { + content: @fa-var-check; + } + div:nth-of-type(6) button:before { + content: @fa-var-chevron-left; + } + div:nth-of-type(7) button:before { + content: @fa-var-chevron-right; + } + } + } + + // Form elements, let's keep'em in .mce-window to make overwriting simpler + + .mce-textbox { + padding: .375rem .75rem; + line-height: 1.5; + color: @color-font; + + &:not(textarea) { + height: auto !important; + } + + &:focus { + color: @color-font; + border-color: @color-input-border-focus; + box-shadow: 0 0 0 .2rem @color-input-border-focus-shadow; + } + + &[size="5"] { + width: auto !important; + } + } + + .mce-formitem { + .mce-widget { + border-radius: .25rem; + } + } + + .mce-checkbox { + line-height: 1.5; + + i.mce-i-checkbox { + border: 0; + width: auto; + color: @color-checkbox; + text-indent: 0; + + &:before { + &:extend(.font-icon-class); + margin: 0; + content: @fa-var-toggle-off; + } + } + + &.mce-checked i.mce-i-checkbox:before { + content: @fa-var-toggle-on; + } + + &:focus { + i.mce-i-checkbox { + border: 0; + color: @color-checkbox-focus; + } + } + } + + .mce-combobox { + display: flex; + + input { + border-radius: .3rem 0 0 .3rem; + flex: 1; + + &:focus { + z-index: 1; + } + } + + button { + padding: .4rem .6rem; + } + + .mce-btn { + border-radius: 0 .3rem .3rem 0; + background: @color-input-addon-background; + + &:focus { + border-color: @color-input-border-focus; + box-shadow: 0 0 0 .2rem @color-input-border-focus-shadow; + } + } + } + + .mce-tabs { + padding-top: 1rem; + margin: 0 1rem; + border-color: @color-layout-border; + + .mce-tab { + border-radius: .25rem .25rem 0 0; + padding: .5rem 1rem; + height: auto !important; + border: 1px solid transparent; + color: @color-link; + background: transparent; + margin-bottom: -1px; + + &.mce-active { + border: 1px solid @color-layout-border; + border-bottom-color: #fff; + color: @color-font !important; + } + + &:not(.mce-active):hover { + border: 1px solid @color-list-border; + border-bottom-color: transparent; + border-bottom: 0; + } + } + } + + .mce-label { + text-shadow: none; + color: @color-font; + } + + .mce-form { + .mce-container { + height: auto !important; + + .mce-container-body { + display: flex; + + & > label { + position: relative; + } + + & > input:not([size="5"]) { + position: relative; + left: 0 !important; + flex: 1; + } + } + } + } +} + +.mce-btn { + &.mce-active { + background: @color-btn-secondary-background !important; + } + + .mce-window .mce-foot & { + .btn-secondary; + border-radius: .3rem; + border-color: transparent; + + &:focus { + border-color: transparent !important; + color: @color-btn-secondary; + background: @color-btn-secondary-background; + } + + &.mce-primary { + .btn-primary; + } + + button:hover, + button { + color: @color-btn-secondary; + padding: .5rem .75rem; + } + } +} + +// Menus and popovers, e.g. color selector, emoticons selector, font selector +.mce-menu, +.mce-floatpanel.mce-popover { + box-shadow: 3px 3px 5px @color-popover-shadow !important; + border-color: @color-layout-border !important; + border-radius: .3rem; +} + +.mce-menu { + .mce-menu-item.mce-active { + color: @color-toolbarmenu-hover; + background-color: @color-toolbarmenu-hover-background; + } +} + +#mce-modal-block.mce-in { + background-color: @color-dialog-overlay-background; + opacity: 1 !important; +} + +@media screen and (max-width: @screen-width-xs) { + .mce-window { + width: 100% !important; + height: 100% !important; + left: 0 !important; + top: 0 !important; + border-width: 0 !important; + + & > .mce-reset { + display: flex; + flex-direction: column; + height: 100%; + } + + .mce-window-body { + flex: 1; + overflow-y: auto !important; + } + + & > .mce-reset > div, + .mce-container-body { + width: 100% !important; + } + + .mce-foot { + .mce-container-body { + justify-content: space-evenly; + + .mce-btn { + position: initial; + height: 100% !important; + margin: 0; + background: transparent; + border-width: 0; + + &:focus { + box-shadow: none; + } + + &:hover { + background: transparent; + } + + button { + color: @color-font; + padding: .45rem; + font-size: .9rem; + + &:before { + display: block; + float: none; + width: 100%; + margin: 0; + line-height: 1.75; + height: 1.75rem; + } + } + } + + .mce-abs-end { + display: none; + } + } + } + } + + .mce-menu { + width: auto !important; + right: 0; + left: 0 !important; + margin: 0 1rem !important; + + .mce-container-body { + width: 100% !important; + } + } + + div.mce-menubtn.mce-opened { + z-index: 65530 !important; + } + + .mce-container.mce-abs-layout-item.mce-first + .mce-container.mce-abs-layout-item.mce-last:not(.mce-formitem), + .mce-menu-item-sep, + .mce-menu-shortcut { + display: none !important; + } + + .mce-charmap-dialog { + position: unset; + } + + .mce-charmap { + display: block; + + tbody { + display: block; + } + + tr { + display: flex; + flex-wrap: wrap; + } + + td { + flex: 1; + height: auto !important; + min-width: 17%; + padding: 0 !important; + border: 0 !important; + border-bottom: 1px solid @color-list-border !important; + + div { + font-size: 3rem; + line-height: 2; + } + } + } + + .mce-form, + .mce-form + .mce-container, // for Embed tab in Media dialog + .mce-formitem, + .mce-combobox, + .mce-panel:not(.mce-popover) { + width: 100% !important; + } + + .mce-form + .mce-container, // for Embed tab in Media dialog + .mce-form { + & > .mce-container-body { + display: flex; + flex-direction: column; + padding: 0 1rem; + left: 0; + right: 0; + box-sizing: border-box; + + & > .mce-container { + position: unset; + } + } + + .mce-container-body { + height: auto !important; + flex-direction: column; + + & > label { + position: unset !important; + width: 100% !important; + margin-top: 1rem; + } + + & > label + * { + position: unset !important; + margin-top: .5rem; + width: auto !important; + flex: 1; + } + + & > .mce-checkbox { + position: absolute; + left: 0 !important; + top: 3rem !important; + } + } + } + + // FIXME: How to fix the input width in less hacky way? + .mce-window .mce-combobox input { + max-width: ~"calc(100% - 4rem)"; + } +} + + +/*** Media file selector for TinyMCE ***/ + +.image-selector { + margin: 1rem 1rem 1rem 1rem !important; + padding: 1rem .5rem 10rem .5rem !important; + + &.droptarget { + border: .2rem dashed @color-table-border; + } + + button { + .btn-secondary; + padding: .5rem .75rem; + line-height: 1.5; + position: relative; + + &:before { + line-height: 1; + } + } + + form { + position: absolute; + top: 0; + } + + .attachmentslist { + margin-left: 0; + overflow-x: hidden; + overflow-y: auto; + height: 18em; + + li { + padding: .25rem; + cursor: pointer; + + &:before { + display: none; + } + + &:hover, + &:focus { + background: @color-list-selected-background; + } + + span.name { + flex: 1; + margin: auto; + padding-left: 1rem; + .overflow-ellipsis; + } + + span.img { + height: 80px; + width: 80px; + display: flex; + border: 1px solid @color-list-border; + background: white; + border-radius: .75rem; + overflow: hidden; + } + + img { + margin: auto; + } + } + } + + @media screen and (max-width: @screen-width-small) { + .attachmentslist { + height: auto; + } + } +} + + +/*** HTML editor widget ***/ + +.html-editor { + position: relative; + display: flex; + margin-bottom: .25rem; + + & > .nav { + border-color: transparent; + z-index: 1; + position: absolute; + top: 1px; + right: 1rem; + + a.active { + border-color: @color-input-border !important; + + &.mode-html { + background-color: @color-input-addon-background; + border-bottom-color: @color-input-addon-background !important; + } + + &.mode-plain { + border-bottom-color: #fff !important; + } + } + + a:hover { + border-bottom-color: transparent; + } + } + + & > iframe, // e.g. mailvelope frame + & > .googie_edit_layer, + & > .mce-tinymce, + & > textarea { + margin-top: 2.55rem; + font-family: monospace; + width: 100% !important; + } + + & > iframe { // e.g. mailvelope frame + border-radius: .3rem; + border: 1px solid @color-input-border; + min-height: 30em; + } + + #composebody_ifr { + min-height: 30em; + } + + & > .mce-tinymce.focused { + border-color: @color-input-border-focus; + box-shadow: 0 0 0 .2rem @color-input-border-focus-shadow !important; + } +} + + +/*** GoogieSpell widget ***/ + +.googie_window { + width: 16rem; +} + +.googie_edit_layer { + font-family: monospace; + + // from Bootstrap's textarea + padding: .5rem .75rem; + border: 1px solid @color-input-border; + border-radius: .3rem; + line-height: 1.25; +} + +.googie_link { + color: @color-spellcheck-link; + text-decoration: underline; + cursor: pointer; +} + +.googie_list { + td { + min-width: 8rem; + width: auto; + + &.googie_list_onhover { + color: @color-toolbarmenu-hover; + background-color: @color-toolbarmenu-hover-background; + } + + .googie_list_revert:before { + &:extend(.font-icon-class); + content: @fa-var-plus-square; + } + + .googie_add_to_dict:before { + &:extend(.font-icon-class); + content: @fa-var-plus-square; + } + } + + input { + display: inline-block; + margin: .5rem .5rem .5rem 0 !important; + padding: .5rem .75rem !important; + } +} diff --git a/skins/elastic/styles/widgets/forms.less b/skins/elastic/styles/widgets/forms.less index 2f9d32b0e..9c8b786ef 100644 --- a/skins/elastic/styles/widgets/forms.less +++ b/skins/elastic/styles/widgets/forms.less @@ -833,6 +833,7 @@ html.touch .popupmenu.form { .hint { margin-bottom: .5rem; color: @color-form-hint; + text-align: center; } .attachmentslist { @@ -1083,7 +1084,6 @@ html.touch .popupmenu.form { /*** Percent input with jQuery-UI slider ***/ - // Structure: .input-percent-slider { display: flex; @@ -1104,6 +1104,7 @@ html.touch .popupmenu.form { } } + /*** Image upload widget ***/ .image-upload { @@ -1204,119 +1205,6 @@ html.touch input.icon-checkbox + label { } -/*** HTML editor widget (and TinyMCE editor) ***/ - -.mce-tinymce { - &.mce-container.mce-panel { - border-radius: .25rem; - border-color: @color-input-border; - overflow: hidden; - } - - .mce-btn, - .mce-panel { - background-color: @color-input-addon-background; - } -} - -.html-editor { - position: relative; - display: flex; - margin-bottom: .25rem; - - & > .nav { - border-color: transparent; - z-index: 1; - position: absolute; - top: 1px; - right: 1rem; - - a.active { - border-color: @color-input-border !important; - - &.mode-html { - background-color: @color-input-addon-background; - border-bottom-color: @color-input-addon-background !important; - } - - &.mode-plain { - border-bottom-color: #fff !important; - } - } - - a:hover { - border-bottom-color: transparent; - } - } - - & > iframe, // e.g. mailvelope frame - & > .googie_edit_layer, - & > .mce-tinymce, - & > textarea { - margin-top: 2.55rem; - font-family: monospace; - width: 100% !important; - } - - & > iframe { // e.g. mailvelope frame - border-radius: .3rem; - border: 1px solid @color-input-border; - min-height: 35em; - } -} - - -/*** GoogieSpell widget ***/ - -.googie_window { - width: 16rem; -} - -.googie_edit_layer { - font-family: monospace; - - // from Bootstrap's textarea - padding: .5rem .75rem; - border: 1px solid @color-input-border; - border-radius: .3rem; - line-height: 1.25; -} - -.googie_link { - color: @color-spellcheck-link; - text-decoration: underline; - cursor: pointer; -} - -.googie_list { - td { - min-width: 8rem; - width: auto; - - &.googie_list_onhover { - color: @color-toolbarmenu-hover; - background-color: @color-toolbarmenu-hover-background; - } - - .googie_list_revert:before { - &:extend(.font-icon-class); - content: @fa-var-plus-square; - } - - .googie_add_to_dict:before { - &:extend(.font-icon-class); - content: @fa-var-plus-square; - } - } - - input { - display: inline-block; - margin: .5rem .5rem .5rem 0 !important; - padding: .5rem .75rem !important; - } -} - - /*** General browser hacks ***/ // Remove outline on selects in Firefox diff --git a/skins/elastic/ui.js b/skins/elastic/ui.js index 190e33e8b..384a1dd5c 100644 --- a/skins/elastic/ui.js +++ b/skins/elastic/ui.js @@ -321,7 +321,9 @@ function rcube_elastic_ui() $(list)[$('.treetoggle', list).length > 0 ? 'removeClass' : 'addClass']('notree'); }; - (new MutationObserver(callback)).observe(list, {childList: true, subtree: true}); + if (window.MutationObserver) { + (new MutationObserver(callback)).observe(list, {childList: true, subtree: true}); + } callback(); }); }; @@ -444,6 +446,20 @@ function rcube_elastic_ui() .addEventListener('setquota', update_quota) .addEventListener('enable-command', enable_command_handler) .addEventListener('init', init); + + // Add styling for TinyMCE editor popups + // We need to use MutationObserver, as TinyMCE does not provide any events for this + if (window.MutationObserver && window.tinymce) { + var callback = function(list) { + $.each(list, function() { + $.each(this.addedNodes, function() { + tinymce_style(this); + }); + }); + }; + + (new MutationObserver(callback)).observe(document.body, {childList: true}); + } }; /** @@ -914,6 +930,32 @@ function rcube_elastic_ui() $('select:not([multiple])', context).each(function() { pretty_select(this); }); }; + /** + * Detects if the element is TinyMCE dialog window + * and adds Elastic styling to it + */ + function tinymce_style(elem) + { + if ($(elem).is('.mce-window')) { + var body = $(elem).find('.mce-window-body'), + foot = $(elem).find('.mce-foot > .mce-container-body'); + + // Apply basic forms style + if (body.length) { + bootstrap_style(body[0]); + } + + body.find('button').filter(function() { return $(this).parent('.mce-btn').length > 0; }).removeClass('btn btn-secondary'); + + // Fix icons in Find and Replace dialog footer + if (foot.children('.mce-widget').length === 5) { + foot.addClass('mce-search-foot'); + } + + $(elem).find('.mce-charmap').parent().parent().addClass('mce-charmap-dialog'); + } + }; + /** * Initializes popup menus */ diff --git a/skins/larry/mail.css b/skins/larry/mail.css index 3644eca13..1f75edec8 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -1305,6 +1305,7 @@ body.status-flagged .flag-icon { overflow: auto; } +#image-selector.droptarget, #compose-attachments.droptarget { background-image: url(images/filedrop.png); background-position: center bottom; @@ -1345,7 +1346,7 @@ body.status-flagged .flag-icon { } #composebodycontainer .mce-panel { - border-color: #ccc !important; + border-color: #ddd !important; background: #f0f0f0; } diff --git a/skins/larry/styles.css b/skins/larry/styles.css index 3458c6acf..d073302ec 100644 --- a/skins/larry/styles.css +++ b/skins/larry/styles.css @@ -3060,6 +3060,14 @@ ul.toolbarmenu li span.copy { } /** Common TinyMCE fixes **/ +.mce-btn:not(.mce-active) { + background: transparent !important; +} + +.mce-btn:not(.mce-active):hover { + background: white !important; +} + .mce-btn-small .mce-ico { display: inline; /* for old Firefox */ } @@ -3073,6 +3081,10 @@ _:not(), _:-moz-handler-blocked, .mozilla .mce-btn-small i { line-height: 20px !important; } +.mce-top-part::before { + box-shadow: none !important; +} + .mce-textbox { border-radius: 0; box-shadow: none; diff --git a/skins/larry/templates/compose.html b/skins/larry/templates/compose.html index 80a928087..8f4688c75 100644 --- a/skins/larry/templates/compose.html +++ b/skins/larry/templates/compose.html @@ -178,7 +178,7 @@

-
+