program/localization/en_US/labels.inc | ●●●●● patch | view | raw | blame | history | |
skins/larry/images/zen-form-sprites.png | patch | view | raw | blame | history | |
skins/larry/includes/footer.html | ●●●●● patch | view | raw | blame | history | |
skins/larry/mail.css | ●●●●● patch | view | raw | blame | history | |
skins/larry/templates/compose.html | ●●●●● patch | view | raw | blame | history | |
skins/larry/ui.js | ●●●●● patch | view | raw | blame | history | |
skins/larry/zen-form.css | ●●●●● patch | view | raw | blame | history | |
skins/larry/zen-form.js | ●●●●● patch | view | raw | blame | history |
program/localization/en_US/labels.inc
@@ -300,6 +300,11 @@ $labels['addreplyto'] = 'Add Reply-To'; $labels['addfollowupto'] = 'Add Followup-To'; // zen mode labels $labels['editfullscreen'] = 'Edit in fullscreen'; $labels['exitfullscreen'] = 'Exit fullscreen mode'; $labels['switchtheme'] = 'Switch theme'; // mdn $labels['mdnrequest'] = 'The sender of this message has asked to be notified when you read this message. Do you wish to notify the sender?'; $labels['receiptread'] = 'Return Receipt (read)'; skins/larry/images/zen-form-sprites.png
skins/larry/includes/footer.html
@@ -7,6 +7,8 @@ $(document).ready(function(){ UI.set('errortitle', '<roundcube:label name="errortitle" quoting="javascript" />'); UI.set('toggleoptions', '<roundcube:label name="toggleadvancedoptions" quoting="javascript" />'); UI.set('exitfullscreen', '<roundcube:label name="exitfullscreen" quoting="javascript" />'); UI.set('switchtheme', '<roundcube:label name="switchtheme" quoting="javascript" />'); UI.init(); }); skins/larry/mail.css
@@ -1298,6 +1298,21 @@ bottom: 42px; } #composebodycontainer .go-zen { position: absolute; top: 6px; right: 4px; } #composebodycontainer .icon-go-zen { display: inline-block; width: 16px; height: 14px; background: url('data:image/gif;base64,R0lGODlhDgAMAPABAHl5ef///yH/C1hNUCBEYXRhWE1QAT8AIfkEBQAAAQAsAAAAAA4ADAAAAh+EEaln6t+YRHGegHB2bW3PfUk3itqoWdSqns8rwUcBADs=') 1px 1px no-repeat; text-indent: -5000px; overflow: hidden; } #composebody { position: absolute; top: 0; skins/larry/templates/compose.html
@@ -3,6 +3,7 @@ <head> <title><roundcube:object name="pagetitle" /></title> <roundcube:include file="/includes/links.html" /> <link rel="stylesheet" type="text/css" href="/zen-form.css" /> <roundcube:if condition="config:enable_spellcheck" /> <link rel="stylesheet" type="text/css" href="/googiespell.css" /> <roundcube:endif /> @@ -169,6 +170,7 @@ <div id="composebodycontainer"> <label for="composebody" class="voice"><roundcube:label name="arialabelmessagebody" /></label> <roundcube:object name="composeBody" id="composebody" form="form" cols="70" rows="20" tabindex="1" /> <a href="#" class="go-zen" title="<roundcube:label name='editfullscreen' />"><span class="icon-go-zen"><roundcube:label name="editfullscreen" /></span></a> </div> <div id="compose-attachments" class="rightcol" role="region" aria-labelledby="aria-label-composeattachments"> <h2 id="aria-label-composeattachments" class="voice"><roundcube:label name="attachments" /></h2> @@ -217,6 +219,8 @@ </ul> </div> <script type="text/javascript" src="/zen-form.js"></script> <roundcube:include file="/includes/footer.html" /> </body> skins/larry/ui.js
@@ -212,6 +212,25 @@ new rcube_splitter({ id:'composesplitterv', p1:'#composeview-left', p2:'#composeview-right', orientation:'v', relative:true, start:206, min:170, size:12, render:layout_composeview }).init(); // enable zen-mode for message body if ($.fn.zenForm) { $('#composebody').zenForm({ theme: 'light' }) .on('zf-initialized', function(event, zenbox) { var subject = $('#compose-subject').val(), $zenbox = $(zenbox); $('<h2>').addClass('zen-forms-subject') .text(subject ? subject : rcmail.gettext('nosubject')) .insertBefore('.zen-forms-close-button', zenbox); $zenbox.find('.zen-forms-close-button').attr('title', env.exitfullscreen) $zenbox.find('.zen-forms-theme-switch').attr('title', env.switchtheme) $zenbox.find('.input').first().focus(); }) .on('zf-destroyed', function(event) { $('#composebody').focus(); }); } } else if (rcmail.env.action == 'list' || !rcmail.env.action) { var previewframe = $('#mailpreviewframe').is(':visible'); @@ -497,7 +516,7 @@ form.css('overflow', ovflw > 0 ? 'auto' : 'hidden'); w = body.parent().width() - 5; h = body.parent().height() - 8; h = body.parent().height() - 16; body.width(w).height(h); $('#composebodycontainer > div').width(w+8); @@ -508,6 +527,9 @@ var abooks = $('#directorylist'); $('#compose-contacts .scroller').css('top', abooks.position().top + abooks.outerHeight()); // hide zen-mode switch in HTML mode $('a.go-zen')[($('#composebody_ifr').is(':visible') ? 'hide' : 'show')](); } skins/larry/zen-form.css
New file @@ -0,0 +1,340 @@ /* ================================== *\ * Zen Form * ================================== */ .zen-forms { font: normal 18px/1.2 "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; position: fixed; z-index: 99999; top: 0; left: 0; right: 0; bottom: 0; padding: 20px 40px 20px 20px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .zen-forms-body-wrap { width: 0; height: 0; overflow: hidden; } body { max-width: 100%; } .zen-forms-input-wrap { max-width: 800px; position: relative; margin: 0 auto; height: 90%; } /** * Buttons style */ .zen-forms-header { max-width: 800px; margin: 0 auto 16px auto; } .zen-forms-header .zen-forms-subject { float: left; display: inline; margin: 0; color: #808080; font-size: 16px; } .zen-forms-close-button, .zen-forms-theme-switch { float: right; position: relative; display: inline-block; cursor: pointer; font-size: .8em; padding: 0 .5em; vertical-align: top; width: 18px; height: 18px; text-indent: -5000px; overflow: hidden; white-space: nowrap; } .zen-icon { background-image: url(images/zen-form-sprites.png); display: inline-block; line-height: 1; position: absolute; zoom: 1; width: 16px; height: 16px; top: 1px; left: 1px; } .zen-icon:before { content: ""; display: block; width: 0; height: 100%; } .light-theme .zen-forms-close-button:hover .zen-icon, .zen-icon--close { background-position: -16px 0; } .light-theme .zen-forms-theme-switch:hover .zen-icon, .zen-icon--theme { background-position: 0 0; } .light-theme .zen-icon--close, .zen-forms-close-button:hover .zen-icon { background-position: -16px -16px; } .light-theme .zen-icon--theme, .zen-forms-theme-switch:hover .zen-icon { background-position: 0 -16px; } /** * Inputs basic style */ .zen-forms-header:after, .zen-forms-input-wrap:after { clear: both; content: ''; display: table; margin-bottom: 2px; } .zen-forms .input { box-shadow: none; background: none; text-shadow: none; padding: 7px 0; width: 100%; height: 100%; border-radius: 0; border: none; margin: 0; cursor: pointer; font: inherit; } .zen-forms .input:focus { cursor: text; } .zen-forms .select { display: none; } .zen-forms .custom-select-wrap + label { padding: 8px 3px; position: static; display: inline-block; width: auto; float: left; } .zen-forms .custom-select-wrap { display: inline-block; vertical-align: top; max-width: 100%; } .zen-forms .custom-select { border-radius: 5px; top: 0; right: 0; left: 0; overflow: auto; max-height: 300px; } .zen-forms .custom-select a { display: inline-block; display: block; text-decoration: none; padding: 6px 10px; display: none; max-width: 100%; overflow: hidden; text-overflow: ellipsis; } .zen-forms .custom-select span { position: relative; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .zen-forms .custom-select.is-open a, .zen-forms .custom-select .selected { position: static; display: block; } .zen-forms textarea { resize: none; } .zen-forms label { cursor: pointer; padding: 8px 8px 8px 0; display: inline-block; vertical-align: top; font: inherit; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .zen-forms .input + label { position: absolute; display: none; top: 0; width: 100%; } .zen-forms .empty + label { display: block; } .zen-forms .input:focus + label { display: none; } @media (min-width: 1024px) { .zen-forms .input:focus + label { display: block; background: #000; font-size: .8em; padding: 0 .5em; line-height: 2; border-radius: 3px; margin: 5px 15px 0 0; right: 100%; width: auto; white-space: nowrap; color: #fff; opacity: .5; } .zen-forms .input:focus + label:after { content: ''; top: 50%; left: 100%; width: 0px; height: 0px; margin-top: -4px; position: absolute; border-style: solid; border-width: 4px 0 4px 4px; border-color: transparent transparent transparent #000000; } } .zen-forms :focus { outline-color: transparent; outline-style: none; } /** * Dark theme */ .zen-forms { background: #151a1c; } .zen-forms .zen-forms-close-button, .zen-forms .zen-forms-theme-switch { color: #768991; } .zen-forms .zen-forms-close-button:hover, .zen-forms .zen-forms-theme-switch:hover { color: #707071; } .zen-forms label { color: #415056; } .zen-forms .input { color: #768991; } .zen-forms .custom-select { background-color: #151a1c; border: 2px solid #0f1314; } .zen-forms .custom-select a { color: #768991; } .zen-forms .custom-select.is-open a:hover { background-color: #181e20; } .zen-forms .custom-select.is-open .selected { background-color: #0f1314; } /** * Light theme */ .zen-forms.light-theme { background: #fefefe; } .zen-forms.light-theme .zen-forms-close-button, .zen-forms.light-theme .zen-forms-theme-switch { color: #707071; } .zen-forms.light-theme .zen-forms-close-button:hover, .zen-forms.light-theme .zen-forms-theme-switch:hover { color: #768991; } .zen-forms.light-theme label { color: #959697; } .zen-forms.light-theme .input { color: #707071; } .zen-forms.light-theme .custom-select { background-color: #fefefe; border: 2px solid #e5e5e5; } .zen-forms.light-theme .custom-select a { color: #707071; } .zen-forms.light-theme .custom-select.is-open a:hover { background-color: #f5f5f5; } .zen-forms.light-theme .custom-select.is-open .selected { background-color: #e5e5e5; } skins/larry/zen-form.js
New file @@ -0,0 +1,392 @@ /** Zen Forms 1.0.3 | MIT License | git.io/zen-form */ (function ($) { $.fn.zenForm = function (settings) { settings = $.extend({ trigger: '.go-zen', theme: 'dark' }, settings); /** * Helper functions */ var Utils = { /** * (Un)Wrap body content to hide overflow */ bodyWrap: function () { var $body = $('body'), $wrap = $body.children('.zen-forms-body-wrap'); if ($wrap.length) { $wrap.children().unwrap(); } else { $body.wrapInner('<div class="zen-forms-body-wrap"/>'); } }, // bodyWrap /** * Watch inputs and add "empty" class if needed */ watchEmpty: function () { App.Environment.find('input, textarea, select').each(function () { $(this).on('change', function () { $(this)[$(this).val() ? 'removeClass' : 'addClass']('empty'); }).trigger('change'); }); }, /** * Custom styled selects */ customSelect: function ($select, $customSelect) { var $selected; $customSelect.on('click', function (event) { event.stopPropagation(); $selected = $customSelect.find('.selected'); $customSelect.toggleClass('is-open'); if ($customSelect.hasClass('is-open')) { $customSelect.scrollTop( $selected.position().top - $selected.outerHeight() ); } }).find('a').on('click', function () { $(this).addClass('selected').siblings().removeClass('selected'); $select.val($(this).data('value')); }); }, // customSelect /** * Hide any elements(mostly selects) when clicked outside them */ manageSelects: function () { $(document).on('click', function () { $('.is-open').removeClass('is-open'); }); }, // manageSelects /** * Hide any elements(mostly selects) when clicked outside them */ focusFirst: function () { var $first = App.Environment.find('input').first(); // we need to re-set value to remove focus selection $first.focus().val($first.val()); } // focusFirst }, // Utils /** * Core functionality */ App = { /** * Orginal form element */ Form: null, /** * Wrapper element */ Environment: null, /** * Functions to create and manipulate environment */ env: { /** * Object where elements created with App.env.addObject are appended */ wrapper: null, create: function () { // Callback: zf-initialize App.Form.trigger('zf-initialize'); Utils.bodyWrap(); App.Environment = $('<div>', { class: 'zen-forms' + (settings.theme == 'dark' ? '' : ' light-theme') }).hide().appendTo('body').fadeIn(200); // ESC to exit. Thanks @ktmud $('body').on('keydown', function (event) { if (event.which == 27) App.env.destroy($elements); }); return App.Environment; }, // create /** * Update orginal inputs with new values and destroy Environment */ destroy: function ($elements) { // Callback: zf-destroy App.Form.trigger('zf-destroy', App.Environment); $('body').off('keydown'); // Update orginal inputs with new values $elements.each(function (i) { var $el = $('#zen-forms-input' + i); if ($el.length) { $(this).val($el.val()); } }); Utils.bodyWrap(); // Hide and remove Environment App.Environment.fadeOut(200, function () { App.env.wrapper = null; App.Environment.remove(); }); // Callback: zf-destroyed App.Form.trigger('zf-destroyed'); }, // destroy /** * Append inputs, textareas to Environment */ add: function ($elements) { var $el, $label, value, id, ID, label; $elements.each(function (i) { App.env.wrapper = App.env.createObject('div', { class: 'zen-forms-input-wrap' }).appendTo(App.Environment); $el = $(this); value = $el.val(); id = $el.attr('id'); ID = 'zen-forms-input' + i; label = $el.data('label') || $("label[for=" + id + "]").text() || $el.attr('placeholder') || ''; // Exclude specified elements if ($.inArray( $el.attr('type'), ['checkbox', 'radio', 'submit']) == -1) { if ($el.is('input') ) App.env.addInput($el, ID, value); else if ($el.is('select') ) App.env.addSelect($el, ID); else App.env.addTextarea($el, ID, value); $label = App.env.addObject('label', { for: ID, text: label }); if ($el.is('select') ) $label.prependTo(App.env.wrapper); } }); // Callback: zf-initialized App.Form.trigger('zf-initialized', App.Environment); }, // add addInput: function ($input, ID, value) { return App.env.addObject('input', { id: ID, value: value, class: 'input', type: $input.attr('type') }); }, // addInput addTextarea: function ($textarea, ID, value) { return App.env.addObject('textarea', { id: ID, text: value, rows: 5, class: 'input' }); }, // addTextarea addSelect: function ($orginalSelect, ID) { var $select = App.env.addObject('select', { id: ID, class: 'select' }), $options = $orginalSelect.find('option'), $customSelect = App.env.addObject('div', { class: 'custom-select-wrap', html: '<div class="custom-select"></div>' }).children(); $select.append($options.clone()); $.each($options, function (i, option) { App.env.createObject('a', { href: '#', html: '<span>' + $(option).text() + '</span>' , 'data-value': $(option).attr('value'), class: $(option).prop('selected') ? 'selected' : '' }).appendTo($customSelect); }); $select.val($orginalSelect.val()); Utils.customSelect($select, $customSelect); return $customSelect; }, // addSelect /** * Wrapper for creating jQuery objects */ createObject: function (type, params, fn, fnMethod) { return $('<'+type+'>', params).on(fnMethod || 'click', fn); }, // createObject /** * Wrapper for adding jQuery objects to wrapper */ addObject: function (type, params, fn, fnMethod) { return App.env.createObject(type, params, fn, fnMethod).appendTo(App.env.wrapper || App.Environment); }, // addObject switchTheme: function () { App.Environment.toggleClass('light-theme'); } // switchTheme }, // env zen: function ($elements) { // Create environment App.env.create(); // Add wrapper div for close and theme buttons App.env.wrapper = App.env.createObject('div', { class: 'zen-forms-header' }).appendTo(App.Environment); // Add close button App.env.addObject('a', { class: 'zen-forms-close-button', html: '<i class="zen-icon zen-icon--close"></i> Exit Zen Mode' }, function () { App.env.destroy($elements); }); // Add theme switch button App.env.addObject('a', { class: 'zen-forms-theme-switch', html: '<i class="zen-icon zen-icon--theme"></i> Switch theme' }, function () { App.env.switchTheme(); }); // Add inputs and textareas from form App.env.add($elements); // Additional select functionality Utils.manageSelects(); // Select first input Utils.focusFirst(); // Add .empty class for empty inputs Utils.watchEmpty(); } // zen }; // App App.Form = $(this); var $elements = App.Form.is('form') ? App.Form.find('input, textarea, select') : App.Form; $(settings.trigger).on('click', function (event) { event.preventDefault(); App.zen($elements); }); // Command: destroy App.Form.on('destroy', function () { App.env.destroy($elements); }); // Command: init App.Form.on('init', function () { App.zen($elements); }); return this; }; })(jQuery);