Small fixes

Utf8 clear supports icon unicode
This commit is contained in:
RainLoop Team 2015-02-25 19:29:34 +04:00
parent e6725198c4
commit 19f089d0bf
15 changed files with 141 additions and 62 deletions

View file

@ -7,7 +7,6 @@
Globals = {}, Globals = {},
window = require('window'), window = require('window'),
is = require('is'),
_ = require('_'), _ = require('_'),
$ = require('$'), $ = require('$'),
ko = require('ko'), ko = require('ko'),
@ -69,14 +68,21 @@
Globals.bUnload = false; Globals.bUnload = false;
/** /**
* @type {boolean} * @type {string}
*/ */
Globals.bTabletDevice = is.tablet(); Globals.sUserAgent = 'navigator' in window && 'userAgent' in window.navigator &&
window.navigator.userAgent.toLowerCase() || '';
/** /**
* @type {boolean} * @type {boolean}
*/ */
Globals.bMobileDevice = is.mobile() || is.tablet(); Globals.bMobileDevice =
/android/i.test(Globals.sUserAgent) ||
/iphone/i.test(Globals.sUserAgent) ||
/ipod/i.test(Globals.sUserAgent) ||
/ipad/i.test(Globals.sUserAgent) ||
/blackberry/i.test(Globals.sUserAgent)
;
/** /**
* @type {boolean} * @type {boolean}

View file

@ -58,7 +58,7 @@
this.selectedItem(null); this.selectedItem(null);
} }
} }
else if (this.bAutoSelect && this.focusedItem()) else if (this.autoSelect() && this.focusedItem())
{ {
this.selectedItem(this.focusedItem()); this.selectedItem(this.focusedItem());
} }
@ -124,10 +124,10 @@
this.sItemFocusedSelector = sItemFocusedSelector; this.sItemFocusedSelector = sItemFocusedSelector;
this.sLastUid = ''; this.sLastUid = '';
this.bAutoSelect = true;
this.oCallbacks = {}; this.oCallbacks = {};
this.emptyFunction = function () {}; this.emptyFunction = function () {};
this.emptyTrueFunction = function () { return true; };
this.focusedItem.subscribe(function (oItem) { this.focusedItem.subscribe(function (oItem) {
if (oItem) if (oItem)
@ -221,7 +221,7 @@
this.selectedItemUseCallback = true; this.selectedItemUseCallback = true;
if (!bChecked && !bSelected && this.bAutoSelect) if (!bChecked && !bSelected && this.autoSelect())
{ {
if (self.focusedItem()) if (self.focusedItem())
{ {
@ -392,9 +392,12 @@
} }
}; };
Selector.prototype.autoSelect = function (bValue) /**
* @returns {boolean}
*/
Selector.prototype.autoSelect = function ()
{ {
this.bAutoSelect = !!bValue; return !!(this.oCallbacks['onAutoSelect'] || this.emptyTrueFunction)();
}; };
/** /**
@ -540,7 +543,7 @@
} }
} }
if ((this.bAutoSelect || !!bForceSelect) && if ((this.autoSelect() || !!bForceSelect) &&
!this.isListChecked() && Enums.EventKeyCode.Space !== iEventKeyCode) !this.isListChecked() && Enums.EventKeyCode.Space !== iEventKeyCode)
{ {
this.selectedItem(oResult); this.selectedItem(oResult);

View file

@ -244,9 +244,7 @@
*/ */
Utils.encodeHtml = function (sText) Utils.encodeHtml = function (sText)
{ {
return Utils.isNormal(sText) ? sText.toString() return Utils.isNormal(sText) ? _.escape(sText.toString()) : '';
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"/g, '&quot;').replace(/'/g, '&#039;') : '';
}; };
/** /**
@ -512,7 +510,7 @@
/** /**
* @param {?} oEvent * @param {?} oEvent
*/ */
Utils.killCtrlAandS = function (oEvent) Utils.kill_CtrlA_CtrlS = function (oEvent)
{ {
oEvent = oEvent || window.event; oEvent = oEvent || window.event;
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey) if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
@ -944,7 +942,7 @@
fixAttibuteValue = function () { fixAttibuteValue = function () {
return (arguments && 1 < arguments.length) ? return (arguments && 1 < arguments.length) ?
'' + arguments[1] + arguments[2].replace(/</g, '&lt;').replace(/>/g, '&gt;') : ''; '' + arguments[1] + _.escape(arguments[2]) : '';
}, },
convertLinks = function () { convertLinks = function () {
@ -1550,6 +1548,36 @@
} }
}; };
/**
* @param {string} sStr
* @return {string}
*/
Utils.FullHtmlEncode = function (sStr)
{
var
iIndex = 0,
sChr = '',
iChrCode = 0,
sResult = '';
;
for (iIndex = 0; iIndex < sStr.length; iIndex++)
{
sChr = sStr[iIndex];
if (-1 < Utils.inArray(sChr, ['&', '<', '>', '"', '`', '\'']))
{
sResult += _.escape(sChr);
}
else
{
iChrCode = sChr.charCodeAt(0);
sResult += (iChrCode > 128) ? '&#' + iChrCode + ';' : sChr;
}
}
return sResult;
};
/** /**
* @param {Object|Array} mObjectOrObjects * @param {Object|Array} mObjectOrObjects
*/ */

View file

@ -63,7 +63,7 @@
if (Consts.Values.TokenErrorLimit < Globals.iTokenErrorCount) if (Consts.Values.TokenErrorLimit < Globals.iTokenErrorCount)
{ {
if (Globals.__APP__) if (Globals.__APP__ && Globals.__APP__.loginAndLogoutReload)
{ {
Globals.__APP__.loginAndLogoutReload(true); Globals.__APP__.loginAndLogoutReload(true);
} }
@ -71,11 +71,11 @@
if (oData.ClearAuth || oData.Logout || Consts.Values.AjaxErrorLimit < Globals.iAjaxErrorCount) if (oData.ClearAuth || oData.Logout || Consts.Values.AjaxErrorLimit < Globals.iAjaxErrorCount)
{ {
if (Globals.__APP__) if (Globals.__APP__ && Globals.__APP__.clearClientSideToken)
{ {
Globals.__APP__.clearClientSideToken(); Globals.__APP__.clearClientSideToken();
if (!oData.ClearAuth) if (!oData.ClearAuth && Globals.__APP__.loginAndLogoutReload)
{ {
Globals.__APP__.loginAndLogoutReload(true); Globals.__APP__.loginAndLogoutReload(true);
} }

View file

@ -43,6 +43,7 @@
this.submitRequest = ko.observable(false); this.submitRequest = ko.observable(false);
this.submitError = ko.observable(''); this.submitError = ko.observable('');
this.submitErrorAdditional = ko.observable('');
this.emailFocus = ko.observable(false); this.emailFocus = ko.observable(false);
@ -61,21 +62,28 @@
Remote.accountSetup(_.bind(function (sResult, oData) { Remote.accountSetup(_.bind(function (sResult, oData) {
this.submitRequest(false); this.submitRequest(false);
if (Enums.StorageResultType.Success === sResult && oData && 'AccountSetup' === oData.Action) if (Enums.StorageResultType.Success === sResult && oData)
{ {
if (oData.Result) if (oData.Result)
{ {
require('App/User').accountsAndIdentities(); require('App/User').accountsAndIdentities();
this.cancelCommand(); this.cancelCommand();
} }
else if (oData.ErrorCode) else
{ {
this.submitError(Translator.getNotification(oData.ErrorCode)); this.submitError(oData.ErrorCode ? Translator.getNotification(oData.ErrorCode) :
Translator.getNotification(Enums.Notification.UnknownError));
if (oData.ErrorMessageAdditional)
{
this.submitErrorAdditional(oData.ErrorMessageAdditional);
}
} }
} }
else else
{ {
this.submitError(Translator.getNotification(Enums.Notification.UnknownError)); this.submitError(Translator.getNotification(Enums.Notification.UnknownError));
this.submitErrorAdditional('');
} }
}, this), this.email(), this.password(), this.isNew()); }, this), this.email(), this.password(), this.isNew());
@ -104,6 +112,7 @@
this.submitRequest(false); this.submitRequest(false);
this.submitError(''); this.submitError('');
this.submitErrorAdditional('');
}; };
AccountPopupView.prototype.onShow = function (oAccount) AccountPopupView.prototype.onShow = function (oAccount)

View file

@ -251,15 +251,9 @@
return oMessage ? oMessage.generateUid() : ''; return oMessage ? oMessage.generateUid() : '';
}); });
MessageStore.messageListEndHash.subscribe(function () { this.selector.on('onAutoSelect', _.bind(function () {
this.selector.scrollToTop(); return this.useAutoSelect();
}, this); }, this));
SettingsStore.layout.subscribe(function (mValue) {
this.selector.autoSelect(Enums.Layout.NoPreview !== mValue);
}, this);
SettingsStore.layout.valueHasMutated();
Events Events
.sub('mailbox.message-list.selector.go-down', function () { .sub('mailbox.message-list.selector.go-down', function () {
@ -270,6 +264,10 @@
}, this) }, this)
; ;
MessageStore.messageListEndHash.subscribe(function () {
this.selector.scrollToTop();
}, this);
kn.constructorEnd(this); kn.constructorEnd(this);
} }
@ -281,6 +279,16 @@
*/ */
MessageListMailBoxUserView.prototype.emptySubjectValue = ''; MessageListMailBoxUserView.prototype.emptySubjectValue = '';
MessageListMailBoxUserView.prototype.useAutoSelect = function ()
{
if (/is:unseen/.test(this.mainMessageListSearch()))
{
return false;
}
return Enums.Layout.NoPreview !== SettingsStore.layout();
};
MessageListMailBoxUserView.prototype.searchEnterAction = function () MessageListMailBoxUserView.prototype.searchEnterAction = function ()
{ {
this.mainMessageListSearch(this.sLastSearchValue); this.mainMessageListSearch(this.sLastSearchValue);
@ -721,6 +729,14 @@
return false; return false;
}); });
key('enter', Enums.KeyState.MessageList, function () {
if (self.message() && self.useAutoSelect())
{
Events.pub('mailbox.message-view.toggle-full-screen');
return false;
}
});
// archive (zip) // archive (zip)
key('z', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () { key('z', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
self.archiveCommand(); self.archiveCommand();

View file

@ -323,6 +323,10 @@
Events.pub('mailbox.message-list.selector.go-down'); Events.pub('mailbox.message-list.selector.go-down');
}); });
Events.sub('mailbox.message-view.toggle-full-screen', function () {
this.toggleFullScreen();
}, this);
kn.constructorEnd(this); kn.constructorEnd(this);
} }
@ -581,6 +585,11 @@
if (this.fullScreenMode()) if (this.fullScreenMode())
{ {
this.fullScreenMode(false); this.fullScreenMode(false);
if (Enums.Layout.NoPreview !== this.layout())
{
this.message.focused(false);
}
} }
else if (Enums.Layout.NoPreview === this.layout()) else if (Enums.Layout.NoPreview === this.layout())
{ {
@ -610,14 +619,6 @@
return false; return false;
}); });
key('enter', Enums.KeyState.MessageList, function () {
if (Enums.Layout.NoPreview !== self.layout() && self.message())
{
self.toggleFullScreen();
return false;
}
});
// reply // reply
key('r', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () { key('r', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
if (MessageStore.message()) if (MessageStore.message())

4
dev/bootstrap.js vendored
View file

@ -22,8 +22,8 @@
Globals.__APP__ = App; Globals.__APP__ = App;
Globals.$win Globals.$win
.keydown(Utils.killCtrlAandS) .keydown(Utils.kill_CtrlA_CtrlS)
.keyup(Utils.killCtrlAandS) .keyup(Utils.kill_CtrlA_CtrlS)
.unload(function () { .unload(function () {
Globals.bUnload = true; Globals.bUnload = true;
}) })

View file

@ -2,7 +2,7 @@
"name": "RainLoop", "name": "RainLoop",
"title": "RainLoop Webmail", "title": "RainLoop Webmail",
"version": "1.8.1", "version": "1.8.1",
"release": "272", "release": "274",
"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

@ -17,6 +17,22 @@ namespace MailSo\Base;
*/ */
class Utils class Utils
{ {
/**
* @var string
*/
static $sValidUtf8Regexp = <<<'END'
/
(
(?: [\x00-\x7F] # single-byte sequences 0xxxxxxx
| [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
){1,100} # ...one or more times
)
| . # anything else
/x
END;
/** /**
* @var array * @var array
*/ */
@ -1432,15 +1448,7 @@ class Utils
return $sUtfString; return $sUtfString;
} }
$sUtfString = \preg_replace( $sUtfString = \preg_replace(\MailSo\Base\Utils::$sValidUtf8Regexp, '$1', $sUtfString);
'/[\x00-\x08\x10\x0B\x0C\x0E-\x1F\x7F]'.
'|[\x00-\x7F][\x80-\xBF]+'.
'|([\xC0\xC1]|[\xF0-\xFF])[\x80-\xBF]*'.
'|[\xC2-\xDF]((?![\x80-\xBF])|[\x80-\xBF]{2,})'.
'|[\xE0-\xEF](([\x80-\xBF](?![\x80-\xBF]))|(?![\x80-\xBF]{2})|[\x80-\xBF]{3,})/S',
$sReplaceOn,
$sUtfString
);
$sUtfString = \preg_replace( $sUtfString = \preg_replace(
'/\xE0[\x80-\x9F][\x80-\xBF]'. '/\xE0[\x80-\x9F][\x80-\xBF]'.
@ -1450,15 +1458,16 @@ class Utils
$sUtfString = \preg_replace('/\xEF\xBF\xBD/', '?', $sUtfString); $sUtfString = \preg_replace('/\xEF\xBF\xBD/', '?', $sUtfString);
$sNewUtfString = false; $sNewUtfString = false;
if (\MailSo\Base\Utils::IsIconvSupported()) if (false === $sNewUtfString && \MailSo\Base\Utils::IsMbStringSupported())
{
$sNewUtfString = \MailSo\Base\Utils::IconvConvertEncoding($sUtfString, 'UTF-8', 'UTF-8');
}
else if (\MailSo\Base\Utils::IsMbStringSupported())
{ {
$sNewUtfString = \MailSo\Base\Utils::MbConvertEncoding($sUtfString, 'UTF-8', 'UTF-8'); $sNewUtfString = \MailSo\Base\Utils::MbConvertEncoding($sUtfString, 'UTF-8', 'UTF-8');
} }
if (false === $sNewUtfString && \MailSo\Base\Utils::IsIconvSupported())
{
$sNewUtfString = \MailSo\Base\Utils::IconvConvertEncoding($sUtfString, 'UTF-8', 'UTF-8');
}
if (false !== $sNewUtfString) if (false !== $sNewUtfString)
{ {
$sUtfString = $sNewUtfString; $sUtfString = $sNewUtfString;

View file

@ -1185,7 +1185,7 @@ class ImapClient extends \MailSo\Net\NetClient
foreach ($aResponse as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse) foreach ($aResponse as /* @var $oImapResponse \MailSo\Imap\Response */ $oImapResponse)
{ {
if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType if (\MailSo\Imap\Enumerations\ResponseType::UNTAGGED === $oImapResponse->ResponseType
&& 'ESEARCH' === $oImapResponse->StatusOrIndex && ('ESEARCH' === $oImapResponse->StatusOrIndex || 'ESORT' === $oImapResponse->StatusOrIndex)
&& \is_array($oImapResponse->ResponseList) && \is_array($oImapResponse->ResponseList)
&& isset($oImapResponse->ResponseList[2], $oImapResponse->ResponseList[2][0], $oImapResponse->ResponseList[2][1]) && isset($oImapResponse->ResponseList[2], $oImapResponse->ResponseList[2][0], $oImapResponse->ResponseList[2][1])
&& 'TAG' === $oImapResponse->ResponseList[2][0] && $sRequestTag === $oImapResponse->ResponseList[2][1] && 'TAG' === $oImapResponse->ResponseList[2][0] && $sRequestTag === $oImapResponse->ResponseList[2][1]

View file

@ -2156,6 +2156,9 @@ class Actions
1 < \count($aOrder['Accounts'])) 1 < \count($aOrder['Accounts']))
{ {
$aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts); $aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts);
$aAccounts = \array_filter($aAccounts, function ($sHash) {
return 5 < \strlen($sHash);
});
} }
} }

View file

@ -131,9 +131,10 @@
<div class="content g-scrollbox"> <div class="content g-scrollbox">
<div class="content-wrapper"> <div class="content-wrapper">
<div class="listSearchDesc" data-bind="visible: '' !== messageListSearchDesc()"> <div class="listSearchDesc" data-bind="visible: '' !== messageListSearchDesc()">
<a class="btn btn-small pull-right searchCancelButton" data-bind="click: cancelSearch"> <a class="close-custom" data-bind="click: cancelSearch">&times;</a>
<!-- <a class="btn btn-small pull-right searchCancelButton" data-bind="click: cancelSearch">
<i class="icon-remove"></i> <i class="icon-remove"></i>
</a> </a>-->
<span data-bind="text: messageListSearchDesc"></span>: <span data-bind="text: messageListSearchDesc"></span>:
</div> </div>
<div class="listDragOver" data-bind="css: {'viewAppendArea': dragOver() && '' === messageListError() && !popupVisibility(), 'dragOverEnter': dragOverEnter }, initDom: dragOverArea"> <div class="listDragOver" data-bind="css: {'viewAppendArea': dragOver() && '' === messageListError() && !popupVisibility(), 'dragOverEnter': dragOverEnter }, initDom: dragOverArea">

View file

@ -11,14 +11,18 @@
<div class="modal-body"> <div class="modal-body">
<div class="form-horizontal"> <div class="form-horizontal">
<div class="alert" data-bind="visible: '' !== submitError()"> <div class="alert" data-bind="visible: '' !== submitError()">
<button type="button" class="close" data-bind="click: function () { submitError('') }">&times;</button> <button type="button" class="close-custom" data-bind="click: function () { submitError('') }">&times;</button>
<span data-bind="text: submitError"></span> <span data-bind="text: submitError"></span>
<div data-bind="visible: submitErrorAdditional">
<br />
<span data-bind="text: submitErrorAdditional"></span>
</div>
</div> </div>
<br /> <br />
<div class="control-group" data-bind="css: {'error': emailError}"> <div class="control-group" data-bind="css: {'error': emailError}">
<label class="i18n control-label" data-i18n-text="LOGIN/LABEL_EMAIL"></label> <label class="i18n control-label" data-i18n-text="LOGIN/LABEL_EMAIL"></label>
<div class="controls"> <div class="controls">
<label style="margin-top: 5px;"><strong data-bind="visible: !isNew(), text: email"></strong></label> <label style="margin-top: 5px;" data-bind="visible: !isNew()"><strong data-bind="text: email"></strong></label>
<input type="email" class="inputEmail input-large" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" <input type="email" class="inputEmail input-large" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="visible: isNew, textInput: email, onEnter: addAccountCommand, hasfocus: emailFocus" /> data-bind="visible: isNew, textInput: email, onEnter: addAccountCommand, hasfocus: emailFocus" />
</div> </div>

View file

@ -42,7 +42,6 @@ module.exports = {
'ssm': 'window.ssm', 'ssm': 'window.ssm',
'key': 'window.key', 'key': 'window.key',
'_': 'window._', '_': 'window._',
'is': 'window.is',
'$': 'window.jQuery' '$': 'window.jQuery'
} }
}; };