diff --git a/_include.php b/_include.php index eefd7e088..b615511b3 100644 --- a/_include.php +++ b/_include.php @@ -10,3 +10,12 @@ function __get_custom_data_full_path() return ''; return '/var/external-rainloop-data-folder/'; // custom data folder path } + +/** + * @return string + */ +function __get_additional_configuration_name() +{ + return ''; + return defined('APP_SITE') && 0 < strlen(APP_SITE) ? APP_SITE.'.ini' : ''; // additional configuration file name +} diff --git a/dev/App/Admin.js b/dev/App/Admin.js index cd4a42a9f..60ef8fe50 100644 --- a/dev/App/Admin.js +++ b/dev/App/Admin.js @@ -7,7 +7,7 @@ window = require('window'), _ = require('_'), ko = require('ko'), - SimplePace = require('SimplePace'), + progressJs = require('progressJs'), Enums = require('Common/Enums'), Utils = require('Common/Utils'), @@ -208,7 +208,7 @@ Remote.licensing(function (sResult, oData) { LicenseStore.licensingProcess(false); - + if (Enums.StorageResultType.Success === sResult && oData && oData.Result && Utils.isNormal(oData.Result['Expired'])) { LicenseStore.licenseValid(true); @@ -280,9 +280,9 @@ } } - if (SimplePace) + if (progressJs) { - SimplePace.set(100); + progressJs().end(); } }; diff --git a/dev/App/User.js b/dev/App/User.js index 5f68b0675..01a2881a7 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -7,7 +7,7 @@ window = require('window'), _ = require('_'), $ = require('$'), - SimplePace = require('SimplePace'), + progressJs = require('progressJs'), Tinycon = require('Tinycon'), Enums = require('Common/Enums'), @@ -255,6 +255,7 @@ { var self = this, + sTrashFolder = FolderStore.trashFolder(), sSpamFolder = FolderStore.spamFolder() ; @@ -262,11 +263,12 @@ var bSpam = sSpamFolder === oItem['To'], + bTrash = sTrashFolder === oItem['To'], bHam = !bSpam && sSpamFolder === oItem['From'] && Cache.getFolderInboxName() === oItem['To'] ; Remote.messagesMove(self.moveOrDeleteResponseHelper, oItem['From'], oItem['To'], oItem['Uid'], - bSpam ? 'SPAM' : (bHam ? 'HAM' : '')); + bSpam ? 'SPAM' : (bHam ? 'HAM' : ''), bSpam || bTrash); }); this.oMoveCache = {}; @@ -1305,9 +1307,9 @@ { kn.hideLoading(); - if (SimplePace) + if (progressJs) { - SimplePace.set(100); + progressJs().end(); } }; @@ -1332,10 +1334,9 @@ bTwitter = Settings.settingsGet('AllowTwitterSocial') ; - if (SimplePace) + if (progressJs) { - SimplePace.set(70); - SimplePace.sleep(); + progressJs().set(70); } Globals.leftPanelDisabled.subscribe(function (bValue) { diff --git a/dev/External/ko.js b/dev/External/ko.js index 131b5a8de..9fe17256c 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -811,7 +811,7 @@ 'parseOnBlur': true, 'allowDragAndDrop': true, 'focusCallback': fFocusCallback, - 'inputDelimiters': [',', ';'], + 'inputDelimiters': [',', ';', '\n'], 'autoCompleteSource': fAutoCompleteSource, // 'elementHook': function (oEl, oItem) { // if (oEl && oItem) @@ -821,23 +821,53 @@ // } // }, 'parseHook': function (aInput) { - return _.map(aInput, function (sInputValue) { + + var aResult = []; + + _.each(aInput, function (sInputValue) { var + aM = null, + aValues = [], sValue = Utils.trim(sInputValue), oEmail = null ; if ('' !== sValue) { - oEmail = new EmailModel(); - oEmail.mailsoParse(sValue); - return [oEmail.toLine(false), oEmail]; + aM = sValue.match(/[@]/g); + if (aM && 0 < aM.length) + { + sValue = sValue.replace(/[\r\n]+/g, '; ').replace(/[\s]+/g, ' '); + aValues = EmailModel.splitHelper(sValue, ';'); + + _.each(aValues, function (sV) { + + oEmail = new EmailModel(); + oEmail.mailsoParse(sV); + + if (oEmail.email) + { + aResult.push([oEmail.toLine(false), oEmail]); + } + else + { + aResult.push(['', null]); + } + }); + } + else + { + aResult.push([sInputValue, null]); + } + } + else + { + aResult.push([sInputValue, null]); } - - return [sValue, null]; - }); + + return aResult; }, 'change': _.bind(function (oEvent) { $oEl.data('EmailsTagsValue', oEvent.target.value); diff --git a/dev/Model/Email.js b/dev/Model/Email.js index 173ed2a80..9304c7d22 100644 --- a/dev/Model/Email.js +++ b/dev/Model/Email.js @@ -36,6 +36,49 @@ return oEmailModel.initByJson(oJsonEmail) ? oEmailModel : null; }; + /** + * @static + * @param {string} sLine + * @param {string=} sDelimiter = ';' + * @return {Array} + */ + EmailModel.splitHelper = function (sLine, sDelimiter) + { + sDelimiter = sDelimiter || ';'; + + sLine = sLine.replace(/[\r\n]+/g, '; ').replace(/[\s]+/g, ' '); + + var + iIndex = 0, + iLen = sLine.length, + bAt = false, + sChar = '', + sResult = '' + ; + + for (; iIndex < iLen; iIndex++) + { + sChar = sLine.charAt(iIndex); + switch (sChar) + { + case '@': + bAt = true; + break; + case ' ': + if (bAt) + { + bAt = false; + sResult += sDelimiter; + } + break; + } + + sResult += sChar; + } + + return sResult.split(sDelimiter); + }; + /** * @type {string} */ diff --git a/dev/Remote/User/Ajax.js b/dev/Remote/User/Ajax.js index e5ae6c606..b5a145b25 100644 --- a/dev/Remote/User/Ajax.js +++ b/dev/Remote/User/Ajax.js @@ -727,13 +727,15 @@ * @param {string} sToFolder * @param {Array} aUids * @param {string=} sLearning + * @param {boolean=} bMarkAsRead */ - RemoteUserAjax.prototype.messagesMove = function (fCallback, sFolder, sToFolder, aUids, sLearning) + RemoteUserAjax.prototype.messagesMove = function (fCallback, sFolder, sToFolder, aUids, sLearning, bMarkAsRead) { this.defaultRequest(fCallback, 'MessageMove', { 'FromFolder': sFolder, 'ToFolder': sToFolder, 'Uids': aUids.join(','), + 'MarkAsRead': bMarkAsRead ? '1' : '0', 'Learning': sLearning || '' }, null, '', ['MessageList']); }; diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 1c5de6b8a..9e7a3eed3 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -328,6 +328,8 @@ self = this, iUnseenCount = 0, oMessage = null, + sTrashFolder = FolderStore.trashFolder(), + sSpamFolder = FolderStore.spamFolder(), aMessageList = this.messageList(), oFromFolder = Cache.getFolderFromCacheList(sFromFolderFullNameRaw), oToFolder = '' === sToFolderFullNameRaw ? null : Cache.getFolderFromCacheList(sToFolderFullNameRaw || ''), @@ -359,6 +361,11 @@ if (oToFolder) { + if (sTrashFolder === oToFolder.fullNameRaw || sSpamFolder === oToFolder.fullNameRaw) + { + iUnseenCount = 0; + } + oToFolder.messageCountAll(oToFolder.messageCountAll() + aUidForRemove.length); if (0 < iUnseenCount) { diff --git a/dev/Styles/Ui.less b/dev/Styles/Ui.less index 09afa1136..53a821a38 100644 --- a/dev/Styles/Ui.less +++ b/dev/Styles/Ui.less @@ -239,7 +239,6 @@ html.cssanimations { .e-spinner .bounce2 { animation-delay: -0.16s; } - } @keyframes bouncedelay { diff --git a/gulpfile.js b/gulpfile.js index dc22784a1..51d9eb205 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -110,7 +110,8 @@ cfg.paths.css = { 'vendors/fontastic/styles.css', 'vendors/jquery-nanoscroller/nanoscroller.css', 'vendors/jquery-letterfx/jquery-letterfx.min.css', - 'vendors/simple-pace/styles.css', + 'vendors/progress.js/minified/progressjs.min.css', + 'vendors/progress.js/minified/progressjs.rainloop.css', 'vendors/inputosaurus/inputosaurus.css', 'vendors/opentip/opentip.css', 'vendors/photoswipe/photoswipe.css', @@ -127,7 +128,8 @@ cfg.paths.js = { src: [ 'vendors/json2.min.js', 'vendors/labjs/LAB.min.js', - 'vendors/simple-pace/simple-pace-1.0.min.js', + 'vendors/modernizr.js', + 'vendors/progress.js/minified/progress.min.js', 'vendors/rl/rl-1.5.min.js' ] }, @@ -153,7 +155,6 @@ cfg.paths.js = { libs: { name: 'libs.js', src: [ - 'vendors/modernizr.js', 'vendors/underscore/1.6.0/underscore-min.js', 'vendors/jquery/jquery-1.11.2.min.js', 'vendors/jquery-ui/js/jquery-ui-1.10.3.custom.min.js', diff --git a/package.json b/package.json index cb36f1de7..78156e8cd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.9.1", - "release": "334", + "release": "335", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php index 96f9ac02d..bb9a2e419 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php @@ -126,7 +126,7 @@ class HtmlUtils { $aRemoveTags = array( 'link', 'base', 'meta', 'title', 'script', 'bgsound', 'keygen', 'source', - 'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio' + 'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio', 'area', 'map' ); if ($bClearStyleAndHead) @@ -755,7 +755,8 @@ class HtmlUtils if ('' !== $sTagNameLower && \in_array($sTagNameLower, array('svg', 'head', 'link', 'base', 'meta', 'title', 'style', 'x-script', 'script', 'bgsound', 'keygen', 'source', - 'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio'))) + 'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', + 'video', 'audio', 'area', 'map'))) { if (isset($oElement->parentNode)) { @@ -867,7 +868,8 @@ class HtmlUtils foreach (array( 'id', 'class', 'contenteditable', 'designmode', 'formaction', - 'data-bind', 'data-reactid', 'xmlns', 'srcset' + 'data-bind', 'data-reactid', 'xmlns', 'srcset', + 'data-x-skip-style' ) as $sAttr) { @$oElement->removeAttribute($sAttr); @@ -903,27 +905,50 @@ class HtmlUtils $sAlt = $oElement->hasAttribute('alt') ? \trim($oElement->getAttribute('alt')) : ''; - $sH = $oElement->hasAttribute('height') - ? \trim($oElement->getAttribute('height')) : ''; - - $sW = $oElement->hasAttribute('width') - ? \trim($oElement->getAttribute('width')) : ''; - - $sStyles = $oElement->hasAttribute('style') - ? \trim(\trim(\trim($oElement->getAttribute('style')), ';')) : ''; - - if ($oElement->hasAttribute('src')) + if ($oElement->hasAttribute('src') && '' === $sAlt) { - $aValues = array('4', '3', '2', '1', '0', '4px', '3px', '2px', '1px', '0px'); - if (('' === $sAlt && \in_array($sH, $aValues) && \in_array($sW, $aValues)) || - \preg_match('/(display:none|visibility:hidden)/i', \preg_replace('/[\s]+/', '', $sStyles))) + $aH = array( + 'email.microsoftemail.com/open', + 'github.com/notifications/beacon/', + 'mandrillapp.com/track/open', + 'list-manage.com/track/open' + ); + + $sH = $oElement->hasAttribute('height') + ? \trim($oElement->getAttribute('height')) : ''; + +// $sW = $oElement->hasAttribute('width') +// ? \trim($oElement->getAttribute('width')) : ''; + + $sStyles = $oElement->hasAttribute('style') + ? \preg_replace('/[\s]+/', '', \trim(\trim(\trim($oElement->getAttribute('style')), ';'))) : ''; + + $sSrc = \trim($oElement->getAttribute('src')); + + $bC = \in_array($sH, array('1', '0', '1px', '0px')) || + \preg_match('/(display:none|visibility:hidden|height:0|height:[01][a-z][a-z])/i', $sStyles); + + if (!$bC) { - if (isset($oElement->parentNode)) + $sSrcLower = \strtolower($sSrc); + foreach ($aH as $sLine) { - @$oElement->parentNode->removeChild($oElement); - continue; + if (false !== \strpos($sSrcLower, $sLine)) + { + $bC = true; + break; + } } } + + if ($bC) + { + $oElement->setAttribute('style', 'display:none'); + $oElement->setAttribute('data-x-skip-style', 'true'); + $oElement->setAttribute('data-x-hidden-src', $sSrc); + + $oElement->removeAttribute('src'); + } } } @@ -1002,12 +1027,14 @@ class HtmlUtils $oElement->setAttribute('style', (empty($sStyles) ? '' : $sStyles.'; ').\implode('; ', $aStyles)); } - if ($oElement->hasAttribute('style')) + if ($oElement->hasAttribute('style') && !$oElement->hasAttribute('data-x-skip-style')) { $oElement->setAttribute('style', \MailSo\Base\HtmlUtils::ClearStyle($oElement->getAttribute('style'), $oElement, $bHasExternals, $aFoundCIDs, $aContentLocationUrls, $aFoundedContentLocationUrls, $bDoNotReplaceExternalUrl, $fAdditionalExternalFilter)); } + + $oElement->removeAttribute('data-x-skip-style'); } $sResult = $oDom->saveHTML(); @@ -1023,7 +1050,7 @@ class HtmlUtils $sResult = '