From 1423b88839e84d4d293d8623a31d99d4477ac25b Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Thu, 30 Oct 2014 01:08:53 +0400 Subject: [PATCH] Added knockoutjs components Added material design checkbox component Added lang changing animation --- dev/App/Abstract.js | 19 +- dev/App/User.js | 5 + dev/Common/Utils.js | 20 +- dev/Component/AbstracCheckbox.js | 66 ++++ dev/Component/AbstracRadio.js | 65 ++++ dev/{Components => Component}/Abstract.js | 0 .../AbstractInput.js | 3 +- dev/Component/Checkbox.js | 29 ++ dev/{Components => Component}/Input.js | 20 +- dev/Component/MaterialDesign/Checkbox.js | 66 ++++ dev/Component/Radio.js | 29 ++ dev/{Components => Component}/SaveTrigger.js | 4 +- dev/{Components => Component}/Select.js | 5 +- dev/{Components => Component}/TextArea.js | 4 +- dev/Components/Checkbox.js | 38 -- dev/Settings/Admin/Login.js | 2 + dev/Styles/@Main.less | 1 + dev/Styles/Components.less | 145 ++++++++ gulpfile.js | 4 +- rainloop/v/0.0.0/app/src/RainLoop/Actions.php | 3 + rainloop/v/0.0.0/app/src/RainLoop/Service.php | 1 + .../0.0.0/app/src/RainLoop/ServiceActions.php | 5 +- rainloop/v/0.0.0/app/templates/Index.html | 29 +- .../Admin/AdminSettingsPluginProperty.html | 15 +- .../templates/Views/Admin/PopupsDomain.html | 71 ++-- .../templates/Views/Components/Checkbox.html | 12 +- .../Components/CheckboxMaterialDesign.html | 11 + .../app/templates/Views/Components/Input.html | 16 +- .../app/templates/Views/Components/Radio.html | 7 + .../Views/Components/SaveTrigger.html | 4 +- .../templates/Views/Components/Select.html | 5 +- .../templates/Views/Components/TextArea.html | 14 +- .../0.0.0/app/templates/Views/User/Login.html | 4 +- .../Views/User/PopupsAdvancedSearch.html | 36 +- .../Views/User/PopupsComposeOpenPgp.html | 24 +- .../templates/Views/User/PopupsFilter.html | 24 +- .../Views/User/SettingsContacts.html | 24 +- .../templates/Views/User/SettingsGeneral.html | 47 +-- .../Views/User/SettingsIdentities.html | 57 ++- .../Views/User/SettingsIdentity.html | 44 ++- .../templates/Views/User/SettingsPane.html | 2 +- .../Views/User/SettingsSecurity.html | 12 +- vendors/jquery-letterfx/LICENSE.md | 21 ++ vendors/jquery-letterfx/README.md | 11 + vendors/jquery-letterfx/jquery-letterfx.css | 111 ++++++ vendors/jquery-letterfx/jquery-letterfx.js | 332 ++++++++++++++++++ .../jquery-letterfx/jquery-letterfx.min.css | 1 + .../jquery-letterfx/jquery-letterfx.min.js | 1 + 48 files changed, 1213 insertions(+), 256 deletions(-) create mode 100644 dev/Component/AbstracCheckbox.js create mode 100644 dev/Component/AbstracRadio.js rename dev/{Components => Component}/Abstract.js (100%) rename dev/{Components => Component}/AbstractInput.js (94%) create mode 100644 dev/Component/Checkbox.js rename dev/{Components => Component}/Input.js (51%) create mode 100644 dev/Component/MaterialDesign/Checkbox.js create mode 100644 dev/Component/Radio.js rename dev/{Components => Component}/SaveTrigger.js (96%) rename dev/{Components => Component}/Select.js (82%) rename dev/{Components => Component}/TextArea.js (77%) delete mode 100644 dev/Components/Checkbox.js create mode 100644 dev/Styles/Components.less create mode 100644 rainloop/v/0.0.0/app/templates/Views/Components/CheckboxMaterialDesign.html create mode 100644 rainloop/v/0.0.0/app/templates/Views/Components/Radio.html create mode 100644 vendors/jquery-letterfx/LICENSE.md create mode 100644 vendors/jquery-letterfx/README.md create mode 100644 vendors/jquery-letterfx/jquery-letterfx.css create mode 100644 vendors/jquery-letterfx/jquery-letterfx.js create mode 100644 vendors/jquery-letterfx/jquery-letterfx.min.css create mode 100644 vendors/jquery-letterfx/jquery-letterfx.min.js diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 89176fdb8..7fcd9d0eb 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -216,11 +216,20 @@ ko = require('ko') ; - ko.components.register('SaveTrigger', require('Components/SaveTrigger')); - ko.components.register('Checkbox', require('Components/Checkbox')); - ko.components.register('Input', require('Components/Input')); - ko.components.register('Select', require('Components/Select')); - ko.components.register('TextArea', require('Components/TextArea')); + ko.components.register('SaveTrigger', require('Component/SaveTrigger')); + ko.components.register('Input', require('Component/Input')); + ko.components.register('Select', require('Component/Select')); + ko.components.register('TextArea', require('Component/TextArea')); + ko.components.register('Radio', require('Component/Radio')); + + if (Settings.settingsGet('MaterialDesign')) + { + ko.components.register('Checkbox', require('Component/MaterialDesign/Checkbox')); + } + else + { + ko.components.register('Checkbox', require('Component/Checkbox')); + } Utils.initOnStartOrLangChange(function () { Utils.initNotificationLanguage(); diff --git a/dev/App/User.js b/dev/App/User.js index aa461fc9e..e4845ff2a 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -1291,6 +1291,8 @@ { this.setTitle(Utils.i18n('TITLES/LOADING')); +//require.ensure([], function() { // require code splitting + self.folders(_.bind(function (bValue) { kn.hideLoading(); @@ -1425,6 +1427,9 @@ } }, self)); + +//}); // require code splitting + } else { diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index ba1bb635f..95dab0cf0 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -403,8 +403,9 @@ /** * @param {Object} oElement + * @param {boolean=} bAnimate = false */ - Utils.i18nToNode = function (oElement) + Utils.i18nToNode = function (oElement, bAnimate) { _.defer(function () { $('.i18n', oElement).each(function () { @@ -439,6 +440,13 @@ } } }); + + if (bAnimate) + { + $('.i18n-animation.i18n', oElement).letterfx({ + 'fx': 'fall fade', 'backwards': false, 'timing': 50, 'fx_duration': '50ms', 'letter_end': 'restore', 'element_end': 'restore' + }); + } }); }; @@ -448,7 +456,7 @@ { Globals.oI18N = window['rainloopI18N'] || {}; - Utils.i18nToNode(Globals.$doc); + Utils.i18nToNode(Globals.$doc, true); Globals.langChangeTrigger(!Globals.langChangeTrigger()); } @@ -1618,8 +1626,9 @@ .replace(/>/g, '>').replace(/') .replace(/[\s]*~~~\/blockquote~~~/g, '') -// .replace(/[\-_~]{10,}/g, '
') - .replace(/\n/g, '
'); + .replace(/ /g, ' ') + .replace(/\n/g, '
') + ; return bFindEmailAndLinks ? Utils.findEmailAndLinks(sPlain) : sPlain; }; @@ -2015,7 +2024,8 @@ }; /** - * @param {Object} oObject + * @param {mixed} mPropOrValue + * @param {mixed} mValue */ Utils.disposeOne = function (mPropOrValue, mValue) { diff --git a/dev/Component/AbstracCheckbox.js b/dev/Component/AbstracCheckbox.js new file mode 100644 index 000000000..9be52944b --- /dev/null +++ b/dev/Component/AbstracCheckbox.js @@ -0,0 +1,66 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + Utils = require('Common/Utils'), + + AbstractComponent = require('Component/Abstract') + ; + + /** + * @constructor + * + * @param {Object} oParams + * + * @extends AbstractComponent + */ + function AbstracCheckbox(oParams) { + + AbstractComponent.call(this); + + this.value = oParams.value; + if (Utils.isUnd(this.value) || !this.value.subscribe) + { + this.value = ko.observable(Utils.isUnd(this.value) ? false : !!this.value); + } + + this.enable = oParams.enable; + if (Utils.isUnd(this.enable) || !this.enable.subscribe) + { + this.enable = ko.observable(Utils.isUnd(this.enable) ? true : !!this.enable); + } + + this.disable = oParams.disable; + if (Utils.isUnd(this.disable) || !this.disable.subscribe) + { + this.disable = ko.observable(Utils.isUnd(this.disable) ? false : !!this.disable); + } + + this.label = oParams.label || ''; + this.inline = Utils.isUnd(oParams.inline) ? false : oParams.inline; + + this.readOnly = Utils.isUnd(oParams.readOnly) ? false : !!oParams.readOnly; + this.inverted = Utils.isUnd(oParams.inverted) ? false : !!oParams.inverted; + + this.labeled = !Utils.isUnd(oParams.label); + }; + + _.extend(AbstracCheckbox.prototype, AbstractComponent.prototype); + + AbstracCheckbox.prototype.click = function() { + if (!this.readOnly && this.enable() && !this.disable()) + { + this.value(!this.value()); + } + }; + + AbstracCheckbox.componentExportHelper = AbstractComponent.componentExportHelper; + + module.exports = AbstracCheckbox; + +}()); diff --git a/dev/Component/AbstracRadio.js b/dev/Component/AbstracRadio.js new file mode 100644 index 000000000..4a7e3430d --- /dev/null +++ b/dev/Component/AbstracRadio.js @@ -0,0 +1,65 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + Utils = require('Common/Utils'), + + AbstractComponent = require('Component/Abstract') + ; + + /** + * @constructor + * + * @param {Object} oParams + * + * @extends AbstractComponent + */ + function AbstracRadio(oParams) { + + AbstractComponent.call(this); + + this.values = ko.observableArray([]); + + this.value = oParams.value; + if (Utils.isUnd(this.value) || !this.value.subscribe) + { + this.value = ko.observable(''); + } + + this.inline = Utils.isUnd(oParams.inline) ? false : oParams.inline; + this.readOnly = Utils.isUnd(oParams.readOnly) ? false : !!oParams.readOnly; + + if (oParams.values) + { + var aValues = _.map(oParams.values, function (sLabel, sValue) { + return { + 'label': sLabel, + 'value': sValue + }; + }); + + this.values(aValues); + } + + this.click = _.bind(this.click, this); + }; + + AbstracRadio.prototype.click = function(oValue) { + if (!this.readOnly && oValue) + { + this.value(oValue.value); + } + }; + + _.extend(AbstracRadio.prototype, AbstractComponent.prototype); + + AbstracRadio.componentExportHelper = AbstractComponent.componentExportHelper; + + module.exports = AbstracRadio; + +}()); diff --git a/dev/Components/Abstract.js b/dev/Component/Abstract.js similarity index 100% rename from dev/Components/Abstract.js rename to dev/Component/Abstract.js diff --git a/dev/Components/AbstractInput.js b/dev/Component/AbstractInput.js similarity index 94% rename from dev/Components/AbstractInput.js rename to dev/Component/AbstractInput.js index a261db0d8..54c3b6f66 100644 --- a/dev/Components/AbstractInput.js +++ b/dev/Component/AbstractInput.js @@ -10,7 +10,7 @@ Enums = require('Common/Enums'), Utils = require('Common/Utils'), - AbstractComponent = require('Components/Abstract') + AbstractComponent = require('Component/Abstract') ; /** @@ -29,6 +29,7 @@ this.label = oParams.label || ''; this.enable = oParams.enable || true; this.trigger = oParams.trigger && oParams.trigger.subscribe ? oParams.trigger : null; + this.placeholder = oParams.placeholder || ''; this.labeled = !Utils.isUnd(oParams.label); this.triggered = !Utils.isUnd(oParams.trigger) && !!this.trigger; diff --git a/dev/Component/Checkbox.js b/dev/Component/Checkbox.js new file mode 100644 index 000000000..3846450c6 --- /dev/null +++ b/dev/Component/Checkbox.js @@ -0,0 +1,29 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + + AbstracCheckbox = require('Component/AbstracCheckbox') + ; + + /** + * @constructor + * + * @param {Object} oParams + * + * @extends AbstracCheckbox + */ + function CheckboxComponent(oParams) { + + AbstracCheckbox.call(this, oParams); + }; + + _.extend(CheckboxComponent.prototype, AbstracCheckbox.prototype); + + module.exports = AbstracCheckbox.componentExportHelper( + CheckboxComponent, 'CheckboxComponent'); + +}()); diff --git a/dev/Components/Input.js b/dev/Component/Input.js similarity index 51% rename from dev/Components/Input.js rename to dev/Component/Input.js index c2aeda53f..bfc58263a 100644 --- a/dev/Components/Input.js +++ b/dev/Component/Input.js @@ -9,7 +9,7 @@ Enums = require('Common/Enums'), Utils = require('Common/Utils'), - AbstractInput = require('Components/AbstractInput') + AbstractInput = require('Component/AbstractInput') ; /** @@ -22,24 +22,6 @@ function InputComponent(oParams) { AbstractInput.call(this, oParams); - - this.placeholder = oParams.placeholder || '' - }; - - InputComponent.prototype.setTriggerState = function (nValue) - { - switch (Utils.pInt(nValue)) - { - case Enums.SaveSettingsStep.TrueResult: - this.classForTrigger('success'); - break; - case Enums.SaveSettingsStep.FalseResult: - this.classForTrigger('error'); - break; - default: - this.classForTrigger(''); - break; - } }; _.extend(InputComponent.prototype, AbstractInput.prototype); diff --git a/dev/Component/MaterialDesign/Checkbox.js b/dev/Component/MaterialDesign/Checkbox.js new file mode 100644 index 000000000..486e0f858 --- /dev/null +++ b/dev/Component/MaterialDesign/Checkbox.js @@ -0,0 +1,66 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + AbstracCheckbox = require('Component/AbstracCheckbox') + ; + + /** + * @constructor + * + * @param {Object} oParams + * + * @extends AbstracCheckbox + */ + function CheckboxMaterialDesignComponent(oParams) { + + AbstracCheckbox.call(this, oParams); + + this.animationBox = ko.observable(false).extend({'falseTimeout': 200}); + this.animationCheckmark = ko.observable(false).extend({'falseTimeout': 200}); + + this.animationBoxSetTrue = _.bind(this.animationBoxSetTrue, this); + this.animationCheckmarkSetTrue = _.bind(this.animationCheckmarkSetTrue, this); + + this.disposable.push( + this.value.subscribe(function (bValue) { + this.triggerAnimation(bValue); + }, this) + ); + }; + + _.extend(CheckboxMaterialDesignComponent.prototype, AbstracCheckbox.prototype); + + CheckboxMaterialDesignComponent.prototype.animationBoxSetTrue = function() + { + this.animationBox(true); + }; + + CheckboxMaterialDesignComponent.prototype.animationCheckmarkSetTrue = function() + { + this.animationCheckmark(true); + }; + + CheckboxMaterialDesignComponent.prototype.triggerAnimation = function(bBox) + { + if (bBox) + { + this.animationBoxSetTrue(); + _.delay(this.animationCheckmarkSetTrue, 200); + } + else + { + this.animationCheckmarkSetTrue(); + _.delay(this.animationBoxSetTrue, 200); + } + }; + + module.exports = AbstracCheckbox.componentExportHelper( + CheckboxMaterialDesignComponent, 'CheckboxMaterialDesignComponent'); + +}()); diff --git a/dev/Component/Radio.js b/dev/Component/Radio.js new file mode 100644 index 000000000..178bbc0d4 --- /dev/null +++ b/dev/Component/Radio.js @@ -0,0 +1,29 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + + AbstracRadio = require('Component/AbstracRadio') + ; + + /** + * @constructor + * + * @param {Object} oParams + * + * @extends AbstracRadio + */ + function RadioComponent(oParams) { + + AbstracRadio.call(this, oParams); + }; + + _.extend(RadioComponent.prototype, AbstracRadio.prototype); + + module.exports = AbstracRadio.componentExportHelper( + RadioComponent, 'RadioComponent'); + +}()); diff --git a/dev/Components/SaveTrigger.js b/dev/Component/SaveTrigger.js similarity index 96% rename from dev/Components/SaveTrigger.js rename to dev/Component/SaveTrigger.js index a49777020..1cba4f40a 100644 --- a/dev/Components/SaveTrigger.js +++ b/dev/Component/SaveTrigger.js @@ -9,7 +9,7 @@ Enums = require('Common/Enums'), Utils = require('Common/Utils'), - AbstractComponent = require('Components/Abstract') + AbstractComponent = require('Component/Abstract') ; /** @@ -36,7 +36,7 @@ { this.element.css('vertical-align', oParams.verticalAlign); } - + this.setState(this.value()); this.disposable.push( diff --git a/dev/Components/Select.js b/dev/Component/Select.js similarity index 82% rename from dev/Components/Select.js rename to dev/Component/Select.js index 3f1750422..d9162b166 100644 --- a/dev/Components/Select.js +++ b/dev/Component/Select.js @@ -6,10 +6,9 @@ var _ = require('_'), - Enums = require('Common/Enums'), Utils = require('Common/Utils'), - AbstractInput = require('Components/AbstractInput') + AbstractInput = require('Component/AbstractInput') ; /** @@ -27,6 +26,8 @@ this.optionsText = oParams.optionsText || null; this.optionsValue = oParams.optionsValue || null; + + this.defautOptionsAfterRender = Utils.defautOptionsAfterRender; }; _.extend(SelectComponent.prototype, AbstractInput.prototype); diff --git a/dev/Components/TextArea.js b/dev/Component/TextArea.js similarity index 77% rename from dev/Components/TextArea.js rename to dev/Component/TextArea.js index 8c21565a3..11cd1b0ba 100644 --- a/dev/Components/TextArea.js +++ b/dev/Component/TextArea.js @@ -6,10 +6,9 @@ var _ = require('_'), - Enums = require('Common/Enums'), Utils = require('Common/Utils'), - AbstractInput = require('Components/AbstractInput') + AbstractInput = require('Component/AbstractInput') ; /** @@ -24,6 +23,7 @@ AbstractInput.call(this, oParams); this.rows = oParams.rows || 5; + this.spellcheck = Utils.isUnd(oParams.spellcheck) ? false : !!oParams.spellcheck; }; _.extend(TextAreaComponent.prototype, AbstractInput.prototype); diff --git a/dev/Components/Checkbox.js b/dev/Components/Checkbox.js deleted file mode 100644 index 1693a9948..000000000 --- a/dev/Components/Checkbox.js +++ /dev/null @@ -1,38 +0,0 @@ - -(function () { - - 'use strict'; - - var - _ = require('_'), - ko = require('ko'), - - AbstractComponent = require('Components/Abstract') - ; - - /** - * @constructor - * - * @param {Object} oParams - * - * @extends AbstractComponent - */ - function CheckboxComponent(oParams) { - - AbstractComponent.call(this); - - this.value = oParams.value || ko.observable(false); - this.label = oParams.label || ''; - this.inline = oParams.inline; - }; - - _.extend(CheckboxComponent.prototype, AbstractComponent.prototype); - - CheckboxComponent.prototype.toggle = function() { - this.value(!this.value()); - }; - - module.exports = AbstractComponent.componentExportHelper( - CheckboxComponent, 'CheckboxComponent'); - -}()); diff --git a/dev/Settings/Admin/Login.js b/dev/Settings/Admin/Login.js index f3e6a0503..1cb499396 100644 --- a/dev/Settings/Admin/Login.js +++ b/dev/Settings/Admin/Login.js @@ -26,6 +26,8 @@ this.allowLanguagesOnLogin = Data.allowLanguagesOnLogin; this.defaultDomainTrigger = ko.observable(Enums.SaveSettingsStep.Idle); + + this.dummy = ko.observable(false); } LoginAdminSetting.prototype.onBuild = function () diff --git a/dev/Styles/@Main.less b/dev/Styles/@Main.less index c802056c7..ea0fd5ecb 100644 --- a/dev/Styles/@Main.less +++ b/dev/Styles/@Main.less @@ -52,6 +52,7 @@ @import "Main.less"; @import "Layout.less"; @import "Scroll.less"; +@import "Components.less"; @import "SystemDropDown.less"; @import "Login.less"; @import "Ask.less"; diff --git a/dev/Styles/Components.less b/dev/Styles/Components.less new file mode 100644 index 000000000..8846c7e97 --- /dev/null +++ b/dev/Styles/Components.less @@ -0,0 +1,145 @@ + +.checkbox-box-sizes() { + top: 0px; + left: 0px; + width: 18px; + height: 18px; + border: solid 2px #999; + + transform: rotate(0deg); +} + +.checkbox-mark-sizes() { +/* top: -4px; + left: 6px; + width: 10px; + height: 21px;*/ + + top: -1px; + left: 5px; + width: 10px; + height: 18px; + + border-right-width: 2px; + border-bottom-width: 2px; + + transform: rotate(45deg); +} + +.checkbox-result-sizes() { + top: 13px; + left: 5px; + width: 4px; + height: 4px; + + transform: rotate(45deg); +} + +.e-component { + + &.e-checkbox { + + cursor: pointer; + + &.disabled { + cursor: default; + color: #999; + } + } + + &.e-radio { + + cursor: pointer; + + &.disabled { + cursor: default; + color: #999; + } + } +} + +.e-component.material-design { + + &.e-checkbox { + + .sub-checkbox-container { + display: inline-block; + position: relative; + transform: translateZ(0); + width: 18px; + height: 18px; + vertical-align: bottom; + margin-bottom: 3px; + margin-top: 1px; + } + + .sub-label { + padding-left: 8px; + } + + .sub-checkbox { + position: absolute; + box-sizing: border-box; + + .checkbox-box-sizes(); + } + + &.disabled .sub-checkbox { + cursor: default; + color: #aaa; + } + + .sub-checkbox.checked { + border-top: none; + border-left: none; + border-color: #0F9D58; + + .checkbox-mark-sizes(); + } + + // animation + .sub-checkbox.checked { + &.box { + border: solid 2px; + animation: box-shrink 140ms ease-out forwards; + } + &.checkmark { + border-left: none; + border-top: none; + animation: checkmark-expand 140ms ease-out forwards; + } + } + + .sub-checkbox.unchecked { + &.box { + animation: box-expand 140ms ease-out forwards; + } + &.checkmark { + border-left: none; + border-top: none; + transform: rotate(45deg); + animation: checkmark-shrink 140ms ease-out forwards; + } + } + } +} + +@keyframes box-shrink { + 0% { .checkbox-box-sizes(); } + 100% { .checkbox-result-sizes(); } +} + +@keyframes checkmark-expand { + 0% { .checkbox-result-sizes(); } + 100% { .checkbox-mark-sizes(); } +} + +@keyframes checkmark-shrink { + 0% { .checkbox-mark-sizes(); } + 100% { .checkbox-result-sizes(); } +} + +@keyframes box-expand { + 0% { .checkbox-result-sizes(); } + 100% { .checkbox-box-sizes(); } +} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 839131846..fd9caba16 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -95,6 +95,7 @@ cfg.paths.css = { 'vendors/jquery-nanoscroller/nanoscroller.css', 'vendors/jquery-magnific-popup/magnific-popup.css', 'vendors/jquery-magnific-popup/magnific-popup-animations.css', + 'vendors/jquery-letterfx/jquery-letterfx.min.css', 'vendors/simple-pace/styles.css', 'vendors/inputosaurus/inputosaurus.css', 'vendors/flags/flags-fixed.css', @@ -146,6 +147,7 @@ cfg.paths.js = { 'vendors/jquery-lazyload/jquery.lazyload.min.js', 'vendors/jquery-nanoscroller/jquery.nanoscroller-0.7.min.js', 'vendors/jquery-wakeup/jquery.wakeup.min.js', + 'vendors/jquery-letterfx/jquery-letterfx.min.js', 'vendors/inputosaurus/inputosaurus.min.js', 'vendors/moment/min/moment.min.js ', 'vendors/routes/signals.min.js', @@ -194,7 +196,7 @@ gulp.task('css:main', ['less:main'], function() { return gulp.src(cfg.paths.css.main.src) .pipe(concat(cfg.paths.css.main.name)) .pipe(autoprefixer('last 3 versions', '> 1%', 'ie 9', 'Firefox ESR', 'Opera 12.1')) - .pipe(csscomb()) +// .pipe(csscomb()) // .pipe(csslint()) // .pipe(csslint.reporter()) .pipe(eol('\n', true)) diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php index 5eb1220e8..d731f6a7e 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Actions.php @@ -1020,11 +1020,14 @@ class Actions 'UseImapThread' => (bool) $oConfig->Get('labs', 'use_imap_thread', false), 'UseImapSubscribe' => (bool) $oConfig->Get('labs', 'use_imap_list_subscribe', true), 'AllowAppendMessage' => (bool) $oConfig->Get('labs', 'allow_message_append', false), + 'MaterialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true), 'PremType' => $this->PremType(), 'Capa' => array(), 'Plugins' => array() ); +// $aResult['MaterialDesign'] = false; + if ($aResult['UseRsaEncryption'] && \file_exists(APP_PRIVATE_DATA.'rsa/public') && \file_exists(APP_PRIVATE_DATA.'rsa/private')) { diff --git a/rainloop/v/0.0.0/app/src/RainLoop/Service.php b/rainloop/v/0.0.0/app/src/RainLoop/Service.php index 380a9a5b6..944d7c3de 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/Service.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/Service.php @@ -164,6 +164,7 @@ class Service if (0 === \strlen($sResult)) { +// $aTemplateParameters['{{BaseTemplates}}'] = $this->oServiceActions->compileTemplates($bAdmin, false); $sResult = \strtr(\file_get_contents(APP_VERSION_ROOT_PATH.'app/templates/Index.html'), $aTemplateParameters); $sResult = \RainLoop\Utils::ClearHtmlOutput($sResult); diff --git a/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php index 34d211c1e..6d8a86847 100644 --- a/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/src/RainLoop/ServiceActions.php @@ -1066,10 +1066,11 @@ class ServiceActions /** * @param bool $bAdmin = false + * @param bool $bJsOutput = true * * @return string */ - private function compileTemplates($bAdmin = false) + public function compileTemplates($bAdmin = false, $bJsOutput = true) { $sHtml = \RainLoop\Utils::CompileTemplates(APP_VERSION_ROOT_PATH.'app/templates/Views/Components', $this->oActions, 'Component'). @@ -1077,7 +1078,7 @@ class ServiceActions \RainLoop\Utils::CompileTemplates(APP_VERSION_ROOT_PATH.'app/templates/Views/Common', $this->oActions). $this->oActions->Plugins()->CompileTemplate($bAdmin); - return 'window.rainloopTEMPLATES='.\MailSo\Base\Utils::Php2js(array($sHtml), $this->Logger()).';'; + return $bJsOutput ? 'window.rainloopTEMPLATES='.\MailSo\Base\Utils::Php2js(array($sHtml), $this->Logger()).';' : $sHtml; } /** diff --git a/rainloop/v/0.0.0/app/templates/Index.html b/rainloop/v/0.0.0/app/templates/Index.html index a44e38988..0395264aa 100644 --- a/rainloop/v/0.0.0/app/templates/Index.html +++ b/rainloop/v/0.0.0/app/templates/Index.html @@ -45,20 +45,21 @@
An Error occurred, diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html index c63ce404f..f245ecf6e 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPluginProperty.html @@ -12,18 +12,19 @@ data-bind="value: value, attr: {placeholder: placeholder}" /> - +
- +
diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html index e4e5e0aa8..140f2cb43 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/PopupsDomain.html @@ -48,21 +48,26 @@      - +

- +
+   + (user@domain.com → user)
@@ -95,26 +100,34 @@      - +

- - +
+   + (user@domain.com → user) +
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/Components/Checkbox.html b/rainloop/v/0.0.0/app/templates/Views/Components/Checkbox.html index 48d43c5d6..0172d430e 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Components/Checkbox.html +++ b/rainloop/v/0.0.0/app/templates/Views/Components/Checkbox.html @@ -1,4 +1,10 @@ -
@@ -20,11 +22,13 @@
- +
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsGeneral.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsGeneral.html index 190906b63..24f39a517 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsGeneral.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsGeneral.html @@ -1,7 +1,7 @@
- +
- - +
- +
@@ -102,17 +100,24 @@
- +
- +
+   + + +
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentities.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentities.html index cdb86eca1..0904ffa47 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentities.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentities.html @@ -8,9 +8,16 @@
- -
+
@@ -18,38 +25,52 @@
- -
+
- diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentity.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentity.html index 62fd71d66..b70803104 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentity.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsIdentity.html @@ -8,38 +8,52 @@
- -
+
- diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsPane.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsPane.html index 571a16c72..02da68161 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsPane.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsPane.html @@ -4,7 +4,7 @@    - +
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/SettingsSecurity.html b/rainloop/v/0.0.0/app/templates/Views/User/SettingsSecurity.html index ce1a93e41..11368cfca 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/SettingsSecurity.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/SettingsSecurity.html @@ -5,11 +5,13 @@
- +
diff --git a/vendors/jquery-letterfx/LICENSE.md b/vendors/jquery-letterfx/LICENSE.md new file mode 100644 index 000000000..ea208db2d --- /dev/null +++ b/vendors/jquery-letterfx/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jared Anderson, dba tuxsudo.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendors/jquery-letterfx/README.md b/vendors/jquery-letterfx/README.md new file mode 100644 index 000000000..e720c041f --- /dev/null +++ b/vendors/jquery-letterfx/README.md @@ -0,0 +1,11 @@ +LetterFX +======== + +A jQuery plugin to apply animated, visual effects to letters, words or other text patterns. + +example usage: + + $(element).letterfx({"fx":"fly-right fly-bottom spin"}) + + +[More Info](http://tuxsudo.com/code/project/letterfx) diff --git a/vendors/jquery-letterfx/jquery-letterfx.css b/vendors/jquery-letterfx/jquery-letterfx.css new file mode 100644 index 000000000..eef6cd71d --- /dev/null +++ b/vendors/jquery-letterfx/jquery-letterfx.css @@ -0,0 +1,111 @@ +.letterfx-container{} + +.letterfx{ + display:inline-block; + margin:0; + padding:0; + transition: all 1s; -ms-transition: all 1s; -webkit-transition: all 1s; +} + +/* Spin FX */ +.letterfx-spin-before{ + transform:rotate(3600deg); -ms-transform:rotate(3600deg); -webkit-transform:rotate(3600deg); +} + +.letterfx-spin-after{ + transform:none; +} + +/* Fade FX */ +.letterfx-fade-before{ + opacity: 0; +} + + +/* Grow FX */ +.letterfx-grow-before{ + transform:scale(0,0); -ms-transform:scale(0,0); -webkit-transform:scale(0,0); +} + +.letterfx-grow-after{ + transform:none; +} + + +/* Smear FX */ +.letterfx-smear-before{ + color: transparent; + text-shadow:-1px -1px 25px transparent; +} + +.letterfx-smear-after{ + color:inherit; + text-shadow:0 0 #333; + +} + +/* Fall FX */ +.letterfx-fall-before +{ + visibility: hidden; + transform:scale(3,3); -ms-transform:scale(3,3); -webkit-transform:scale(3,3); +} + +.letterfx-fall-after{ + transform:scale(1,1); -ms-transform:scale(1,1); -webkit-transform:scale(1,1); + text-shadow:0; +} + +/* Swirl FX */ +.letterfx-swirl-before +{ + visibility: hidden; + transform:scale(3,3) rotate(3600deg); -ms-transform:scale(3,3) rotate(3600deg); -webkit-transform:scale(3,3) rotate(3600deg); +} + +.letterfx-swirl-after{ + transform:none + text-shadow:0; +} + + +/* Wave FX */ +.letterfx-wave-container .letterfx +{ + position:relative; +} + +.letterfx-wave-before +{ + bottom:0; +} + +.letterfx-wave-after{ + bottom:15px; +} + + + +/* FLY FX */ +.letterfx-fly-left-container .letterfx, +.letterfx-fly-right-container .letterfx, +.letterfx-fly-top-container .letterfx, +.letterfx-fly-bottom-container .letterfx +{ + position:relative; +} + +.letterfx-fly-left-before{ left:-50em; } +.letterfx-fly-left-after{ left:0; } + +.letterfx-fly-right-before{ right:-50em; } +.letterfx-fly-right-after{ right:0; } + +.letterfx-fly-top-before{ top:-20em; } +.letterfx-fly-top-after{ top:0; } + +.letterfx-fly-bottom-before{ bottom:-20em; } +.letterfx-fly-bottom-after{ bottom:0; } + + + diff --git a/vendors/jquery-letterfx/jquery-letterfx.js b/vendors/jquery-letterfx/jquery-letterfx.js new file mode 100644 index 000000000..0370399af --- /dev/null +++ b/vendors/jquery-letterfx/jquery-letterfx.js @@ -0,0 +1,332 @@ +(function ($) { + + "use strict"; + + var LetterFx = function (element, options) { + + this.options = $.extend({}, $.fn.letterfx.defaults, options); + + this.num_completed_fx = 0; + + this.is_done = false; + + this.monitor_timer = null; + + this.killswitch=null; + + this.$element = $(element); + + if(this.options.restore) + this.original_html = this.$element.html(); + + this.init(); + + } + + LetterFx.prototype.init = function(){ + + this.new_html = this.$element.text().replace( this.options.pattern, this.options.replacement ); + + this.$element.addClass(this.options.css.element.base).addClass( this.options.css.element.before ); + + this.$element.html( this.new_html ); + + this.$letters = this.$element.find(this.options.selector); + + this.$letters + .css('transition-duration', this.options.fx_duration) + .addClass(this.options.css.letters.base) + .addClass(this.options.css.letters.before); + + this.bindLetterFxEnd(); + + this.num_letters = this.$letters.length; + + + this.fx(); + + return this; + } + + LetterFx.prototype.bindLetterFxEnd = function(){ + var options = this.options; + var lfx = this; + this.$letters.bind("transitionend", function(){ + options.onLetterComplete( $(this), lfx.$element, lfx ); + lfx.notifyFXEnd( ); + + switch(options.letter_end){ + case "destroy": + $(this).remove(); + break; + + case "rewind": + lfx.applyLetterFx( $(this), options.timing, options.css.letters.after, options.css.letters.before ); + break; + + case "stay": + break; + + // restore + default: + $(this).replaceWith( $(this).text() ); + } + + }); + return lfx; + } + + LetterFx.prototype.terminate=function(){ + this.is_done = true; + this.options.onElementComplete(this.$element, this); + + clearTimeout(this.killswitch); + + switch(this.options.element_end){ + case "destroy": + this.$element.remove(); + break; + + case "stay": + break; + + // restore + default: + this.$element.html( this.original_html ); + this.$element.removeClass( this.options.css.element.base ).removeClass(this.options.css.element.after); + break; + } + + } + + LetterFx.prototype.notifyFXEnd=function(){ + clearTimeout(this.monitor_timer); + this.num_completed_fx++; + + var lfx = this; + this.monitor_timer = setTimeout( + function(){ + if(lfx.num_completed_fx % lfx.num_letters===0){ + lfx.terminate(); + } + }, + Math.max(this.options.timing+10, 50) + ); + + return this; + } + + LetterFx.prototype.startKillWatch = function(){ + var fx_duration = this.options.fx_duration.match(/\d+s/) ? parseInt(this.options.fx_duration) : 1; + var time = Math.ceil(1.5 * this.num_letters * this.options.timing * fx_duration ); + var lfx = this; + this.killswitch = window.setTimeout(function(){ + if(!lfx.isDone()){ + lfx.terminate() + } + }, time) + } + + LetterFx.prototype.fx = function(){ + var lfx = this; + this.startKillWatch(); + this.$element.removeClass(this.options.css.element.before).addClass(this.options.css.element.after); + var $letters = this.options.sort(this.$letters); + var options = this.options; + + + + $letters.each( + function(i, letter){ + lfx.applyLetterFx( $(letter), (i+1)*options.timing, options.css.letters.before, options.css.letters.after); + } + ); + return this; + } + + LetterFx.prototype.applyLetterFx = function($letter, timing, css_before, css_after){ + var options = this.options; + + window.setTimeout( + function(){ + $letter.removeClass(css_before).addClass(css_after); + }, + timing + ); + return this + } + + LetterFx.prototype.isDone = function(){ + return this.is_done; + } + + + + + var LetterFxConfig = function(conf){ + this.config = $.extend({}, $.fn.letterfx.defaults, conf); + this.buildCss(this.config.backwards); + + // check & change to word pattern + if(this.config.words) + this.config.pattern=/(\S+)/g; + + + } + + + LetterFxConfig.prototype.buildCss = function(flip){ + var options = this.config; + var before = flip ? 'after' : 'before'; + var after = flip ? 'before' : 'after'; + + + var css = { element:{}, letters:{} }; + + css.element.base=options.element_class+ "-container " + options.fx.replace(/(\S+)/g, options.element_class + "-$1-container"); + css.element[before]=options.fx.replace(/(\S+)/g, options.element_class + "-$1-before-container"); + css.element[after]=options.fx.replace(/(\S+)/g, options.element_class + "-$1-after-container"); + + css.letters.base = options.element_class; + css.letters[before] = options.fx.replace(/(\S+)/g, options.element_class + "-$1-before") + css.letters[after] = options.fx.replace(/(\S+)/g, options.element_class + "-$1-after") + + this.config = $.extend( options, {'css':css} ); + } + + LetterFxConfig.prototype.getConfig = function(){ + return this.config; + } + + LetterFxConfig.parse=function(config){ + return (new LetterFxConfig( config )).getConfig(); + } + + + + +$.fn.letterfx=function(config){ + + config = LetterFxConfig.parse( config ); + + return $(this).each(function(){ + var $element = $(this); + if (! $element.data('letterfx-obj') || $element.data('letterfx-obj').isDone() ){ + $element.data('letterfx-obj', new LetterFx( $element, config ) ); + } + }); + +}; + +$.fn.letterfx.sort={ + random:function(array){ + var currentIndex = array.length, temporaryValue, randomIndex; + + // While there remain elements to shuffle... + while (0 !== currentIndex) { + + // Pick a remaining element... + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; + }, + reverse:function($array){ + return $array.toArray().reverse(); + } +} + +$.fn.letterfx.patterns={ + letters:/(\S)/gi +} + +// Plugin Configurables +$.fn.letterfx.defaults = { + + // Default fx + fx:'spin fly-top', + + // defaults to selecting all characters + pattern:/(\S)/gi, + + // switch from letter FX to word FX. + word:false, + + // fx, like fly or fade, can happen in (eg fade-in) or out (eg fade-out). + // The default is in; change backwards to true to reverse the order of the effects. + backwards:false, + + // defaults to injecting spans, can be any inline element + replacement:"$1", + + //selector -- should match replacement above + selector:'span', + + // letter fx start sequentially: letters start their fx one after another. + // this sets time between the letters + timing:50, + + //duration of each fx + // options the same as css property for transition-duration ("1ms", "1s", etc) + fx_duration:"1s", + + + // stabile dimensions + // stabilize:true, + + // sort callback for custom sorting elements + sort:function($letters){ return $letters; }, + + + // Callback when letter is done animating. Runs after each letter + onLetterComplete:function($letter, $element, LetterFXObj){}, + + // Runs after all are done. + onElementComplete:function($element, LetterFXObj){}, + + + // what to do when a letter completes its animation. + // options include + // restore: return letter to plain text (default) + // destroy: get rid of the letter. + // stay: leave it as is. + // rewind: reverse the animation + letter_end:"restore", + + + // what to do when the entire element has completed all its letter effects + // options include: + // restore: return element to its pre-fx state (default) + // stay: do nothing + // destroy: get rid of the element... FOREVER + element_end:"restore", + + // Restore container element back to original state. + // options: true, false, "element" ("element" waits until all letters complete fx before restoring) + restore:true, + + // destroy element/letters after fx, useful on {out:true} fx + // options: true, false, "letters" ("letters" destroys each letter after fx, but elements original content may be restored after all letters complete fx) + destroy:false, + + // default class for injected elements + element_class:'letterfx', + + // placeholder values that are calculated on launch + css:{ + element:{ base:'', before:'', after:''}, + letters:{ base:'', before:'', after:''} + } + + + +}; + +}(jQuery)); + diff --git a/vendors/jquery-letterfx/jquery-letterfx.min.css b/vendors/jquery-letterfx/jquery-letterfx.min.css new file mode 100644 index 000000000..2a0f6877f --- /dev/null +++ b/vendors/jquery-letterfx/jquery-letterfx.min.css @@ -0,0 +1 @@ +.letterfx{display:inline-block;margin:0;padding:0;transition:all 1s;-ms-transition:all 1s;-webkit-transition:all 1s}.letterfx-spin-before{transform:rotate(3600deg);-ms-transform:rotate(3600deg);-webkit-transform:rotate(3600deg)}.letterfx-spin-after{transform:none}.letterfx-fade-before{opacity:0}.letterfx-grow-before{transform:scale(0,0);-ms-transform:scale(0,0);-webkit-transform:scale(0,0)}.letterfx-grow-after{transform:none}.letterfx-smear-before{color:transparent;text-shadow:-1px -1px 25px transparent}.letterfx-smear-after{color:inherit;text-shadow:0 0 #333}.letterfx-fall-before{visibility:hidden;transform:scale(3,3);-ms-transform:scale(3,3);-webkit-transform:scale(3,3)}.letterfx-fall-after{transform:scale(1,1);-ms-transform:scale(1,1);-webkit-transform:scale(1,1);text-shadow:0}.letterfx-swirl-before{visibility:hidden;transform:scale(3,3) rotate(3600deg);-ms-transform:scale(3,3) rotate(3600deg);-webkit-transform:scale(3,3) rotate(3600deg)}.letterfx-swirl-after{transform:none text-shadow:0}.letterfx-wave-container .letterfx{position:relative}.letterfx-wave-before{bottom:0}.letterfx-wave-after{bottom:15px}.letterfx-fly-left-container .letterfx,.letterfx-fly-right-container .letterfx,.letterfx-fly-top-container .letterfx,.letterfx-fly-bottom-container .letterfx{position:relative}.letterfx-fly-left-before{left:-50em}.letterfx-fly-left-after{left:0}.letterfx-fly-right-before{right:-50em}.letterfx-fly-right-after{right:0}.letterfx-fly-top-before{top:-20em}.letterfx-fly-top-after{top:0}.letterfx-fly-bottom-before{bottom:-20em}.letterfx-fly-bottom-after{bottom:0} \ No newline at end of file diff --git a/vendors/jquery-letterfx/jquery-letterfx.min.js b/vendors/jquery-letterfx/jquery-letterfx.min.js new file mode 100644 index 000000000..8aeb86b18 --- /dev/null +++ b/vendors/jquery-letterfx/jquery-letterfx.min.js @@ -0,0 +1 @@ +(function($){"use strict";var LetterFx=function(element,options){this.options=$.extend({},$.fn.letterfx.defaults,options);this.num_completed_fx=0;this.is_done=false;this.monitor_timer=null;this.killswitch=null;this.$element=$(element);if(this.options.restore)this.original_html=this.$element.html();this.init()};LetterFx.prototype.init=function(){this.new_html=this.$element.text().replace(this.options.pattern,this.options.replacement);this.$element.addClass(this.options.css.element.base).addClass(this.options.css.element.before);this.$element.html(this.new_html);this.$letters=this.$element.find(this.options.selector);this.$letters.css("transition-duration",this.options.fx_duration).addClass(this.options.css.letters.base).addClass(this.options.css.letters.before);this.bindLetterFxEnd();this.num_letters=this.$letters.length;this.fx();return this};LetterFx.prototype.bindLetterFxEnd=function(){var options=this.options;var lfx=this;this.$letters.bind("transitionend",function(){options.onLetterComplete($(this),lfx.$element,lfx);lfx.notifyFXEnd();switch(options.letter_end){case"destroy":$(this).remove();break;case"rewind":lfx.applyLetterFx($(this),options.timing,options.css.letters.after,options.css.letters.before);break;case"stay":break;default:$(this).replaceWith($(this).text())}});return lfx};LetterFx.prototype.terminate=function(){this.is_done=true;this.options.onElementComplete(this.$element,this);clearTimeout(this.killswitch);switch(this.options.element_end){case"destroy":this.$element.remove();break;case"stay":break;default:this.$element.html(this.original_html);this.$element.removeClass(this.options.css.element.base).removeClass(this.options.css.element.after);break}};LetterFx.prototype.notifyFXEnd=function(){clearTimeout(this.monitor_timer);this.num_completed_fx++;var lfx=this;this.monitor_timer=setTimeout(function(){if(lfx.num_completed_fx%lfx.num_letters===0){lfx.terminate()}},Math.max(this.options.timing+10,50));return this};LetterFx.prototype.startKillWatch=function(){var fx_duration=this.options.fx_duration.match(/\d+s/)?parseInt(this.options.fx_duration):1;var time=Math.ceil(1.5*this.num_letters*this.options.timing*fx_duration);var lfx=this;this.killswitch=window.setTimeout(function(){if(!lfx.isDone()){lfx.terminate()}},time)};LetterFx.prototype.fx=function(){var lfx=this;this.startKillWatch();this.$element.removeClass(this.options.css.element.before).addClass(this.options.css.element.after);var $letters=this.options.sort(this.$letters);var options=this.options;$letters.each(function(i,letter){lfx.applyLetterFx($(letter),(i+1)*options.timing,options.css.letters.before,options.css.letters.after)});return this};LetterFx.prototype.applyLetterFx=function($letter,timing,css_before,css_after){var options=this.options;window.setTimeout(function(){$letter.removeClass(css_before).addClass(css_after)},timing);return this};LetterFx.prototype.isDone=function(){return this.is_done};var LetterFxConfig=function(conf){this.config=$.extend({},$.fn.letterfx.defaults,conf);this.buildCss(this.config.backwards);if(this.config.words)this.config.pattern=/(\S+)/g};LetterFxConfig.prototype.buildCss=function(flip){var options=this.config;var before=flip?"after":"before";var after=flip?"before":"after";var css={element:{},letters:{}};css.element.base=options.element_class+"-container "+options.fx.replace(/(\S+)/g,options.element_class+"-$1-container");css.element[before]=options.fx.replace(/(\S+)/g,options.element_class+"-$1-before-container");css.element[after]=options.fx.replace(/(\S+)/g,options.element_class+"-$1-after-container");css.letters.base=options.element_class;css.letters[before]=options.fx.replace(/(\S+)/g,options.element_class+"-$1-before");css.letters[after]=options.fx.replace(/(\S+)/g,options.element_class+"-$1-after");this.config=$.extend(options,{css:css})};LetterFxConfig.prototype.getConfig=function(){return this.config};LetterFxConfig.parse=function(config){return new LetterFxConfig(config).getConfig()};$.fn.letterfx=function(config){config=LetterFxConfig.parse(config);return $(this).each(function(){var $element=$(this);if(!$element.data("letterfx-obj")||$element.data("letterfx-obj").isDone()){$element.data("letterfx-obj",new LetterFx($element,config))}})};$.fn.letterfx.sort={random:function(array){var currentIndex=array.length,temporaryValue,randomIndex;while(0!==currentIndex){randomIndex=Math.floor(Math.random()*currentIndex);currentIndex-=1;temporaryValue=array[currentIndex];array[currentIndex]=array[randomIndex];array[randomIndex]=temporaryValue}return array},reverse:function($array){return $array.toArray().reverse()}};$.fn.letterfx.patterns={letters:/(\S)/gi};$.fn.letterfx.defaults={fx:"spin fly-top",pattern:/(\S)/gi,word:false,backwards:false,replacement:"$1",selector:"span",timing:50,fx_duration:"1s",sort:function($letters){return $letters},onLetterComplete:function($letter,$element,LetterFXObj){},onElementComplete:function($element,LetterFXObj){},letter_end:"restore",element_end:"restore",restore:true,destroy:false,element_class:"letterfx",css:{element:{base:"",before:"",after:""},letters:{base:"",before:"",after:""}}}})(jQuery); \ No newline at end of file