Added knockoutjs components

Added material design checkbox component
Added lang changing animation
This commit is contained in:
RainLoop Team 2014-10-30 01:08:53 +04:00
parent c2b7632c13
commit 1423b88839
48 changed files with 1213 additions and 256 deletions

View file

@ -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();

View file

@ -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
{

View file

@ -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, '&gt;').replace(/</g, '&lt;')
.replace(/~~~blockquote~~~[\s]*/g, '<blockquote>')
.replace(/[\s]*~~~\/blockquote~~~/g, '</blockquote>')
// .replace(/[\-_~]{10,}/g, '<hr />')
.replace(/\n/g, '<br />');
.replace(/ /g, '&nbsp;')
.replace(/\n/g, '<br />')
;
return bFindEmailAndLinks ? Utils.findEmailAndLinks(sPlain) : sPlain;
};
@ -2015,7 +2024,8 @@
};
/**
* @param {Object} oObject
* @param {mixed} mPropOrValue
* @param {mixed} mValue
*/
Utils.disposeOne = function (mPropOrValue, mValue)
{

View file

@ -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;
}());

View file

@ -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;
}());

View file

@ -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;

29
dev/Component/Checkbox.js Normal file
View file

@ -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');
}());

View file

@ -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);

View file

@ -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');
}());

29
dev/Component/Radio.js Normal file
View file

@ -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');
}());

View file

@ -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(

View file

@ -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);

View file

@ -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);

View file

@ -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');
}());

View file

@ -26,6 +26,8 @@
this.allowLanguagesOnLogin = Data.allowLanguagesOnLogin;
this.defaultDomainTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.dummy = ko.observable(false);
}
LoginAdminSetting.prototype.onBuild = function ()

View file

@ -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";

145
dev/Styles/Components.less Normal file
View file

@ -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(); }
}

View file

@ -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))

View file

@ -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'))
{

View file

@ -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);

View file

@ -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;
}
/**

View file

@ -45,20 +45,21 @@
</div>
</div>
<script data-cfasync="false">
var
oE = window.document.getElementById('rl-loading'),
oElDesc = window.document.getElementById('rl-loading-desc')
;
if (oElDesc && window.rainloopAppData['LoadingDescriptionEsc']) {
oElDesc.innerHTML = window.rainloopAppData['LoadingDescriptionEsc'];
}
if (oE && oE.style) {
oE.style.opacity = 0;
window.setTimeout(function () {
oE.style.opacity = 1;
}, 300);
}
(function (window) {
var
oE = window.document.getElementById('rl-loading'),
oElDesc = window.document.getElementById('rl-loading-desc')
;
if (oElDesc && window.rainloopAppData['LoadingDescriptionEsc']) {
oElDesc.innerHTML = window.rainloopAppData['LoadingDescriptionEsc'];
}
if (oE && oE.style) {
oE.style.opacity = 0;
window.setTimeout(function () {
oE.style.opacity = 1;
}, 300);
}
}(window));
</script>
<div id="rl-loading-error" class="thm-loading">
An Error occurred,

View file

@ -12,18 +12,19 @@
data-bind="value: value, attr: {placeholder: placeholder}" />
<!-- /ko -->
<!-- ko if: 2 === Type -->
<textarea autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: value, attr: {placeholder: placeholder}"></textarea>
<div data-bind="component: {
name: 'TextArea',
params: { value: value, placeholder: placeholder }
}"></div>
<!-- /ko -->
<!-- ko if: 4 === Type -->
<select data-bind="options: Default, value: value"></select>
<!-- /ko -->
<!-- ko if: 5 === Type -->
<label data-bind="click: function () { value(!value()); }">
<i data-bind="css: value() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span data-bind="text: Label"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: { value: value, label: Label }
}"></div>
<!-- /ko -->
<span class="help-block" style="color: #ccc" data-bind="text: Desc, visible: '' !== Desc"></span>
</div>

View file

@ -48,21 +48,26 @@
<option value="2">STARTTLS</option>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;
<label class="inline" data-bind="visible: '1' === imapSecure() && false, click: function () { imapVerifySsl(!imapVerifySsl()); }">
<i data-bind="css: imapVerifySsl() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Verify ssl certificate
</label>
<div data-bind="visible: '1' === imapSecure() && false, component: {
name: 'Checkbox',
params: {
label: 'Verify ssl certificate',
value: imapVerifySsl
}
}"></div>
</div>
</div>
<br />
<label data-bind="click: function () { imapShortLogin(!imapShortLogin()); }">
<i data-bind="css: imapShortLogin() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Use short login form
&nbsp;&nbsp;
<span style="color: #aaa">(user@domain.com &rarr; user)</span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'Use short login form',
value: imapShortLogin,
inline: true
}
}"></div>
&nbsp;
<span style="color: #aaa">(user@domain.com &rarr; user)</span>
</div>
<div class="span5" data-bind="css: { 'testing-done': testingDone, 'testing-error': testingSmtpError }">
<div class="legend smtp-header">
@ -95,26 +100,34 @@
<option value="2">STARTTLS</option>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;
<label class="inline" data-bind="visible: '1' === smtpSecure() && false, click: function () { smtpVerifySsl(!smtpVerifySsl()); }">
<i data-bind="css: smtpVerifySsl() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Verify ssl certificate
</label>
<div data-bind="visible: '1' === smtpSecure() && false, component: {
name: 'Checkbox',
params: {
label: 'Verify ssl certificate',
value: smtpVerifySsl
}
}"></div>
</div>
</div>
<br />
<label data-bind="click: function () { smtpShortLogin(!smtpShortLogin()); }">
<i data-bind="css: smtpShortLogin() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Use short login form
&nbsp;&nbsp;
<span style="color: #aaa">(user@domain.com &rarr; user)</span>
</label>
<label data-bind="click: function () { smtpAuth(!smtpAuth()); }">
<i data-bind="css: smtpAuth() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Use authentication
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'Use short login form',
value: smtpShortLogin,
inline: true
}
}"></div>
&nbsp;
<span style="color: #aaa">(user@domain.com &rarr; user)</span>
<br />
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'Use authentication',
value: smtpAuth
}
}"></div>
</div>
<div class="span10">
<div class="legend white-list-header">

View file

@ -1,4 +1,10 @@
<label data-bind="click: toggle, css: {'inline': inline}">
<i data-bind="css: value() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;<span class="i18n" data-bind="attr: {'data-i18n-text': label}"></span>
<label class="e-component e-checkbox" data-bind="click: click, css: {'inline': inline, 'disabled': disable() || !enable() }">
<i data-bind="css: value() ?
(inverted ? 'icon-checkbox-unchecked' : 'icon-checkbox-checked') :
(inverted ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked')
"></i>
<!-- ko if: labeled -->
&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n-text': label}"></span>
<!-- /ko -->
</label>

View file

@ -0,0 +1,11 @@
<label class="e-component e-checkbox material-design" data-bind="click: click, css: {'inline': inline, 'disabled': disable() || !enable() }">
<div class="sub-checkbox-container">
<div class="sub-checkbox" data-bind="css: {'checked': (value() && !inverted) || (!value() && inverted),
'unchecked': (!value() && !inverted) || (value() && inverted),
'box': animationBox, 'checkmark': animationCheckmark}"></div>
</div>
<!-- ko if: labeled -->
&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n-text': label}"></span>
<!-- /ko -->
</label>

View file

@ -1,14 +1,14 @@
<input class="i18n" type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder=""
data-bind="value: value, attr: {'data-i18n-placeholder': placeholder}, enable: enable, css: className" />
<!-- ko if: labeled -->
&nbsp;
<span class="i18n" data-bind="attr: {'data-i18n-text': label}"></span>
&nbsp;
&nbsp;
<span class="i18n" data-bind="attr: {'data-i18n-text': label}"></span>
&nbsp;
<!-- /ko -->
<!-- ko if: triggered -->
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger }
}"></div>
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger }
}"></div>
<!-- /ko -->

View file

@ -0,0 +1,7 @@
<div class="e-component e-radio" data-bind="foreach: values">
<label data-bind="click: $parent.click, css: {'inline': $parent.inline}">
<i data-bind="css: $parent.value() === value ? 'icon-radio-checked' : 'icon-radio-unchecked'"></i>
&nbsp;&nbsp;
<span class="sub-label i18n" data-bind="attr: {'data-i18n-text': label}"></span>
</label>
</div>

View file

@ -1,3 +1,3 @@
<div class="settings-saved-trigger">
<i class="icon-spinner animated"></i><i class="icon-remove error"></i><i class="icon-ok success"></i>
<div class="e-component settings-saved-trigger">
<i class="icon-spinner animated"></i><i class="icon-remove error"></i><i class="icon-ok success"></i>
</div>

View file

@ -1,11 +1,12 @@
<select data-bind="options: options, value: value, optionsText: optionsText, optionsValue: optionsValue, css: className"></select>
<select data-bind="options: options, value: value, optionsText: optionsText, optionsValue: optionsValue,
css: className, optionsAfterRender: defautOptionsAfterRender"></select>
<!-- ko if: labeled -->
&nbsp;
<span class="i18n" data-bind="attr: {'data-i18n-text': label}"></span>
&nbsp;
<!-- /ko -->
<!-- ko if: triggered -->
&nbsp;&nbsp;
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger }

View file

@ -1,9 +1,9 @@
<textarea class="i18n" rows="5" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="true"
data-bind="value: value, enable: enable, attr: { rows: rows }, css: className"></textarea>
<textarea class="i18n" rows="5" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder=""
data-bind="value: value, enable: enable, attr: { placeholder: placeholder, rows: rows, spellcheck: spellcheck ? 'true' : 'false' }, css: className"></textarea>
<!-- ko if: triggered -->
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger, verticalAlign: 'top' }
}"></div>
&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: trigger, verticalAlign: 'top' }
}"></div>
<!-- /ko -->

View file

@ -58,14 +58,14 @@
<div class="control-group">
<button type="submit" class="btn btn-large span4 buttonLogin" data-bind="command: submitCommand">
<i class="icon-spinner animated" data-bind="visible: submitRequest"></i>
<span class="i18n" data-i18n-text="LOGIN/BUTTON_SIGN_IN" data-bind="visible: !submitRequest()"></span>
<span class="i18n i18n-animation" data-i18n-text="LOGIN/BUTTON_SIGN_IN" data-bind="visible: !submitRequest()"></span>
</button>
</div>
<div class="control-group">
<label class="pull-left signMeLabel" data-bind="click: function () { signMe(!signMe()); }, visible: signMeVisibility">
<i data-bind="css: signMe() ? 'checkboxSignMe icon-checkbox-checked' : 'checkboxSignMe icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="LOGIN/LABEL_SIGN_ME"></span>
<span class="i18n i18n-animation" data-i18n-text="LOGIN/LABEL_SIGN_ME"></span>
</label>
<div class="pull-right">
<div class="btn-group dropdown" data-bind="registrateBootstrapDropdown: true, visible: socialLoginEnabled() || allowLanguagesOnLogin()">

View file

@ -69,21 +69,27 @@
<label class="control-label">
</label>
<div class="controls">
<label data-bind="click: function () { unseen(!unseen()); }">
<i data-bind="css: unseen() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_UNSEEN"></span>
</label>
<label data-bind="click: function () { starred(!starred()); }">
<i data-bind="css: starred() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_FLAGGED"></span>
</label>
<label data-bind="click: function () { hasAttachment(!hasAttachment()); }">
<i data-bind="css: hasAttachment() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SEARCH/LABEL_ADV_HAS_ATTACHMENT"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_UNSEEN',
value: unseen
}
}"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_FLAGGED',
value: starred
}
}"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SEARCH/LABEL_ADV_HAS_ATTACHMENT',
value: hasAttachment
}
}"></div>
</div>
</div>
</div>

View file

@ -15,16 +15,20 @@
<br />
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { sign(!sign()); }">
<i data-bind="css: sign() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="POPUPS_COMPOSE_OPEN_PGP/LABEL_SIGN"></span>
</label>
<label data-bind="click: function () { encrypt(!encrypt()); }">
<i data-bind="css: encrypt() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="POPUPS_COMPOSE_OPEN_PGP/LABEL_ENCRYPT"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'POPUPS_COMPOSE_OPEN_PGP/LABEL_SIGN',
value: sign
}
}"></div>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'POPUPS_COMPOSE_OPEN_PGP/LABEL_ENCRYPT',
value: encrypt
}
}"></div>
</div>
</div>
<div class="control-group">

View file

@ -41,17 +41,21 @@
<hr />
<div data-bind="template: {'name': actionTemplate()}"></div>
<label data-bind="visible: actionMarkAsReadVisiblity, click: function () { actionMarkAsRead(!actionMarkAsRead()); }">
<i data-bind="css: actionMarkAsRead() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span>Mark as read</span>
</label>
<div data-bind="visible: actionMarkAsReadVisiblity, component: {
name: 'Checkbox',
params: {
label: 'Mark as read',
value: actionMarkAsRead
}
}"></div>
<label data-bind="click: function () { actionSkipOtherFilters(!actionSkipOtherFilters()); }">
<i data-bind="css: actionSkipOtherFilters() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span>Skip other filters</span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'Skip other filters',
value: actionSkipOtherFilters
actionSkipOtherFilters
}"></div>
</div>
</div>
</div>

View file

@ -5,11 +5,13 @@
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { contactsAutosave(!contactsAutosave()); }">
<i data-bind="css: contactsAutosave() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_AUTOSAVE"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_CONTACTS/LABEL_CONTACTS_AUTOSAVE',
value: contactsAutosave
}
}"></div>
</div>
</div>
</div>
@ -20,11 +22,13 @@
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { enableContactsSync(!enableContactsSync()); }">
<i data-bind="css: enableContactsSync() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_ENABLE"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_CONTACTS/LABEL_CONTACTS_SYNC_ENABLE',
value: enableContactsSync
}
}"></div>
</div>
</div>
<div class="control-group">

View file

@ -1,7 +1,7 @@
<div class="b-settings-general g-ui-user-select-none">
<div class="form-horizontal">
<div class="legend">
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LEGEND_GENERAL"></span>
<span class="i18n i18n-animation" data-i18n-text="SETTINGS_GENERAL/LEGEND_GENERAL"></span>
</div>
<div class="control-group" data-bind="visible: allowLanguagesOnSettings">
<label class="control-label">
@ -26,24 +26,22 @@
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LABEL_EDITOR"></span>
</label>
<div class="controls">
<label for="richTextEditorTypeID1">
<input type="radio" id="richTextEditorTypeID1" name="richTextEditorType" value="Html" data-bind="checked: editorDefaultType" style="position: absolute; left: -10000px" />
<i data-bind="css: 'Html' === editorDefaultType() ? ' icon-radio-checked' : 'icon-radio-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LABEL_EDITOR_HTML_AS_DEFAULT"></span>
</label>
<label for="richTextEditorTypeID2">
<input type="radio" id="richTextEditorTypeID2" name="richTextEditorType" value="Plain" data-bind="checked: editorDefaultType" style="position: absolute; left: -10000px" />
<i data-bind="css: 'Plain' === editorDefaultType() ? ' icon-radio-checked' : 'icon-radio-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LABEL_EDITOR_PLAIN_AS_DEFAULT"></span>
</label>
<div data-bind="component: {
name: 'Radio',
params: {
value: editorDefaultType,
values: {
'Html': 'SETTINGS_GENERAL/LABEL_EDITOR_HTML_AS_DEFAULT',
'Plain': 'SETTINGS_GENERAL/LABEL_EDITOR_PLAIN_AS_DEFAULT'
}
}
}"></div>
</div>
</div>
</div>
<div class="form-horizontal">
<div class="legend">
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LABEL_VIEW_OPTIONS"></span>
<span class="i18n i18n-animation" data-i18n-text="SETTINGS_GENERAL/LABEL_VIEW_OPTIONS"></span>
</div>
<div class="control-group">
<div class="controls">
@ -102,17 +100,24 @@
</div>
<div class="form-horizontal" data-bind="visible: isDesktopNotificationsSupported()">
<div class="legend">
<span class="i18n" data-i18n-text="SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION"></span>
<span class="i18n i18n-animation" data-i18n-text="SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION"></span>
</div>
<div class="control-group">
<div class="controls">
<div data-bind="visible: isDesktopNotificationsSupported">
<label data-bind="css: { 'denied-by-browser': isDesktopNotificationsDenied }, click: function () { if (!isDesktopNotificationsDenied()) { useDesktopNotifications(!useDesktopNotifications()); }}">
<i data-bind="css: useDesktopNotifications() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n notification-desc" data-i18n-text="SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION_DESC"></span>
<span class="i18n notification-desc-denied" data-i18n-text="SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION_DESC_DENIED"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION_DESC',
value: useDesktopNotifications,
disable: isDesktopNotificationsDenied,
inline: true
}
}"></div>
&nbsp;
<span data-bind="visible: isDesktopNotificationsDenied">
<span class="i18n" style="color: #999" data-i18n-text="SETTINGS_GENERAL/LABEL_CHROME_NOTIFICATION_DESC_DENIED"></span>
</span>
</div>
</div>
</div>

View file

@ -8,9 +8,16 @@
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_DEFAULT"></span>
</label>
<div class="controls">
<select data-bind="options: identitiesOptions, value: defaultIdentityID,
optionsText: 'name', optionsValue: 'id', optionsAfterRender: $root.defautOptionsAfterRender, saveTrigger: defaultIdentityIDTrigger"></select>
<div data-bind="saveTrigger: defaultIdentityIDTrigger"></div>
<div data-bind="component: {
name: 'Select',
params: {
options: identitiesOptions,
value: defaultIdentityID,
optionsText: 'name',
optionsValue: 'id',
trigger: defaultIdentityIDTrigger
}
}"></div>
</div>
</div>
<div class="control-group g-ui-user-select-none">
@ -18,38 +25,52 @@
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_DISPLAY_NAME"></span>
</label>
<div class="controls">
<input type="text" class="input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: displayName, saveTrigger: displayNameTrigger" />
<div data-bind="saveTrigger: displayNameTrigger"></div>
<div data-bind="component: {
name: 'Input',
params: {
value: displayName,
size: 4,
trigger: displayNameTrigger
}
}"></div>
</div>
</div>
<div class="control-group g-ui-user-select-none" style="display: none">
<!-- <div class="control-group g-ui-user-select-none" style="display: none">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_REPLY_TO"></span>
</label>
<div class="controls">
<input type="text" class="input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: replyTo" />
<div data-bind="component: {
name: 'Input',
params: {
value: replyTo,
size: 4
}
}"></div>
</div>
</div>
</div>-->
<div class="control-group">
<label class="control-label g-ui-user-select-none">
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_SIGNATURE"></span>
</label>
<div class="controls">
<div class="e-signature-place" data-bind="initDom: signatureDom"></div>
<div style="vertical-align: top;" data-bind="saveTrigger: signatureTrigger"></div>
&nbsp;&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: signatureTrigger, verticalAlign: 'top' }
}"></div>
</div>
</div>
<div class="control-group g-ui-user-select-none">
<div class="controls">
<label data-bind="click: function () { signatureToAll(!signatureToAll()); }">
<i data-bind="css: signatureToAll() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_ADD_SIGNATURE_TO_ALL"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_IDENTITIES/LABEL_ADD_SIGNATURE_TO_ALL',
value: signatureToAll
}
}"></div>
</div>
</div>
</div>

View file

@ -8,38 +8,52 @@
<span class="i18n" data-i18n-text="SETTINGS_IDENTITY/LABEL_DISPLAY_NAME"></span>
</label>
<div class="controls">
<input type="text" class="input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: displayName, saveTrigger: displayNameTrigger" />
<div data-bind="saveTrigger: displayNameTrigger"></div>
<div data-bind="component: {
name: 'Input',
params: {
value: displayName,
size: 4,
trigger: displayNameTrigger
}
}"></div>
</div>
</div>
<div class="control-group g-ui-user-select-none" style="display: none">
<!-- <div class="control-group g-ui-user-select-none" style="display: none">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_IDENTITY/LABEL_REPLY_TO"></span>
</label>
<div class="controls">
<input type="text" class="input-xlarge"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: replyTo" />
<div data-bind="component: {
name: 'Input',
params: {
value: replyTo,
size: 4
}
}"></div>
</div>
</div>
</div>-->
<div class="control-group">
<label class="control-label g-ui-user-select-none">
<span class="i18n" data-i18n-text="SETTINGS_IDENTITY/LABEL_SIGNATURE"></span>
</label>
<div class="controls">
<div class="e-signature-place" data-bind="initDom: signatureDom"></div>
<div style="vertical-align: top;" data-bind="saveTrigger: signatureTrigger"></div>
&nbsp;&nbsp;
<div data-bind="component: {
name: 'SaveTrigger',
params: { value: signatureTrigger, verticalAlign: 'top' }
}"></div>
</div>
</div>
<div class="control-group g-ui-user-select-none">
<div class="controls">
<label data-bind="click: function () { signatureToAll(!signatureToAll()); }">
<i data-bind="css: signatureToAll() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_IDENTITIES/LABEL_ADD_SIGNATURE_TO_ALL"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_IDENTITIES/LABEL_ADD_SIGNATURE_TO_ALL',
value: signatureToAll
}
}"></div>
</div>
</div>
</div>

View file

@ -4,7 +4,7 @@
<a class="btn button-back" data-bind="click: backToMailBoxClick">
<i class="icon-left"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_LABELS/BUTTON_BACK"></span>
<span class="i18n i18n-animation" data-i18n-text="SETTINGS_LABELS/BUTTON_BACK"></span>
</a>
</div>
</div>

View file

@ -5,11 +5,13 @@
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { viewEnable(!viewEnable()); }">
<i data-bind="css: viewEnable() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_ENABLE_TWO_FACTOR"></span>
</label>
<div data-bind="component: {
name: 'Checkbox',
params: {
label: 'SETTINGS_SECURITY/LABEL_ENABLE_TWO_FACTOR',
value: viewEnable
}
}"></div>
</div>
</div>
<div class="control-group">

21
vendors/jquery-letterfx/LICENSE.md vendored Normal file
View file

@ -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.

11
vendors/jquery-letterfx/README.md vendored Normal file
View file

@ -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)

View file

@ -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; }

View file

@ -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:"<span>$1</span>",
//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));

View file

@ -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}

File diff suppressed because one or more lines are too long