PreRelease 1.8.3

This commit is contained in:
RainLoop Team 2015-04-13 22:45:09 +04:00
parent 39566292af
commit 0ea982671b
40 changed files with 712 additions and 370 deletions

View file

@ -73,7 +73,7 @@
}, 50)); }, 50));
// TODO // DEBUG
// Events.sub({ // Events.sub({
// 'window.resize': function () { // 'window.resize': function () {
// window.console.log('window.resize'); // window.console.log('window.resize');

View file

@ -211,7 +211,7 @@
/** /**
* @param {Function} fResultFunc * @param {Function} fResultFunc
* @returns {boolean} * @return {boolean}
*/ */
AppUser.prototype.contactsSync = function (fResultFunc) AppUser.prototype.contactsSync = function (fResultFunc)
{ {

View file

@ -20,58 +20,90 @@
{ {
var self = this; var self = this;
this.obj = this.createNewObject(); // this.userMedia = window.navigator.getUserMedia || window.navigator.webkitGetUserMedia ||
this.objForNotification = this.createNewObject(); // window.navigator.mozGetUserMedia || window.navigator.msGetUserMedia;
//
// this.audioContext = window.AudioContext || window.webkitAudioContext;
// if (!this.audioContext || !window.Float32Array)
// {
// this.audioContext = null;
// this.userMedia = null;
// }
this.supported = !Globals.bMobileDevice && !Globals.bSafari && this.player = this.createNewObject();
this.obj && '' !== this.obj.canPlayType('audio/mpeg');
if (this.obj && this.supported) this.supported = !Globals.bMobileDevice && !Globals.bSafari && !!this.player && !!this.player.play;
if (this.supported && this.player.canPlayType)
{ {
this.objForNotification.src = Links.sound('new-mail.mp3'); this.supportedMp3 = '' !== this.player.canPlayType('audio/mpeg;').replace(/no/, '');
this.supportedWav = '' !== this.player.canPlayType('audio/wav; codecs="1"').replace(/no/, '');
this.supportedOgg = '' !== this.player.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, '');
this.supportedNotification = this.supported && this.supportedMp3;
}
$(this.obj).on('ended error', function () { if (!this.player || (!this.supportedMp3 && !this.supportedOgg && !this.supportedWav))
{
this.supported = false;
this.supportedMp3 = false;
this.supportedOgg = false;
this.supportedWav = false;
this.supportedNotification = false;
}
if (this.supported)
{
$(this.player).on('ended error', function () {
self.stop(); self.stop();
}); });
Events.sub('audio.api.stop', function () { Events.sub('audio.api.stop', function () {
self.stop(); self.stop();
}); });
Events.sub('audio.api.play', function (sUrl, sName) {
self.playMp3(sUrl, sName);
});
} }
} }
Audio.prototype.obj = null; Audio.prototype.player = null;
Audio.prototype.objForNotification = null; Audio.prototype.notificator = null;
Audio.prototype.supported = false; Audio.prototype.supported = false;
Audio.prototype.supportedMp3 = false;
Audio.prototype.supportedOgg = false;
Audio.prototype.supportedWav = false;
Audio.prototype.supportedNotification = false;
// Audio.prototype.record = function ()
// {
// this.getUserMedia({audio:true}, function () {
// window.console.log(arguments);
// }, function(oError) {
// window.console.log(arguments);
// });
// };
Audio.prototype.createNewObject = function () Audio.prototype.createNewObject = function ()
{ {
var obj = window.Audio ? new window.Audio() : null; var player = window.Audio ? new window.Audio() : null;
if (obj && obj.canPlayType) if (player && player.canPlayType && player.pause && player.play)
{ {
obj.preload = 'none'; player.preload = 'none';
obj.loop = false; player.loop = false;
obj.autoplay = false; player.autoplay = false;
obj.muted = false; player.muted = false;
} }
return obj; return player;
}; };
Audio.prototype.paused = function () Audio.prototype.paused = function ()
{ {
return this.supported ? !!this.obj.paused : true; return this.supported ? !!this.player.paused : true;
}; };
Audio.prototype.stop = function () Audio.prototype.stop = function ()
{ {
if (this.supported && this.obj.pause) if (this.supported && this.player.pause)
{ {
this.obj.pause(); this.player.pause();
} }
Events.pub('audio.stop'); Events.pub('audio.stop');
@ -79,15 +111,11 @@
Audio.prototype.pause = Audio.prototype.stop; Audio.prototype.pause = Audio.prototype.stop;
Audio.prototype.playMp3 = function (sUrl, sName) Audio.prototype.clearName = function (sName, sExt)
{ {
if (this.supported && this.obj.play) sExt = sExt || '';
{
this.obj.src = sUrl;
this.obj.play();
sName = Utils.isUnd(sName) ? '' : Utils.trim(sName); sName = Utils.isUnd(sName) ? '' : Utils.trim(sName);
if ('.mp3' === sName.toLowerCase().substr(-4)) if (sExt && '.' + sExt === sName.toLowerCase().substr((sExt.length + 1) * -1))
{ {
sName = Utils.trim(sName.substr(0, sName.length - 4)); sName = Utils.trim(sName.substr(0, sName.length - 4));
} }
@ -97,15 +125,59 @@
sName = 'audio'; sName = 'audio';
} }
Events.pub('audio.start', [sName]); return sName;
};
Audio.prototype.playMp3 = function (sUrl, sName)
{
if (this.supported && this.supportedMp3)
{
this.player.src = sUrl;
this.player.play();
Events.pub('audio.start', [this.clearName(sName, 'mp3'), 'mp3']);
}
};
Audio.prototype.playOgg = function (sUrl, sName)
{
if (this.supported && this.supportedOgg)
{
this.player.src = sUrl;
this.player.play();
sName = this.clearName(sName, 'oga');
sName = this.clearName(sName, 'ogg');
Events.pub('audio.start', [sName, 'ogg']);
}
};
Audio.prototype.playWav = function (sUrl, sName)
{
if (this.supported && this.supportedWav)
{
this.player.src = sUrl;
this.player.play();
Events.pub('audio.start', [this.clearName(sName, 'wav'), 'wav']);
} }
}; };
Audio.prototype.playNotification = function () Audio.prototype.playNotification = function ()
{ {
if (this.supported && this.objForNotification.play) if (this.supported && this.supportedMp3)
{ {
this.objForNotification.play(); if (!this.notificator)
{
this.notificator = this.createNewObject();
this.notificator.src = Links.sound('new-mail.mp3');
}
if (this.notificator && this.notificator.play)
{
this.notificator.play();
}
} }
}; };

View file

@ -5,6 +5,26 @@
var Enums = {}; var Enums = {};
/**
* @enum {string}
*/
Enums.FileType = {
'Unknown': 'unknown',
'Text': 'text',
'Html': 'html',
'Code': 'code',
'Eml': 'eml',
'WordText': 'word-text',
'Pdf': 'pdf',
'Image': 'image',
'Audio': 'audio',
'Video': 'video',
'Sheet': 'sheet',
'Presentation': 'presentation',
'Certificate': 'certificate',
'Archive': 'archive'
};
/** /**
* @enum {string} * @enum {string}
*/ */

View file

@ -18,6 +18,7 @@
Globals.$win = $(window); Globals.$win = $(window);
Globals.$doc = $(window.document); Globals.$doc = $(window.document);
Globals.$html = $('html'); Globals.$html = $('html');
Globals.$body = $('body');
Globals.$div = $('<div></div>'); Globals.$div = $('<div></div>');
Globals.$win.__sizes = [0, 0]; Globals.$win.__sizes = [0, 0];
@ -268,7 +269,7 @@
}); });
Globals.keyScopeReal.subscribe(function (sValue) { Globals.keyScopeReal.subscribe(function (sValue) {
// window.console.log('keyScope=' + sValue); // TODO // window.console.log('keyScope=' + sValue); // DEBUG
key.setScope(sValue); key.setScope(sValue);
}); });

View file

@ -31,6 +31,9 @@
this.resize = _.throttle(_.bind(this.resize, this), 100); this.resize = _.throttle(_.bind(this.resize, this), 100);
this.__inited = false;
this.__initedData = null;
this.init(); this.init();
} }
@ -190,7 +193,7 @@
HtmlEditor.prototype.setHtml = function (sHtml, bFocus) HtmlEditor.prototype.setHtml = function (sHtml, bFocus)
{ {
if (this.editor) if (this.editor && this.__inited)
{ {
this.modeToggle(true); this.modeToggle(true);
@ -203,11 +206,15 @@
this.focus(); this.focus();
} }
} }
else
{
this.__initedData = [true, sHtml, bFocus];
}
}; };
HtmlEditor.prototype.setPlain = function (sPlain, bFocus) HtmlEditor.prototype.setPlain = function (sPlain, bFocus)
{ {
if (this.editor) if (this.editor && this.__inited)
{ {
this.modeToggle(false); this.modeToggle(false);
if ('plain' === this.editor.mode && this.editor.plugins.plain && this.editor.__plain) if ('plain' === this.editor.mode && this.editor.plugins.plain && this.editor.__plain)
@ -226,6 +233,10 @@
this.focus(); this.focus();
} }
} }
else
{
this.__initedData = [false, sPlain, bFocus];
}
}; };
HtmlEditor.prototype.init = function () HtmlEditor.prototype.init = function ()
@ -322,7 +333,20 @@
self.fOnReady(); self.fOnReady();
self.__resizable = true; self.__resizable = true;
self.__inited = true;
self.resize(); self.resize();
if (self.__initedData)
{
if (self.__initedData[0])
{
self.setHtml(self.__initedData[1], self.__initedData[2]);
}
else
{
self.setPlain(self.__initedData[1], self.__initedData[2]);
}
}
}); });
} }
} }

View file

@ -435,7 +435,7 @@
}; };
/** /**
* @returns {boolean} * @return {boolean}
*/ */
Selector.prototype.autoSelect = function () Selector.prototype.autoSelect = function ()
{ {
@ -452,7 +452,7 @@
/** /**
* @param {Object} oItem * @param {Object} oItem
* @returns {string} * @return {string}
*/ */
Selector.prototype.getItemUid = function (oItem) Selector.prototype.getItemUid = function (oItem)
{ {

View file

@ -144,7 +144,7 @@
/** /**
* @param {string} sMailToUrl * @param {string} sMailToUrl
* @param {Function} PopupComposeVoreModel * @param {Function} PopupComposeVoreModel
* @returns {boolean} * @return {boolean}
*/ */
Utils.mailToHelper = function (sMailToUrl, PopupComposeVoreModel) Utils.mailToHelper = function (sMailToUrl, PopupComposeVoreModel)
{ {
@ -598,7 +598,7 @@
* @param {string} sTheme * @param {string} sTheme
* @return {string} * @return {string}
*/ */
Utils.convertThemeName = function (sTheme) Utils.convertThemeName = _.memoize(function (sTheme)
{ {
if ('@custom' === sTheme.substr(-7)) if ('@custom' === sTheme.substr(-7))
{ {
@ -606,7 +606,7 @@
} }
return Utils.trim(sTheme.replace(/[^a-zA-Z0-9]+/g, ' ').replace(/([A-Z])/g, ' $1').replace(/[\s]+/g, ' ')); return Utils.trim(sTheme.replace(/[^a-zA-Z0-9]+/g, ' ').replace(/([A-Z])/g, ' $1').replace(/[\s]+/g, ' '));
}; });
/** /**
* @param {string} sName * @param {string} sName
@ -822,40 +822,6 @@
sText = '', sText = '',
splitPlainText = function (sText)
{
var
iLen = 100,
sPrefix = '',
sSubText = '',
sResult = sText,
iSpacePos = 0,
iNewLinePos = 0
;
while (sResult.length > iLen)
{
sSubText = sResult.substring(0, iLen);
iSpacePos = sSubText.lastIndexOf(' ');
iNewLinePos = sSubText.lastIndexOf('\n');
if (-1 !== iNewLinePos)
{
iSpacePos = iNewLinePos;
}
if (-1 === iSpacePos)
{
iSpacePos = iLen;
}
sPrefix += sSubText.substring(0, iSpacePos) + '\n';
sResult = sResult.substring(iSpacePos + 1);
}
return sPrefix + sResult;
},
convertBlockquote = function (sText) { convertBlockquote = function (sText) {
sText = Utils.trim(sText); sText = Utils.trim(sText);
sText = '> ' + sText.replace(/\n/gm, '\n> '); sText = '> ' + sText.replace(/\n/gm, '\n> ');
@ -929,7 +895,7 @@
.replace(/&amp;/gi, '&') .replace(/&amp;/gi, '&')
; ;
sText = splitPlainText(Utils.trim(sText)); sText = Utils.splitPlainText(Utils.trim(sText));
iPos = 0; iPos = 0;
iLimit = 800; iLimit = 800;

View file

@ -9,10 +9,21 @@
; ;
Opentip.styles.rainloop = { Opentip.styles.rainloop = {
'extends': 'standard', 'extends': 'standard',
'fixed': true, 'fixed': true,
'target': true, 'target': true,
'delay': 0.2,
'hideDelay': 0,
'hideEffect': 'fade',
'hideEffectDuration': 0.2,
'showEffect': 'fade',
'showEffectDuration': 0.2,
'showOn': 'mouseover click', 'showOn': 'mouseover click',
'removeElementsOnHide': true, 'removeElementsOnHide': true,
@ -26,8 +37,7 @@
Opentip.styles.rainloopTip = { Opentip.styles.rainloopTip = {
'extends': 'rainloop', 'extends': 'rainloop',
'stemLength': 3, 'delay': 0.4,
'stemBase': 5,
'group': 'rainloopTips' 'group': 'rainloopTips'
}; };

27
dev/External/ko.js vendored
View file

@ -33,7 +33,7 @@
} }
}, },
fUpdateKoValue = function () { fUpdateKoValue = function () {
if (oEditor) if (oEditor && oEditor.__inited)
{ {
fValue(oEditor.getDataWithHtmlMark()); fValue(oEditor.getDataWithHtmlMark());
} }
@ -49,6 +49,7 @@
fValue.__updateEditorValue = fUpdateEditorValue; fValue.__updateEditorValue = fUpdateEditorValue;
fValue.subscribe(fUpdateEditorValue); fValue.subscribe(fUpdateEditorValue);
fUpdateEditorValue();
} }
} }
}; };
@ -61,6 +62,7 @@
sValue = '', sValue = '',
Translator = null, Translator = null,
$oEl = $(oElement), $oEl = $(oElement),
fValue = fValueAccessor(),
bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'), bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'),
Globals = require('Common/Globals') Globals = require('Common/Globals')
; ;
@ -68,7 +70,7 @@
if (!Globals.bMobileDevice || bMobile) if (!Globals.bMobileDevice || bMobile)
{ {
bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on'); bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on');
sValue = ko.unwrap(fValueAccessor()); sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
oElement.__opentip = new Opentip(oElement, { oElement.__opentip = new Opentip(oElement, {
'style': 'rainloopTip', 'style': 'rainloopTip',
@ -78,12 +80,21 @@
Globals.dropdownVisibility.subscribe(function (bV) { Globals.dropdownVisibility.subscribe(function (bV) {
if (bV) { if (bV) {
oElement.__opentip.deactivate(); oElement.__opentip.hide();
} else {
oElement.__opentip.activate();
} }
}); });
if ('' === sValue)
{
oElement.__opentip.hide();
oElement.__opentip.deactivate();
oElement.__opentip.setContent('');
}
else
{
oElement.__opentip.activate();
}
if (bi18n) if (bi18n)
{ {
Translator = require('Common/Translator'); Translator = require('Common/Translator');
@ -113,6 +124,7 @@
bi18n = true, bi18n = true,
sValue = '', sValue = '',
$oEl = $(oElement), $oEl = $(oElement),
fValue = fValueAccessor(),
bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'), bMobile = 'on' === ($oEl.data('tooltip-mobile') || 'off'),
Globals = require('Common/Globals') Globals = require('Common/Globals')
; ;
@ -120,7 +132,7 @@
if ((!Globals.bMobileDevice || bMobile) && oElement.__opentip) if ((!Globals.bMobileDevice || bMobile) && oElement.__opentip)
{ {
bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on'); bi18n = 'on' === ($oEl.data('tooltip-i18n') || 'on');
sValue = ko.unwrap(fValueAccessor()); sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
if (sValue) if (sValue)
{ {
@ -165,7 +177,8 @@
var var
$oEl = $(oElement), $oEl = $(oElement),
sValue = ko.unwrap(fValueAccessor()), fValue = fValueAccessor(),
sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue),
oOpenTips = oElement.__opentip oOpenTips = oElement.__opentip
; ;

View file

@ -8,6 +8,7 @@
_ = require('_'), _ = require('_'),
ko = require('ko'), ko = require('ko'),
Enums = require('Common/Enums'),
Globals = require('Common/Globals'), Globals = require('Common/Globals'),
Utils = require('Common/Utils'), Utils = require('Common/Utils'),
Links = require('Common/Links'), Links = require('Common/Links'),
@ -27,6 +28,8 @@
this.mimeType = ''; this.mimeType = '';
this.fileName = ''; this.fileName = '';
this.fileNameExt = '';
this.fileType = Enums.FileType.Unknown;
this.estimatedSize = 0; this.estimatedSize = 0;
this.friendlySize = ''; this.friendlySize = '';
this.isInline = false; this.isInline = false;
@ -57,6 +60,8 @@
AttachmentModel.prototype.mimeType = ''; AttachmentModel.prototype.mimeType = '';
AttachmentModel.prototype.fileName = ''; AttachmentModel.prototype.fileName = '';
AttachmentModel.prototype.fileType = '';
AttachmentModel.prototype.fileNameExt = '';
AttachmentModel.prototype.estimatedSize = 0; AttachmentModel.prototype.estimatedSize = 0;
AttachmentModel.prototype.friendlySize = ''; AttachmentModel.prototype.friendlySize = '';
AttachmentModel.prototype.isInline = false; AttachmentModel.prototype.isInline = false;
@ -97,6 +102,9 @@
this.friendlySize = Utils.friendlySize(this.estimatedSize); this.friendlySize = Utils.friendlySize(this.estimatedSize);
this.cidWithOutTags = this.cid.replace(/^<+/, '').replace(/>+$/, ''); this.cidWithOutTags = this.cid.replace(/^<+/, '').replace(/>+$/, '');
this.fileNameExt = Utils.getFileExtension(this.fileName);
this.fileType = AttachmentModel.staticFileType(this.fileNameExt, this.mimeType);
bResult = true; bResult = true;
} }
@ -108,9 +116,7 @@
*/ */
AttachmentModel.prototype.isImage = function () AttachmentModel.prototype.isImage = function ()
{ {
return -1 < Utils.inArray(this.mimeType.toLowerCase(), return Enums.FileType.Image === this.fileType;
['image/png', 'image/jpg', 'image/jpeg', 'image/gif']
);
}; };
/** /**
@ -118,7 +124,26 @@
*/ */
AttachmentModel.prototype.isMp3 = function () AttachmentModel.prototype.isMp3 = function ()
{ {
return Audio.supported && '.mp3' === this.fileName.toLowerCase().substr(-4); return Enums.FileType.Audio === this.fileType &&
'mp3' === this.fileNameExt;
};
/**
* @return {boolean}
*/
AttachmentModel.prototype.isOgg = function ()
{
return Enums.FileType.Audio === this.fileType &&
('oga' === this.fileNameExt || 'ogg' === this.fileNameExt);
};
/**
* @return {boolean}
*/
AttachmentModel.prototype.isWav = function ()
{
return Enums.FileType.Audio === this.fileType &&
'wav' === this.fileNameExt;
}; };
/** /**
@ -134,8 +159,12 @@
*/ */
AttachmentModel.prototype.isText = function () AttachmentModel.prototype.isText = function ()
{ {
return -1 < Utils.inArray(this.mimeType, ['application/pgp-signature', 'message/delivery-status', 'message/rfc822']) || return Enums.FileType.Text === this.fileType ||
('text/' === this.mimeType.substr(0, 5) && -1 === Utils.inArray(this.mimeType, ['text/html'])); Enums.FileType.Eml === this.fileType ||
Enums.FileType.Certificate === this.fileType ||
Enums.FileType.Html === this.fileType ||
Enums.FileType.Code === this.fileType
;
}; };
/** /**
@ -143,7 +172,7 @@
*/ */
AttachmentModel.prototype.isPdf = function () AttachmentModel.prototype.isPdf = function ()
{ {
return Globals.bAllowPdfPreview && 'application/pdf' === this.mimeType; return Enums.FileType.Pdf === this.fileType;
}; };
/** /**
@ -152,7 +181,7 @@
AttachmentModel.prototype.isFramed = function () AttachmentModel.prototype.isFramed = function ()
{ {
return this.framed && (Globals.__APP__ && Globals.__APP__.googlePreviewSupported()) && return this.framed && (Globals.__APP__ && Globals.__APP__.googlePreviewSupported()) &&
!this.isPdf() && !this.isText() && !this.isImage(); !(this.isPdf() && Globals.bAllowPdfPreview) && !this.isText() && !this.isImage();
}; };
/** /**
@ -160,7 +189,7 @@
*/ */
AttachmentModel.prototype.hasPreview = function () AttachmentModel.prototype.hasPreview = function ()
{ {
return this.isImage() || this.isPdf() || this.isText() || this.isFramed(); return this.isImage() || (this.isPdf() && Globals.bAllowPdfPreview) || this.isText() || this.isFramed();
}; };
/** /**
@ -168,7 +197,10 @@
*/ */
AttachmentModel.prototype.hasPreplay = function () AttachmentModel.prototype.hasPreplay = function ()
{ {
return this.isMp3(); return (Audio.supportedMp3 && this.isMp3()) ||
(Audio.supportedOgg && this.isOgg()) ||
(Audio.supportedWav && this.isWav())
;
}; };
/** /**
@ -229,7 +261,7 @@
switch (true) switch (true)
{ {
case this.isImage(): case this.isImage():
case this.isPdf(): case this.isPdf() && Globals.bAllowPdfPreview:
sResult = this.linkPreview(); sResult = this.linkPreview();
break; break;
case this.isText(): case this.isText():
@ -274,76 +306,85 @@
}; };
/** /**
* @param {string} sExt
* @param {string} sMimeType * @param {string} sMimeType
* @returns {string} * @return {string}
*/ */
AttachmentModel.staticIconClassHelper = function (sMimeType) AttachmentModel.staticFileType = _.memoize(function (sExt, sMimeType)
{ {
sExt = Utils.trim(sExt).toLowerCase();
sMimeType = Utils.trim(sMimeType).toLowerCase(); sMimeType = Utils.trim(sMimeType).toLowerCase();
var var
sText = '', sResult = Enums.FileType.Unknown,
sClass = 'icon-file', aMimeTypeParts = sMimeType.split('/')
aParts = sMimeType.split('/')
; ;
if (aParts && aParts[1]) switch (true)
{ {
if ('image' === aParts[0]) case 'image' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
{ 'png', 'jpg', 'jpeg', 'gif', 'bmp'
sClass = 'icon-file-image'; ]):
} sResult = Enums.FileType.Image;
else if ('text' === aParts[0]) break;
{ case 'audio' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
sClass = 'icon-file-text'; 'mp3', 'ogg', 'oga', 'wav'
} ]):
else if ('audio' === aParts[0]) sResult = Enums.FileType.Audio;
{ break;
sClass = 'icon-file-music'; case 'video' === aMimeTypeParts[0] || -1 < Utils.inArray(sExt, [
} 'mkv', 'avi'
else if ('video' === aParts[0]) ]):
{ sResult = Enums.FileType.Video;
sClass = 'icon-file-movie'; break;
} case -1 < Utils.inArray(sExt, [
else if (-1 < Utils.inArray(aParts[1], 'php', 'js', 'css'
['zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2', 'x-zip', 'x-7z', 'x-rar', 'x-tar', 'x-gzip', 'x-bzip', 'x-bzip2', 'x-zip-compressed', 'x-7z-compressed', 'x-rar-compressed'])) ]):
{ sResult = Enums.FileType.Code;
sClass = 'icon-file-zip'; break;
} case 'eml' === sExt || -1 < Utils.inArray(sMimeType, [
else if (-1 < Utils.inArray(aParts[1],
['pdf', 'x-pdf']))
{
sText = 'pdf';
sClass = 'icon-none';
}
// else if (-1 < Utils.inArray(aParts[1], [
// 'exe', 'x-exe', 'x-winexe', 'bat'
// ]))
// {
// sClass = 'icon-console';
// }
else if (-1 < Utils.inArray(sMimeType, [
'application/pgp-signature', 'application/pkcs7-signature'
]))
{
sClass = 'icon-file-certificate';
}
else if (-1 < Utils.inArray(sMimeType, [
'message/delivery-status', 'message/rfc822' 'message/delivery-status', 'message/rfc822'
])) ]):
{ sResult = Enums.FileType.Eml;
sClass = 'icon-file-text'; break;
} case ('text' === aMimeTypeParts[0] && 'html' !== aMimeTypeParts[1]) || -1 < Utils.inArray(sExt, [
else if (-1 < Utils.inArray(aParts[1], [ 'txt', 'log'
]):
sResult = Enums.FileType.Text;
break;
case ('text/html' === sMimeType) || -1 < Utils.inArray(sExt, [
'html'
]):
sResult = Enums.FileType.Html;
break;
case -1 < Utils.inArray(aMimeTypeParts[1], [
'zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2', 'x-zip', 'x-7z', 'x-rar', 'x-tar', 'x-gzip', 'x-bzip', 'x-bzip2', 'x-zip-compressed', 'x-7z-compressed', 'x-rar-compressed'
]) || -1 < Utils.inArray(sExt, [
'zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2'
]):
sResult = Enums.FileType.Archive;
break;
case -1 < Utils.inArray(aMimeTypeParts[1], ['pdf', 'x-pdf']) || -1 < Utils.inArray(sExt, [
'pdf'
]):
sResult = Enums.FileType.Pdf;
break;
case -1 < Utils.inArray(sMimeType, [
'application/pgp-signature', 'application/pkcs7-signature', 'application/pgp-keys'
]) || -1 < Utils.inArray(sExt, [
'asc', 'pem', 'ppk'
]):
sResult = Enums.FileType.Certificate;
break;
case -1 < Utils.inArray(aMimeTypeParts[1], [
'rtf', 'msword', 'vnd.msword', 'vnd.openxmlformats-officedocument.wordprocessingml.document', 'rtf', 'msword', 'vnd.msword', 'vnd.openxmlformats-officedocument.wordprocessingml.document',
'vnd.openxmlformats-officedocument.wordprocessingml.template', 'vnd.openxmlformats-officedocument.wordprocessingml.template',
'vnd.ms-word.document.macroEnabled.12', 'vnd.ms-word.document.macroEnabled.12',
'vnd.ms-word.template.macroEnabled.12' 'vnd.ms-word.template.macroEnabled.12'
])) ]):
{ sResult = Enums.FileType.WordText;
sClass = 'icon-file-text'; break;
} case -1 < Utils.inArray(aMimeTypeParts[1], [
else if (-1 < Utils.inArray(aParts[1], [
'excel', 'ms-excel', 'vnd.ms-excel', 'excel', 'ms-excel', 'vnd.ms-excel',
'vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'vnd.openxmlformats-officedocument.spreadsheetml.template', 'vnd.openxmlformats-officedocument.spreadsheetml.template',
@ -351,11 +392,10 @@
'vnd.ms-excel.template.macroEnabled.12', 'vnd.ms-excel.template.macroEnabled.12',
'vnd.ms-excel.addin.macroEnabled.12', 'vnd.ms-excel.addin.macroEnabled.12',
'vnd.ms-excel.sheet.binary.macroEnabled.12' 'vnd.ms-excel.sheet.binary.macroEnabled.12'
])) ]):
{ sResult = Enums.FileType.Sheet;
sClass = 'icon-file-excel'; break;
} case -1 < Utils.inArray(aMimeTypeParts[1], [
else if (-1 < Utils.inArray(aParts[1], [
'powerpoint', 'ms-powerpoint', 'vnd.ms-powerpoint', 'powerpoint', 'ms-powerpoint', 'vnd.ms-powerpoint',
'vnd.openxmlformats-officedocument.presentationml.presentation', 'vnd.openxmlformats-officedocument.presentationml.presentation',
'vnd.openxmlformats-officedocument.presentationml.template', 'vnd.openxmlformats-officedocument.presentationml.template',
@ -364,29 +404,140 @@
'vnd.ms-powerpoint.presentation.macroEnabled.12', 'vnd.ms-powerpoint.presentation.macroEnabled.12',
'vnd.ms-powerpoint.template.macroEnabled.12', 'vnd.ms-powerpoint.template.macroEnabled.12',
'vnd.ms-powerpoint.slideshow.macroEnabled.12' 'vnd.ms-powerpoint.slideshow.macroEnabled.12'
])) ]):
{ sResult = Enums.FileType.Presentation;
sClass = 'icon-file-chart-graph'; break;
} }
return sResult;
});
/**
* @param {string} sFileType
* @return {string}
*/
AttachmentModel.staticIconClass = _.memoize(function (sFileType)
{
var
sText = '',
sClass = 'icon-file'
;
switch (sFileType)
{
case Enums.FileType.Text:
case Enums.FileType.Eml:
case Enums.FileType.WordText:
sClass = 'icon-file-text';
break;
case Enums.FileType.Html:
case Enums.FileType.Code:
sClass = 'icon-file-code';
break;
case Enums.FileType.Image:
sClass = 'icon-file-image';
break;
case Enums.FileType.Audio:
sClass = 'icon-file-music';
break;
case Enums.FileType.Video:
sClass = 'icon-file-movie';
break;
case Enums.FileType.Archive:
sClass = 'icon-file-zip';
break;
case Enums.FileType.Certificate:
sClass = 'icon-file-certificate';
break;
case Enums.FileType.Sheet:
sClass = 'icon-file-excel';
break;
case Enums.FileType.Presentation:
sClass = 'icon-file-chart-graph';
break;
case Enums.FileType.Pdf:
sText = 'pdf';
sClass = 'icon-none';
break;
} }
return [sClass, sText]; return [sClass, sText];
});
/**
* @param {string} sFileType
* @return {string}
*/
AttachmentModel.staticCombinedIconClass = function (aData)
{
var
sClass = '',
aTypes = []
;
if (Utils.isNonEmptyArray(aData))
{
sClass = 'icon-attachment';
aTypes = _.uniq(_.compact(_.map(aData, function (aItem) {
return aItem ? AttachmentModel.staticFileType(
Utils.getFileExtension(aItem[0]), aItem[1]) : '';
})));
if (aTypes && 1 === aTypes.length && aTypes[0])
{
switch (aTypes[0])
{
case Enums.FileType.Text:
case Enums.FileType.WordText:
sClass = 'icon-file-text';
break;
case Enums.FileType.Html:
case Enums.FileType.Code:
sClass = 'icon-file-code';
break;
case Enums.FileType.Image:
sClass = 'icon-file-image';
break;
case Enums.FileType.Audio:
sClass = 'icon-file-music';
break;
case Enums.FileType.Video:
sClass = 'icon-file-movie';
break;
case Enums.FileType.Archive:
sClass = 'icon-file-zip';
break;
case Enums.FileType.Certificate:
sClass = 'icon-file-certificate';
break;
case Enums.FileType.Sheet:
sClass = 'icon-file-excel';
break;
case Enums.FileType.Presentation:
sClass = 'icon-file-chart-graph';
break;
}
}
}
return sClass;
}; };
/** /**
* @returns {string} * @return {string}
*/ */
AttachmentModel.prototype.iconClass = function () AttachmentModel.prototype.iconClass = function ()
{ {
return AttachmentModel.staticIconClassHelper(this.mimeType)[0]; return AttachmentModel.staticIconClass(this.fileType)[0];
}; };
/** /**
* @returns {string} * @return {string}
*/ */
AttachmentModel.prototype.iconText = function () AttachmentModel.prototype.iconText = function ()
{ {
return AttachmentModel.staticIconClassHelper(this.mimeType)[1]; return AttachmentModel.staticIconClass(this.fileType)[1];
}; };
module.exports = AttachmentModel; module.exports = AttachmentModel;

View file

@ -70,7 +70,11 @@
return Utils.mimeContentType(this.fileName()); return Utils.mimeContentType(this.fileName());
}, this); }, this);
this.regDisposables([this.friendlySize]); this.fileExt = ko.computed(function () {
return Utils.getFileExtension(this.fileName());
}, this);
this.regDisposables([this.progressText, this.progressStyle, this.title, this.friendlySize, this.mimeType, this.fileExt]);
} }
_.extend(ComposeAttachmentModel.prototype, AbstractModel.prototype); _.extend(ComposeAttachmentModel.prototype, AbstractModel.prototype);
@ -108,7 +112,8 @@
*/ */
ComposeAttachmentModel.prototype.iconClass = function () ComposeAttachmentModel.prototype.iconClass = function ()
{ {
return AttachmentModel.staticIconClassHelper(this.mimeType())[0]; return AttachmentModel.staticIconClass(
AttachmentModel.staticFileType(this.fileExt(), this.mimeType()))[0];
}; };
/** /**
@ -116,7 +121,8 @@
*/ */
ComposeAttachmentModel.prototype.iconText = function () ComposeAttachmentModel.prototype.iconText = function ()
{ {
return AttachmentModel.staticIconClassHelper(this.mimeType())[1]; return AttachmentModel.staticIconClass(
AttachmentModel.staticFileType(this.fileExt(), this.mimeType()))[1];
}; };
module.exports = ComposeAttachmentModel; module.exports = ComposeAttachmentModel;

View file

@ -66,7 +66,7 @@
}; };
/** /**
* @returns {boolean} * @return {boolean}
*/ */
EmailModel.prototype.validate = function () EmailModel.prototype.validate = function ()
{ {

View file

@ -74,30 +74,11 @@
this.selected = ko.observable(false); this.selected = ko.observable(false);
this.checked = ko.observable(false); this.checked = ko.observable(false);
this.hasAttachments = ko.observable(false); this.hasAttachments = ko.observable(false);
this.attachmentsMainType = ko.observable(''); this.attachmentsSpecData = ko.observableArray([]);
this.attachmentIconClass = ko.computed(function () { this.attachmentIconClass = ko.computed(function () {
var sClass = ''; return AttachmentModel.staticCombinedIconClass(
if (this.hasAttachments()) this.hasAttachments() ? this.attachmentsSpecData() : []);
{
sClass = 'icon-attachment';
switch (this.attachmentsMainType())
{
case 'image':
sClass = 'icon-image';
break;
case 'archive':
sClass = 'icon-file-zip';
break;
case 'doc':
sClass = 'icon-file-text';
break;
case 'certificate':
sClass = 'icon-file-certificate';
break;
}
}
return sClass;
}, this); }, this);
this.body = null; this.body = null;
@ -193,7 +174,7 @@
this.selected(false); this.selected(false);
this.checked(false); this.checked(false);
this.hasAttachments(false); this.hasAttachments(false);
this.attachmentsMainType(''); this.attachmentsSpecData([]);
this.body = null; this.body = null;
this.isHtml(false); this.isHtml(false);
@ -287,7 +268,8 @@
this.dateTimeStampInUTC(Utils.pInt(oJsonMessage.DateTimeStampInUTC)); this.dateTimeStampInUTC(Utils.pInt(oJsonMessage.DateTimeStampInUTC));
this.hasAttachments(!!oJsonMessage.HasAttachments); this.hasAttachments(!!oJsonMessage.HasAttachments);
this.attachmentsMainType(oJsonMessage.AttachmentsMainType); this.attachmentsSpecData(Utils.isArray(oJsonMessage.AttachmentsSpecData) ?
oJsonMessage.AttachmentsSpecData : []);
this.fromEmailString(MessageHelper.emailArrayToString(this.from, true)); this.fromEmailString(MessageHelper.emailArrayToString(this.from, true));
this.fromClearEmailString(MessageHelper.emailArrayToStringClear(this.from)); this.fromClearEmailString(MessageHelper.emailArrayToStringClear(this.from));
@ -337,7 +319,8 @@
} }
this.hasAttachments(!!oJsonMessage.HasAttachments); this.hasAttachments(!!oJsonMessage.HasAttachments);
this.attachmentsMainType(oJsonMessage.AttachmentsMainType); this.attachmentsSpecData(Utils.isArray(oJsonMessage.AttachmentsSpecData) ?
oJsonMessage.AttachmentsSpecData : []);
this.foundedCIDs = Utils.isArray(oJsonMessage.FoundedCIDs) ? oJsonMessage.FoundedCIDs : []; this.foundedCIDs = Utils.isArray(oJsonMessage.FoundedCIDs) ? oJsonMessage.FoundedCIDs : [];
this.attachments(this.initAttachmentsFromJson(oJsonMessage.Attachments)); this.attachments(this.initAttachmentsFromJson(oJsonMessage.Attachments));
@ -524,15 +507,6 @@
if (this.hasAttachments()) if (this.hasAttachments())
{ {
aResult.push('withAttachments'); aResult.push('withAttachments');
switch (this.attachmentsMainType())
{
case 'image':
aResult.push('imageOnlyAttachments');
break;
case 'archive':
aResult.push('archiveOnlyAttachments');
break;
}
} }
if (this.newForAnimation()) if (this.newForAnimation())
{ {
@ -797,7 +771,7 @@
}; };
/** /**
* @returns {string} * @return {string}
*/ */
MessageModel.prototype.generateUid = function () MessageModel.prototype.generateUid = function ()
{ {
@ -850,7 +824,7 @@
this.selected(oMessage.selected()); this.selected(oMessage.selected());
this.checked(oMessage.checked()); this.checked(oMessage.checked());
this.hasAttachments(oMessage.hasAttachments()); this.hasAttachments(oMessage.hasAttachments());
this.attachmentsMainType(oMessage.attachmentsMainType()); this.attachmentsSpecData(oMessage.attachmentsSpecData());
this.body = null; this.body = null;

View file

@ -29,8 +29,6 @@
MessageFullModel.prototype.requestHash = ''; MessageFullModel.prototype.requestHash = '';
MessageFullModel.prototype.proxy = false; MessageFullModel.prototype.proxy = false;
MessageFullModel.prototype.hasAttachments = false; MessageFullModel.prototype.hasAttachments = false;
MessageFullModel.prototype.attachmentsMainType = '';
MessageFullModel.prototype.attachmentsClass = '';
MessageFullModel.prototype.clear = function () MessageFullModel.prototype.clear = function ()
{ {
@ -43,37 +41,6 @@
this.proxy = false; this.proxy = false;
this.hasAttachments = false; this.hasAttachments = false;
this.attachmentsMainType = '';
this.attachmentsClass = '';
};
/**
* @return {string}
*/
MessageFullModel.prototype.getAttachmentsClass = function ()
{
var sClass = '';
if (this.hasAttachments)
{
sClass = 'icon-attachment';
switch (this.attachmentsMainType)
{
case 'image':
sClass = 'icon-image';
break;
case 'archive':
sClass = 'icon-file-zip';
break;
case 'doc':
sClass = 'icon-file-text';
break;
case 'certificate':
sClass = 'icon-file-certificate';
break;
}
}
return sClass;
}; };
/** /**
@ -98,8 +65,6 @@
this.proxy = !!oJson.ExternalProxy; this.proxy = !!oJson.ExternalProxy;
this.hasAttachments = !!oJson.HasAttachments; this.hasAttachments = !!oJson.HasAttachments;
this.attachmentsMainType = Utils.pString(oJson.AttachmentsMainType);
this.attachmentsClass = this.getAttachmentsClass();
bResult = true; bResult = true;
} }

View file

@ -112,6 +112,14 @@
}); });
}; };
UserAjaxUserPromises.prototype.attachmentsActions = function (sAction, aHashes, fTrigger)
{
return this.postRequest('AttachmentsActions', fTrigger, {
'Action': sAction,
'Hashes': aHashes
});
};
UserAjaxUserPromises.prototype.welcomeClose = function () UserAjaxUserPromises.prototype.welcomeClose = function ()
{ {
return this.postRequest('WelcomeClose'); return this.postRequest('WelcomeClose');

View file

@ -57,7 +57,7 @@
}; };
/** /**
* @returns {boolean} * @return {boolean}
*/ */
LicensingAdminSettings.prototype.licenseIsUnlim = function () LicensingAdminSettings.prototype.licenseIsUnlim = function ()
{ {
@ -65,7 +65,7 @@
}; };
/** /**
* @returns {string} * @return {string}
*/ */
LicensingAdminSettings.prototype.licenseExpiredMomentValue = function () LicensingAdminSettings.prototype.licenseExpiredMomentValue = function ()
{ {

View file

@ -59,7 +59,7 @@
}; };
/** /**
* @returns {boolean} * @return {boolean}
*/ */
AccountUserStore.prototype.isRootAccount = function () AccountUserStore.prototype.isRootAccount = function ()
{ {

View file

@ -182,7 +182,7 @@
/** /**
* @param {boolean=} bBoot = false * @param {boolean=} bBoot = false
* @returns {Array} * @return {Array}
*/ */
FolderUserStore.prototype.getNextFolderNames = function (bBoot) FolderUserStore.prototype.getNextFolderNames = function (bBoot)
{ {

View file

@ -714,7 +714,7 @@
/** /**
* @param {Array} aList * @param {Array} aList
* @returns {string} * @return {string}
*/ */
MessageUserStore.prototype.calculateMessageListHash = function (aList) MessageUserStore.prototype.calculateMessageListHash = function (aList)
{ {

View file

@ -142,7 +142,7 @@
NotificationUserStore.prototype.initNotificationPlayer = function () NotificationUserStore.prototype.initNotificationPlayer = function ()
{ {
if (Audio && Audio.supported) if (Audio && Audio.supportedNotification)
{ {
this.soundNotificationIsSupported(true); this.soundNotificationIsSupported(true);
} }
@ -155,7 +155,7 @@
NotificationUserStore.prototype.playSoundNotification = function (bSkipSetting) NotificationUserStore.prototype.playSoundNotification = function (bSkipSetting)
{ {
if (Audio && Audio.supported && (bSkipSetting ? true : this.enableSoundNotification())) if (Audio && Audio.supportedNotification && (bSkipSetting ? true : this.enableSoundNotification()))
{ {
Audio.playNotification(); Audio.playNotification();
} }

View file

@ -72,7 +72,7 @@
/** /**
* @param {string} sEmail * @param {string} sEmail
* @param {string=} sPassword * @param {string=} sPassword
* @returns {?} * @return {?}
*/ */
PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword) PgpUserStore.prototype.findPrivateKeyByEmail = function (sEmail, sPassword)
{ {
@ -109,7 +109,7 @@
/** /**
* @param {string=} sPassword * @param {string=} sPassword
* @returns {?} * @return {?}
*/ */
PgpUserStore.prototype.findSelfPrivateKey = function (sPassword) PgpUserStore.prototype.findSelfPrivateKey = function (sPassword)
{ {

View file

@ -21,6 +21,15 @@
0% {background-position: 0 0;} 100% {background-position: 60px 0;} 0% {background-position: 0 0;} 100% {background-position: 60px 0;}
} }
@keyframes login-form-shake {
0% {transform: translateX(0);}
12.5% {transform: translateX(-6px) rotateY(-5deg)}
37.5% {transform: translateX(5px) rotateY(4deg)}
62.5% {transform: translateX(-3px) rotateY(-2deg)}
87.5% {transform: translateX(2px) rotateY(1deg)}
100% {transform: translateX(0)}
}
html.csstransitions.rl-started-trigger.no-mobile .b-login-content .loginFormWrapper { html.csstransitions.rl-started-trigger.no-mobile .b-login-content .loginFormWrapper {
/*transform: scale(1.1);*/ /*transform: scale(1.1);*/
transform: translateY(-20px); transform: translateY(-20px);
@ -56,6 +65,14 @@ html.csstransitions.rl-started-trigger.no-mobile .b-login-content .loginFormWrap
} }
} }
&.cssanimations.csstransitions.no-mobile .b-login-content .errorAnimated {
animation: login-form-shake 400ms ease-in-out;
}
&.cssanimations.csstransitions.no-mobile .b-login-content .afterLoginHide {
opacity: 0;
}
/* &.csstransitions.no-mobile #rl-content { /* &.csstransitions.no-mobile #rl-content {
.transition(opacity 0.3s ease-out); .transition(opacity 0.3s ease-out);
}*/ }*/

View file

@ -16,7 +16,7 @@
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04), 0 1px 5px rgba(0, 0, 0, 0.1); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04), 0 1px 5px rgba(0, 0, 0, 0.1);
border-radius: 2px; border-radius: 3px;
&.waiting { &.waiting {
opacity: 0.6; opacity: 0.6;
@ -120,6 +120,15 @@
color: #aaa; color: #aaa;
} }
.attachmentIcon {
&.icon-none {
display: none;
}
&.icon-file-certificate {
margin-left: 15px;
}
}
.attachmentIconText { .attachmentIconText {
display: inline-block; display: inline-block;
font-size: 28px; font-size: 28px;
@ -131,15 +140,13 @@
font-style: normal; font-style: normal;
} }
.attachmentIcon.icon-none { /*
display: none;
}
.attachmentIconParent.hasPreview.isImage { .attachmentIconParent.hasPreview.isImage {
.iconMain { .iconMain {
display: none; display: none;
} }
} }
*/
.attachmentIconParent.hasPreview:hover { .attachmentIconParent.hasPreview:hover {
.iconPreview { .iconPreview {

View file

@ -9,6 +9,7 @@
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
text-align: center; text-align: center;
perspective: 500px;
.descWrapper { .descWrapper {

View file

@ -322,7 +322,7 @@ html.rl-no-preview-pane {
&.e-single-line .attachmentParent { &.e-single-line .attachmentParent {
float: left; float: left;
margin: 0 5px 0 0; margin: 0 8px 0 0;
} }
.senderParent { .senderParent {

View file

@ -33,6 +33,7 @@
} }
.opentip-container { .opentip-container {
z-index: 2001 !important; z-index: 2001 !important;
.ot-content { .ot-content {
font-size: 13px; font-size: 13px;
@ -41,4 +42,30 @@
&.style-rainloopErrorTip .ot-content { &.style-rainloopErrorTip .ot-content {
color: red; color: red;
} }
&.ot-show-effect-none, &.ot-hide-effect-none {
transition: none !important;
}
&.ot-show-effect-fade {
transition: none;
&.ot-hidden {
opacity: 0;
}
&.ot-going-to-show {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
&.ot-showing {
opacity: 1;
transition: opacity 0.2s ease-in-out;
}
&.ot-visible {
opacity: 1;
transition: none;
}
}
} }

View file

@ -34,8 +34,17 @@
this.loginError = ko.observable(false); this.loginError = ko.observable(false);
this.passwordError = ko.observable(false); this.passwordError = ko.observable(false);
this.loginErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
this.passwordErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
this.loginFocus = ko.observable(false); this.loginFocus = ko.observable(false);
this.formHidden = ko.observable(false);
this.formError = ko.computed(function () {
return this.loginErrorAnimation() || this.passwordErrorAnimation();
}, this);
this.login.subscribe(function () { this.login.subscribe(function () {
this.loginError(false); this.loginError(false);
}, this); }, this);
@ -44,6 +53,14 @@
this.passwordError(false); this.passwordError(false);
}, this); }, this);
this.loginError.subscribe(function (bV) {
this.loginErrorAnimation(!!bV);
}, this);
this.passwordError.subscribe(function (bV) {
this.passwordErrorAnimation(!!bV);
}, this);
this.submitRequest = ko.observable(false); this.submitRequest = ko.observable(false);
this.submitError = ko.observable(''); this.submitError = ko.observable('');
@ -51,6 +68,9 @@
Utils.triggerAutocompleteInputChange(); Utils.triggerAutocompleteInputChange();
this.loginError(false);
this.passwordError(false);
this.loginError('' === Utils.trim(this.login())); this.loginError('' === Utils.trim(this.login()));
this.passwordError('' === Utils.trim(this.password())); this.passwordError('' === Utils.trim(this.password()));
@ -67,6 +87,7 @@
{ {
if (oData.Result) if (oData.Result)
{ {
this.formHidden(true);
require('App/Admin').loginAndLogoutReload(true); require('App/Admin').loginAndLogoutReload(true);
} }
else if (oData.ErrorCode) else if (oData.ErrorCode)

View file

@ -137,7 +137,7 @@
}; };
/** /**
* @returns {boolean} * @return {boolean}
*/ */
ActivatePopupView.prototype.validateSubscriptionKey = function () ActivatePopupView.prototype.validateSubscriptionKey = function ()
{ {

View file

@ -995,7 +995,7 @@
* *
* @param {Array} aList * @param {Array} aList
* @param {boolean} bFriendly * @param {boolean} bFriendly
* @returns {string} * @return {string}
*/ */
ComposePopupView.prototype.emailArrayToStringLineHelper = function (aList, bFriendly) ComposePopupView.prototype.emailArrayToStringLineHelper = function (aList, bFriendly)
{ {

View file

@ -42,6 +42,7 @@
this.additionalCode = ko.observable(''); this.additionalCode = ko.observable('');
this.additionalCode.error = ko.observable(false); this.additionalCode.error = ko.observable(false);
this.additionalCode.errorAnimation = ko.observable(false).extend({'falseTimeout': 500});
this.additionalCode.focused = ko.observable(false); this.additionalCode.focused = ko.observable(false);
this.additionalCode.visibility = ko.observable(false); this.additionalCode.visibility = ko.observable(false);
this.additionalCodeSignMe = ko.observable(false); this.additionalCodeSignMe = ko.observable(false);
@ -56,6 +57,16 @@
this.emailError = ko.observable(false); this.emailError = ko.observable(false);
this.passwordError = ko.observable(false); this.passwordError = ko.observable(false);
this.emailErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
this.passwordErrorAnimation = ko.observable(false).extend({'falseTimeout': 500});
this.formHidden = ko.observable(false);
this.formError = ko.computed(function () {
return this.emailErrorAnimation() || this.passwordErrorAnimation() ||
(this.additionalCode.visibility() && this.additionalCode.errorAnimation());
}, this);
this.emailFocus = ko.observable(false); this.emailFocus = ko.observable(false);
this.passwordFocus = ko.observable(false); this.passwordFocus = ko.observable(false);
this.submitFocus = ko.observable(false); this.submitFocus = ko.observable(false);
@ -78,6 +89,18 @@
this.additionalCode.error(false); this.additionalCode.error(false);
}, this); }, this);
this.emailError.subscribe(function (bV) {
this.emailErrorAnimation(!!bV);
}, this);
this.passwordError.subscribe(function (bV) {
this.passwordErrorAnimation(!!bV);
}, this);
this.additionalCode.error.subscribe(function (bV) {
this.additionalCode.errorAnimation(!!bV);
}, this);
this.submitRequest = ko.observable(false); this.submitRequest = ko.observable(false);
this.submitError = ko.observable(''); this.submitError = ko.observable('');
this.submitErrorAddidional = ko.observable(''); this.submitErrorAddidional = ko.observable('');
@ -115,16 +138,34 @@
Utils.triggerAutocompleteInputChange(); Utils.triggerAutocompleteInputChange();
this.emailError(false);
this.passwordError(false);
this.emailError('' === Utils.trim(this.email())); this.emailError('' === Utils.trim(this.email()));
this.passwordError('' === Utils.trim(this.password())); this.passwordError('' === Utils.trim(this.password()));
if (this.additionalCode.visibility()) if (this.additionalCode.visibility())
{ {
this.additionalCode.error(false);
this.additionalCode.error('' === Utils.trim(this.additionalCode())); this.additionalCode.error('' === Utils.trim(this.additionalCode()));
} }
if (this.emailError() || this.passwordError() || this.additionalCode.error()) if (this.emailError() || this.passwordError() ||
(this.additionalCode.visibility() && this.additionalCode.error()))
{ {
switch (true)
{
case this.emailError():
this.emailFocus(true);
break;
case this.passwordError():
this.passwordFocus(true);
break;
case this.additionalCode.visibility() && this.additionalCode.error():
this.additionalCode.focused(true);
break;
}
return false; return false;
} }
@ -172,10 +213,12 @@
} }
else if (oData.Admin) else if (oData.Admin)
{ {
this.formHidden(true);
require('App/User').redirectToAdminPanel(); require('App/User').redirectToAdminPanel();
} }
else else
{ {
this.formHidden(true);
require('App/User').loginAndLogoutReload(false); require('App/User').loginAndLogoutReload(false);
} }
} }
@ -339,6 +382,8 @@
if (0 === iErrorCode) if (0 === iErrorCode)
{ {
self.submitRequest(true); self.submitRequest(true);
self.formHidden(true);
require('App/User').loginAndLogoutReload(false); require('App/User').loginAndLogoutReload(false);
} }
else else

View file

@ -374,7 +374,7 @@
}; };
/** /**
* @returns {string} * @return {string}
*/ */
MessageListMailBoxUserView.prototype.printableMessageCountForDeletion = function () MessageListMailBoxUserView.prototype.printableMessageCountForDeletion = function ()
{ {

View file

@ -82,6 +82,10 @@
this.allowAttachmnetControls = ko.observable(false); this.allowAttachmnetControls = ko.observable(false);
this.showAttachmnetControls = ko.observable(false); this.showAttachmnetControls = ko.observable(false);
this.downloadAsZipLoading = ko.observable(false);
this.saveToOwnCloudLoading = ko.observable(false);
this.saveToDropboxLoading = ko.observable(false);
this.showAttachmnetControls.subscribe(function (bV) { this.showAttachmnetControls.subscribe(function (bV) {
if (this.message()) if (this.message())
{ {
@ -115,12 +119,6 @@
this.moreDropdownTrigger = ko.observable(false); this.moreDropdownTrigger = ko.observable(false);
this.messageDomFocused = ko.observable(false).extend({'rateLimit': 0}); this.messageDomFocused = ko.observable(false).extend({'rateLimit': 0});
// TODO
// ko.computed(function () {
// window.console.log('focus:' + AppStore.focusedState() + ', dom:' +
// this.messageDomFocused() + ', key:' + Globals.keyScope() + ' ~ ' + Globals.keyScopeReal());
// }, this).extend({'throttle': 1});
this.messageVisibility = ko.computed(function () { this.messageVisibility = ko.computed(function () {
return !this.messageLoadingThrottle() && !!this.message(); return !this.messageLoadingThrottle() && !!this.message();
}, this); }, this);
@ -759,9 +757,20 @@
} }
var oAttachment = ko.dataFor(this); var oAttachment = ko.dataFor(this);
if (oAttachment && oAttachment.isMp3() && Audio.supported) if (oAttachment && Audio.supported)
{ {
switch (true)
{
case Audio.supportedMp3 && oAttachment.isMp3():
Audio.playMp3(oAttachment.linkDownload(), oAttachment.fileName); Audio.playMp3(oAttachment.linkDownload(), oAttachment.fileName);
break;
case Audio.supportedOgg && oAttachment.isOgg():
Audio.playOgg(oAttachment.linkDownload(), oAttachment.fileName);
break;
case Audio.supportedWav && oAttachment.isWav():
Audio.playWav(oAttachment.linkDownload(), oAttachment.fileName);
break;
}
} }
}) })
.on('click', '.thread-list .more-threads', function (e) { .on('click', '.thread-list .more-threads', function (e) {
@ -1107,6 +1116,40 @@
} }
}; };
MessageViewMailBoxUserView.prototype.getAttachmentsHashes = function ()
{
return _.compact(_.map(this.message() ? this.message().attachments() : [], function (oItem) {
return oItem && oItem.checked() ? oItem.download : '';
}));
};
MessageViewMailBoxUserView.prototype.downloadAsZip = function ()
{
var aHashes = this.getAttachmentsHashes();
if (0 < aHashes.length)
{
Promises.attachmentsActions('Zip', aHashes, this.downloadAsZipLoading);
}
};
MessageViewMailBoxUserView.prototype.saveToOwnCloud = function ()
{
var aHashes = this.getAttachmentsHashes();
if (0 < aHashes.length)
{
Promises.attachmentsActions('OwnCloud', aHashes, this.saveToOwnCloudLoading);
}
};
MessageViewMailBoxUserView.prototype.saveToDropbox = function ()
{
var aHashes = this.getAttachmentsHashes();
if (0 < aHashes.length)
{
Promises.attachmentsActions('Dropbox', aHashes, this.saveToDropboxLoading);
}
};
/** /**
* @param {MessageModel} oMessage * @param {MessageModel} oMessage
*/ */
@ -1119,7 +1162,7 @@
}; };
/** /**
* @returns {string} * @return {string}
*/ */
MessageViewMailBoxUserView.prototype.printableCheckedMessageCount = function () MessageViewMailBoxUserView.prototype.printableCheckedMessageCount = function ()
{ {

View file

@ -2,7 +2,7 @@
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.8.3", "version": "1.8.3",
"release": "298", "release": "301",
"description": "Simple, modern & fast web-based email client", "description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net", "homepage": "http://rainloop.net",
"main": "gulpfile.js", "main": "gulpfile.js",

View file

@ -58,62 +58,17 @@ class AttachmentCollection extends \MailSo\Base\Collection
} }
/** /**
* @return int * @return array
*/ */
public function ImageCount() public function SpecData()
{ {
$aList = $this->FilterList(function ($oAttachment) { return $this->MapList(function ($oAttachment) {
return $oAttachment && $oAttachment->IsImage(); if ($oAttachment)
}); {
return array($oAttachment->FileName(true), $oAttachment->MimeType());
return \is_array($aList) ? \count($aList) : 0;
} }
/** return null;
* @return int
*/
public function ArchiveCount()
{
$aList = $this->FilterList(function ($oAttachment) {
return $oAttachment && $oAttachment->IsArchive();
}); });
return \is_array($aList) ? \count($aList) : 0;
}
/**
* @return int
*/
public function PdfCount()
{
$aList = $this->FilterList(function ($oAttachment) {
return $oAttachment && $oAttachment->IsPdf();
});
return \is_array($aList) ? \count($aList) : 0;
}
/**
* @return int
*/
public function DocCount()
{
$aList = $this->FilterList(function ($oAttachment) {
return $oAttachment && $oAttachment->IsDoc();
});
return \is_array($aList) ? \count($aList) : 0;
}
/**
* @return int
*/
public function CertificateCount()
{
$aList = $this->FilterList(function ($oAttachment) {
return $oAttachment && $oAttachment->IsPgpSignature();
});
return \is_array($aList) ? \count($aList) : 0;
} }
} }

View file

@ -2630,6 +2630,17 @@ class Actions
return $this->FalseResponse(__FUNCTION__); return $this->FalseResponse(__FUNCTION__);
} }
/**
* @return array
*
* @throws \MailSo\Base\Exceptions\Exception
*/
public function DoAttachmentsActions()
{
\sleep(1);
return $this->TrueResponse(__FUNCTION__);
}
/** /**
* @return array * @return array
* *
@ -9079,28 +9090,7 @@ class Actions
$iAttachmentsCount = $oAttachments ? $oAttachments->Count() : 0; $iAttachmentsCount = $oAttachments ? $oAttachments->Count() : 0;
$mResult['HasAttachments'] = 0 < $iAttachmentsCount; $mResult['HasAttachments'] = 0 < $iAttachmentsCount;
$mResult['AttachmentsMainType'] = ''; $mResult['AttachmentsSpecData'] = $mResult['HasAttachments'] ? $oAttachments->SpecData() : array();
if (0 < $iAttachmentsCount)
{
switch (true)
{
case $iAttachmentsCount === $oAttachments->ImageCount():
$mResult['AttachmentsMainType'] = 'image';
break;
case $iAttachmentsCount === $oAttachments->ArchiveCount():
$mResult['AttachmentsMainType'] = 'archive';
break;
case $iAttachmentsCount === $oAttachments->PdfCount():
$mResult['AttachmentsMainType'] = 'pdf';
break;
case $iAttachmentsCount === $oAttachments->DocCount():
$mResult['AttachmentsMainType'] = 'doc';
break;
case $iAttachmentsCount === $oAttachments->CertificateCount():
$mResult['AttachmentsMainType'] = 'certificate';
break;
}
}
$sSubject = $mResult['Subject']; $sSubject = $mResult['Subject'];
$mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']); $mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']);

View file

@ -1,14 +1,16 @@
<div class="b-login-content"> <div class="b-login-content">
<div class="loginFormWrapper"> <div class="loginFormWrapper" data-bind="css: {'afterLoginHide': formHidden}">
<center> <center>
<div class="alert alertError" data-bind="visible: '' !== submitError()"> <div class="alert alertError" data-bind="visible: '' !== submitError()">
<button type="button" class="close" data-bind="click: function () { submitError('') }">&times;</button> <button type="button" class="close" data-bind="click: function () { submitError('') }">&times;</button>
<span data-bind="text: submitError"></span> <span data-bind="text: submitError"></span>
</div> </div>
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm"> <form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError}">
<div class="controls" data-bind="css: {'error': loginError}"> <div class="controls" data-bind="css: {'error': loginError, 'animated': loginErrorAnimation}">
<div class="input-append"> <div class="input-append">
<input type="text" class="input-block-level inputLogin checkAutocomplete" name="RainLoopAdminLogin" id="RainLoopAdminLogin" <input type="text" class="input-block-level inputLogin checkAutocomplete"
name="RainLoopAdminLogin" id="RainLoopAdminLogin"
style="padding-right: 35px;"
autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_LOGIN" autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_LOGIN"
data-bind="textInput: login, hasFocus: loginFocus" /> data-bind="textInput: login, hasFocus: loginFocus" />
<span class="add-on"> <span class="add-on">
@ -16,9 +18,11 @@
</span> </span>
</div> </div>
</div> </div>
<div class="controls" data-bind="css: {'error': passwordError}"> <div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
<div class="input-append"> <div class="input-append">
<input type="password" class="input-block-level inputPassword checkAutocomplete" placeholder="Password" name="RainLoopAdminPassword" id="RainLoopAdminPassword" <input type="password" class="input-block-level inputPassword checkAutocomplete"
placeholder="Password" name="RainLoopAdminPassword" id="RainLoopAdminPassword"
style="padding-right: 35px;"
autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" autocorrect="off" autocapitalize="off" spellcheck="false" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD"
data-bind="textInput: password" /> data-bind="textInput: password" />
<span class="add-on"> <span class="add-on">

View file

@ -1,5 +1,5 @@
<div class="b-login-content"> <div class="b-login-content">
<div class="loginFormWrapper"> <div class="loginFormWrapper" data-bind="css: {'afterLoginHide': formHidden}">
<center> <center>
{{INCLUDE/BeforeLogo/PLACE}} {{INCLUDE/BeforeLogo/PLACE}}
<!-- ko if: logoImg --> <!-- ko if: logoImg -->
@ -19,12 +19,13 @@
<span data-bind="text: submitErrorAddidional"></span> <span data-bind="text: submitErrorAddidional"></span>
</div> </div>
</div> </div>
<form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm"> <form class="wrapper loginForm thm-login" action="#/" data-bind="submit: submitForm, css: {'errorAnimated': formError}">
{{INCLUDE/TopControlGroup/PLACE}} {{INCLUDE/TopControlGroup/PLACE}}
<div class="controls" data-bind="css: {'error': emailError}"> <div class="controls" data-bind="css: {'error': emailError, 'animated': emailErrorAnimation}">
<div class="input-append"> <div class="input-append">
<input type="email" class="i18n input-block-level inputEmail checkAutocomplete" <input type="email" class="i18n input-block-level inputEmail checkAutocomplete"
name="RainLoopEmail" id="RainLoopEmail" name="RainLoopEmail" id="RainLoopEmail"
style="padding-right: 35px;"
autocorrect="off" autocapitalize="off" spellcheck="false" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: email, hasFocus: emailFocus" data-i18n="[placeholder]LOGIN/LABEL_EMAIL" /> data-bind="textInput: email, hasFocus: emailFocus" data-i18n="[placeholder]LOGIN/LABEL_EMAIL" />
<span class="add-on"> <span class="add-on">
@ -32,9 +33,11 @@
</span> </span>
</div> </div>
</div> </div>
<div class="controls" data-bind="css: {'error': passwordError}"> <div class="controls" data-bind="css: {'error': passwordError, 'animated': passwordErrorAnimation}">
<div class="input-append"> <div class="input-append">
<input type="password" class="i18n input-block-level inputPassword checkAutocomplete" name="RainLoopPassword" id="RainLoopPassword" <input type="password" class="i18n input-block-level inputPassword checkAutocomplete"
name="RainLoopPassword" id="RainLoopPassword"
style="padding-right: 35px;"
autocorrect="off" autocapitalize="off" spellcheck="false" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: password, hasFocus: passwordFocus" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" /> data-bind="textInput: password, hasFocus: passwordFocus" data-i18n="[placeholder]LOGIN/LABEL_PASSWORD" />
<span class="add-on"> <span class="add-on">
@ -42,9 +45,11 @@
</span> </span>
</div> </div>
</div> </div>
<div class="controls" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error}"> <div class="controls" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error, 'animated': additionalCode.errorAnimation}">
<div class="input-append"> <div class="input-append">
<input type="text" class="i18n input-block-level inputAdditionalCode" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" <input type="text" class="i18n input-block-level inputAdditionalCode"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
style="padding-right: 35px;"
data-bind="textInput: additionalCode, hasFocus: additionalCode.focused" data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" /> data-bind="textInput: additionalCode, hasFocus: additionalCode.focused" data-i18n="[placeholder]LOGIN/LABEL_VERIFICATION_CODE" />
<span class="add-on"> <span class="add-on">
<i class="icon-key"></i> <i class="icon-key"></i>

View file

@ -431,13 +431,30 @@
</div> </div>
<div class="attachmentsControls" <div class="attachmentsControls"
data-bind="visible: showAttachmnetControls() && message() && message().hasVisibleAttachments()"> data-bind="visible: showAttachmnetControls() && message() && message().hasVisibleAttachments()">
<i class="icon-file-zip"></i>
<i class="icon-file-zip"
data-bind="css: {'icon-file-zip': !downloadAsZipLoading(), 'icon-spinner animated': downloadAsZipLoading()}"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="g-ui-link">Download as zip</span> <span class="g-ui-link" data-bind="click: downloadAsZip">Download as zip</span>
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
<i class="icon-cloud-up"></i>
<i class="icon-cloud-up"
data-bind="css: {'icon-cloud-up': !saveToOwnCloudLoading(), 'icon-spinner animated': saveToOwnCloudLoading()}"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="g-ui-link">Save to ownCloud</span> <span class="g-ui-link" data-bind="click: saveToOwnCloud">Save to ownCloud</span>
&nbsp;&nbsp;&nbsp;&nbsp;
<i class="icon-dropbox"
data-bind="css: {'icon-dropbox': !saveToDropboxLoading(), 'icon-spinner animated': saveToDropboxLoading()}"></i>
&nbsp;&nbsp;
<span class="g-ui-link" data-bind="click: saveToDropbox">Save to Dropbox</span>
<!-- // --->
<button type="button" class="close" style="margin-right: 5px;" <button type="button" class="close" style="margin-right: 5px;"
data-bind="click: function () { showAttachmnetControls(false); }">×</button> data-bind="click: function () { showAttachmnetControls(false); }">×</button>
</div> </div>