mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-30 12:39:20 +03:00
Uploading and preparing the repository to the dev version.
Original unminified source code (dev folder - js, css, less) (fixes #6) Grunt build system Multiple identities correction (fixes #9) Compose html editor (fixes #12) New general settings - Loading Description New warning about default admin password Split general and login screen settings
This commit is contained in:
parent
afad45137e
commit
4cc2207513
846 changed files with 99453 additions and 2123 deletions
164
dev/Common/Base64.js
Normal file
164
dev/Common/Base64.js
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*jslint bitwise: true*/
|
||||
// Base64 encode / decode
|
||||
// http://www.webtoolkit.info/
|
||||
|
||||
Base64 = {
|
||||
|
||||
// private property
|
||||
_keyStr : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
||||
|
||||
// public method for urlsafe encoding
|
||||
urlsafe_encode : function (input) {
|
||||
return Base64.encode(input).replace(/[+]/g, '-').replace(/[\/]/g, '_').replace(/[=]/g, '.');
|
||||
},
|
||||
|
||||
// public method for encoding
|
||||
encode : function (input) {
|
||||
var
|
||||
output = '',
|
||||
chr1, chr2, chr3, enc1, enc2, enc3, enc4,
|
||||
i = 0
|
||||
;
|
||||
|
||||
input = Base64._utf8_encode(input);
|
||||
|
||||
while (i < input.length)
|
||||
{
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2))
|
||||
{
|
||||
enc3 = enc4 = 64;
|
||||
}
|
||||
else if (isNaN(chr3))
|
||||
{
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output = output +
|
||||
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
|
||||
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
// public method for decoding
|
||||
decode : function (input) {
|
||||
var
|
||||
output = '',
|
||||
chr1, chr2, chr3, enc1, enc2, enc3, enc4,
|
||||
i = 0
|
||||
;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
|
||||
|
||||
while (i < input.length)
|
||||
{
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 !== 64)
|
||||
{
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
|
||||
if (enc4 !== 64)
|
||||
{
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
}
|
||||
|
||||
return Base64._utf8_decode(output);
|
||||
},
|
||||
|
||||
// private method for UTF-8 encoding
|
||||
_utf8_encode : function (string) {
|
||||
|
||||
string = string.replace(/\r\n/g, "\n");
|
||||
|
||||
var
|
||||
utftext = '',
|
||||
n = 0,
|
||||
l = string.length,
|
||||
c = 0
|
||||
;
|
||||
|
||||
for (; n < l; n++) {
|
||||
|
||||
c = string.charCodeAt(n);
|
||||
|
||||
if (c < 128)
|
||||
{
|
||||
utftext += String.fromCharCode(c);
|
||||
}
|
||||
else if ((c > 127) && (c < 2048))
|
||||
{
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
}
|
||||
|
||||
return utftext;
|
||||
},
|
||||
|
||||
// private method for UTF-8 decoding
|
||||
_utf8_decode : function (utftext) {
|
||||
var
|
||||
string = '',
|
||||
i = 0,
|
||||
c = 0,
|
||||
c2 = 0,
|
||||
c3 = 0
|
||||
;
|
||||
|
||||
while ( i < utftext.length )
|
||||
{
|
||||
c = utftext.charCodeAt(i);
|
||||
|
||||
if (c < 128)
|
||||
{
|
||||
string += String.fromCharCode(c);
|
||||
i++;
|
||||
}
|
||||
else if((c > 191) && (c < 224))
|
||||
{
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
c3 = utftext.charCodeAt(i+2);
|
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
/*jslint bitwise: false*/
|
||||
101
dev/Common/Constants.js
Normal file
101
dev/Common/Constants.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
Consts.Defaults = {};
|
||||
Consts.Values = {};
|
||||
Consts.DataImages = {};
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.MessagesPerPage = 20;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {Array}
|
||||
*/
|
||||
Consts.Defaults.MessagesPerPageArray = [10, 20, 30, 50, 100/*, 150, 200, 300*/];
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.DefaultAjaxTimeout = 20000;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.SearchAjaxTimeout = 120000;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.SendMessageAjaxTimeout = 200000;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.SaveMessageAjaxTimeout = 200000;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
Consts.Values.UnuseOptionValue = '__UNUSE__';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
Consts.Values.GmailFolderName = '[Gmail]';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
Consts.Values.ClientSideCookieIndexName = 'rlcsc';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Values.ImapDefaulPort = 143;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Values.ImapDefaulSecurePort = 993;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Values.SmtpDefaulPort = 25;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Values.SmtpDefaulSecurePort = 465;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Values.iMessageBodyCacheLimit = 15;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
Consts.DataImages.UserDotPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2P8DwQACgAD/il4QJ8AAAAASUVORK5CYII=';
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
Consts.DataImages.TranspPic = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=';
|
||||
286
dev/Common/Enums.js
Normal file
286
dev/Common/Enums.js
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.StorageResultType = {
|
||||
'Success': 'success',
|
||||
'Abort': 'abort',
|
||||
'Error': 'error',
|
||||
'Unload': 'unload'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.State = {
|
||||
'Empty': 10,
|
||||
'Login': 20,
|
||||
'Auth': 30
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.StateType = {
|
||||
'Webmail': 0,
|
||||
'Admin': 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.FolderType = {
|
||||
'Inbox': 10,
|
||||
'SentItems': 11,
|
||||
'Draft': 12,
|
||||
'Trash': 13,
|
||||
'Spam': 14,
|
||||
'User': 99
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.LoginSignMeTypeAsString = {
|
||||
'DefaultOff': 'defaultoff',
|
||||
'DefaultOn': 'defaulton',
|
||||
'Unused': 'unused'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.LoginSignMeType = {
|
||||
'DefaultOff': 0,
|
||||
'DefaultOn': 1,
|
||||
'Unused': 2
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.ComposeType = {
|
||||
'Empty': 'empty',
|
||||
'Reply': 'reply',
|
||||
'ReplyAll': 'replyall',
|
||||
'Forward': 'forward',
|
||||
'ForwardAsAttachment': 'forward-as-attachment',
|
||||
'Draft': 'draft'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.UploadErrorCode = {
|
||||
'Normal': 0,
|
||||
'FileIsTooBig': 1,
|
||||
'FilePartiallyUploaded': 2,
|
||||
'FileNoUploaded': 3,
|
||||
'MissingTempFolder': 4,
|
||||
'FileOnSaveingError': 5,
|
||||
'FileType': 98,
|
||||
'Unknown': 99
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.SetSystemFoldersNotification = {
|
||||
'None': 0,
|
||||
'Sent': 1,
|
||||
'Draft': 2,
|
||||
'Spam': 3,
|
||||
'Trash': 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.ClientSideKeyName = {
|
||||
'FoldersLashHash': 0,
|
||||
'MessagesInboxLastHash': 1,
|
||||
'MailBoxListSize': 2,
|
||||
'ExpandedFolders': 3,
|
||||
'FolderListSize': 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.EventKeyCode = {
|
||||
'Backspace': 8,
|
||||
'Enter': 13,
|
||||
'Esc': 27,
|
||||
'PageUp': 33,
|
||||
'PageDown': 34,
|
||||
'Left': 37,
|
||||
'Right': 39,
|
||||
'Up': 38,
|
||||
'Down': 40,
|
||||
'End': 35,
|
||||
'Home': 36,
|
||||
'Insert': 45,
|
||||
'Delete': 46,
|
||||
'A': 65,
|
||||
'S': 83
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.MessageSetAction = {
|
||||
'SetSeen': 0,
|
||||
'UnsetSeen': 1,
|
||||
'SetFlag': 2,
|
||||
'UnsetFlag': 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.MessageSelectAction = {
|
||||
'All': 0,
|
||||
'None': 1,
|
||||
'Invert': 2,
|
||||
'Unseen': 3,
|
||||
'Seen': 4,
|
||||
'Flagged': 5,
|
||||
'Unflagged': 6
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.DesktopNotifications = {
|
||||
'Allowed': 0,
|
||||
'NotAllowed': 1,
|
||||
'Denied': 2,
|
||||
'NotSupported': 9
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.MessagePriority = {
|
||||
'Low': 5,
|
||||
'Normal': 3,
|
||||
'High': 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.EditorDefaultType = {
|
||||
'Html': 'Html',
|
||||
'Plain': 'Plain'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.CustomThemeType = {
|
||||
'Light': 'Light',
|
||||
'Dark': 'Dark'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.ServerSecure = {
|
||||
'None': 0,
|
||||
'SSL': 1,
|
||||
'TLS': 2
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.SearchDateType = {
|
||||
'All': -1,
|
||||
'Days3': 3,
|
||||
'Days7': 7,
|
||||
'Month': 30
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.EmailType = {
|
||||
'Defailt': 0,
|
||||
'Facebook': 1,
|
||||
'Google': 2
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.SaveSettingsStep = {
|
||||
'Animate': -2,
|
||||
'Idle': -1,
|
||||
'TrueResult': 1,
|
||||
'FalseResult': 0
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.InterfaceAnimation = {
|
||||
'None': 'None',
|
||||
'Normal': 'Normal',
|
||||
'Full': 'Full'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Enums.Notification = {
|
||||
'InvalidToken': 101,
|
||||
'AuthError': 102,
|
||||
'AccessError': 103,
|
||||
'ConnectionError': 104,
|
||||
'CaptchaError': 105,
|
||||
'SocialFacebookLoginAccessDisable': 106,
|
||||
'SocialTwitterLoginAccessDisable': 107,
|
||||
'SocialGoogleLoginAccessDisable': 108,
|
||||
'DomainNotAllowed': 109,
|
||||
'AccountNotAllowed': 110,
|
||||
|
||||
'CantGetMessageList': 201,
|
||||
'CantGetMessage': 202,
|
||||
'CantDeleteMessage': 203,
|
||||
'CantMoveMessage': 204,
|
||||
|
||||
'CantSaveMessage': 301,
|
||||
'CantSendMessage': 302,
|
||||
'InvalidRecipients': 303,
|
||||
|
||||
'CantCreateFolder': 400,
|
||||
'CantRenameFolder': 401,
|
||||
'CantDeleteFolder': 402,
|
||||
'CantSubscribeFolder': 403,
|
||||
'CantUnsubscribeFolder': 404,
|
||||
'CantDeleteNonEmptyFolder': 405,
|
||||
|
||||
'CantSaveSettings': 501,
|
||||
'CantSavePluginSettings': 502,
|
||||
|
||||
'DomainAlreadyExists': 601,
|
||||
|
||||
'CantInstallPackage': 701,
|
||||
'CantDeletePackage': 702,
|
||||
'InvalidPluginPackage': 703,
|
||||
'UnsupportedPluginPackage': 704,
|
||||
|
||||
'LicensingServerIsUnavailable': 710,
|
||||
'LicensingExpired': 711,
|
||||
'LicensingBanned': 712,
|
||||
|
||||
'DemoSendMessageError': 750,
|
||||
|
||||
'AccountAlreadyExists': 801,
|
||||
|
||||
'MailServerError': 901,
|
||||
'UnknownNotification': 999,
|
||||
'UnknownError': 999
|
||||
};
|
||||
67
dev/Common/Globals.js
Normal file
67
dev/Common/Globals.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
Globals.now = (new Date()).getTime();
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
Globals.minuteTick = ko.observable(true);
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
Globals.fiveMinuteTick = ko.observable(true);
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
Globals.langChangeTick = ko.observable(true);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
Globals.iAjaxErrorCount = 0;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
Globals.iTokenErrorCount = 0;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
Globals.iMessageBodyCacheCount = 0;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
Globals.bUnload = false;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
Globals.sUserAgent = (navigator.userAgent || '').toLowerCase();
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
Globals.bIsiOSDevice = -1 < Globals.sUserAgent.indexOf('iphone') || -1 < Globals.sUserAgent.indexOf('ipod') || -1 < Globals.sUserAgent.indexOf('ipad');
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
Globals.bIsAndroidDevice = -1 < Globals.sUserAgent.indexOf('android');
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
Globals.bMobileDevice = Globals.bIsiOSDevice || Globals.bIsAndroidDevice;
|
||||
|
||||
Globals.bDisableNanoScroll = Globals.bMobileDevice;
|
||||
|
||||
Globals.bAnimationSupported = !Globals.bMobileDevice && $html.hasClass('csstransitions');
|
||||
|
||||
Globals.sAnimationType = '';
|
||||
897
dev/Common/HtmlEditor.js
Normal file
897
dev/Common/HtmlEditor.js
Normal file
|
|
@ -0,0 +1,897 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function HtmlEditor(oTextAreaElem, oHtmlAreaElem, oToolBarElement, oOptions)
|
||||
{
|
||||
var
|
||||
oDefOptions = {
|
||||
'DisableHtml': false,
|
||||
'onSwitch': false,
|
||||
'LangSwitcherConferm': 'EDITOR_TEXT_SWITCHER_CONFIRM',
|
||||
'LangSwitcherTextLabel': 'EDITOR_SWITCHER_TEXT_LABEL',
|
||||
'LangSwitcherHtmlLabel': 'EDITOR_SWITCHER_HTML_LABEL'
|
||||
}
|
||||
;
|
||||
|
||||
this.bIe = !!/msie/.test(navigator.userAgent.toLowerCase());
|
||||
|
||||
oOptions = $.extend(oDefOptions, Utils.isUnd(oOptions) ? {} : oOptions);
|
||||
|
||||
this.oOptions = oOptions;
|
||||
this.bOnlyPlain = !!this.oOptions.DisableHtml;
|
||||
this.fOnSwitch = this.oOptions.onSwitch;
|
||||
|
||||
this.textarea = $(oTextAreaElem).empty().addClass('editorTextArea');
|
||||
this.htmlarea = $(oHtmlAreaElem).empty().addClass('editorHtmlArea').prop('contentEditable', 'true');
|
||||
this.toolbar = $(oToolBarElement).empty().addClass('editorToolbar');
|
||||
|
||||
HtmlEditor.htmlInitEditor.apply(this);
|
||||
HtmlEditor.htmlInitToolbar.apply(this);
|
||||
HtmlEditor.htmlAttachEditorEvents.apply(this);
|
||||
|
||||
if (this.bOnlyPlain)
|
||||
{
|
||||
this.toolbar.hide();
|
||||
// this.switchToPlain(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} mA
|
||||
* @param {string} mB
|
||||
* @param {string} mC
|
||||
*/
|
||||
HtmlEditor.prototype.initLanguage = function (mA, mB, mC)
|
||||
{
|
||||
this.oOptions.LangSwitcherConferm = mA;
|
||||
this.oOptions.LangSwitcherTextLabel = mB;
|
||||
this.oOptions.LangSwitcherHtmlLabel = mC;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} mA
|
||||
* @param {boolean=} mB
|
||||
* @param {string=} mC
|
||||
*/
|
||||
HtmlEditor.prototype.execCom = function (mA, mB, mC)
|
||||
{
|
||||
if (window.document)
|
||||
{
|
||||
window.document.execCommand(mA, mB || false, mC || null);
|
||||
this.updateTextArea();
|
||||
}
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.getEditorSelection = function () {
|
||||
var mSelection = null;
|
||||
if (window.getSelection)
|
||||
{
|
||||
mSelection = window.getSelection();
|
||||
}
|
||||
else if (window.document.getSelection)
|
||||
{
|
||||
mSelection = window.document.getSelection();
|
||||
}
|
||||
else if (window.document.selection)
|
||||
{
|
||||
mSelection = window.document.selection;
|
||||
}
|
||||
return mSelection;
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.getEditorRange = function () {
|
||||
var oSelection = this.getEditorSelection();
|
||||
if (!oSelection || 0 === oSelection.rangeCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return (oSelection.getRangeAt) ? oSelection.getRangeAt(0) : oSelection.createRange();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} mA
|
||||
* @param {boolean=} mB
|
||||
* @param {string=} mC
|
||||
*/
|
||||
HtmlEditor.prototype.ec = function (mA, mB, mC)
|
||||
{
|
||||
this.execCom(mA, mB, mC);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} iHeading
|
||||
*/
|
||||
HtmlEditor.prototype.heading = function (iHeading)
|
||||
{
|
||||
this.ec('formatblock', false, (this.bIe) ? 'Heading ' + iHeading : 'h' + iHeading);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sSrc
|
||||
*/
|
||||
HtmlEditor.prototype.insertImage = function (sSrc)
|
||||
{
|
||||
if (this.isHtml() && !this.bOnlyPlain)
|
||||
{
|
||||
this.htmlarea.focus();
|
||||
this.ec('insertImage', false, sSrc);
|
||||
}
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.focus = function ()
|
||||
{
|
||||
if (this.isHtml() && !this.bOnlyPlain)
|
||||
{
|
||||
this.htmlarea.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.textarea.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sType
|
||||
* @param {string} sColor
|
||||
*/
|
||||
HtmlEditor.prototype.setcolor = function (sType, sColor)
|
||||
{
|
||||
var
|
||||
oRange = null,
|
||||
sCmd = ''
|
||||
;
|
||||
|
||||
if (this.bIe && !document['addEventListener'])
|
||||
{
|
||||
oRange = this.getEditorRange();
|
||||
if (oRange)
|
||||
{
|
||||
oRange.execCommand(('forecolor' === sType) ? 'ForeColor' : 'BackColor', false, sColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.bIe)
|
||||
{
|
||||
sCmd = ('forecolor' === sType) ? 'ForeColor' : 'BackColor';
|
||||
}
|
||||
else
|
||||
{
|
||||
sCmd = ('forecolor' === sType) ? 'foreColor' : 'backColor';
|
||||
}
|
||||
|
||||
this.ec(sCmd, false, sColor);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
HtmlEditor.prototype.isHtml = function ()
|
||||
{
|
||||
return (true === this.bOnlyPlain) ? false : this.textarea.is(':hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.toHtmlString = function ()
|
||||
{
|
||||
return this.editor.innerHTML;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.toString = function ()
|
||||
{
|
||||
return this.editor.innerText;
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.updateTextArea = function ()
|
||||
{
|
||||
this.textarea.val(this.toHtmlString());
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.updateHtmlArea = function ()
|
||||
{
|
||||
this.editor.innerHTML = this.textarea.val();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sInnerText
|
||||
* @param {boolean} bIsHtml
|
||||
*/
|
||||
HtmlEditor.prototype.setRawText = function (sInnerText, bIsHtml)
|
||||
{
|
||||
if (bIsHtml && !this.bOnlyPlain)
|
||||
{
|
||||
if (!this.isHtml())
|
||||
{
|
||||
this.textarea.val('');
|
||||
this.switchToHtml();
|
||||
}
|
||||
|
||||
this.textarea.val(sInnerText.toString());
|
||||
this.updateHtmlArea();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.textarea.val(sInnerText.toString());
|
||||
this.updateHtmlArea();
|
||||
this.switchToPlain(false);
|
||||
}
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.clear = function ()
|
||||
{
|
||||
this.textarea.val('');
|
||||
this.editor.innerHTML = '';
|
||||
|
||||
if (this.bOnlyPlain)
|
||||
{
|
||||
this.toolbar.hide();
|
||||
this.switchToPlain(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.switchToHtml();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.getTextForRequest = function ()
|
||||
{
|
||||
if (this.isHtml())
|
||||
{
|
||||
this.updateTextArea();
|
||||
return this.textarea.val();
|
||||
}
|
||||
|
||||
return this.textarea.val();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean=} bWrap
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.getTextFromHtml = function (bWrap)
|
||||
{
|
||||
var
|
||||
sText = '',
|
||||
sQuoteChar = '> ',
|
||||
|
||||
convertBlockquote = function () {
|
||||
if (arguments && 1 < arguments.length)
|
||||
{
|
||||
var sText = Utils.trim(arguments[1])
|
||||
.replace(/__bq__start__([\s\S\n\r]*)__bq__end__/gm, convertBlockquote)
|
||||
;
|
||||
|
||||
sText = '\n' + sQuoteChar + Utils.trim(sText).replace(/\n/gm, '\n' + sQuoteChar) + '\n>\n';
|
||||
|
||||
return sText.replace(/\n([> ]+)/gm, function () {
|
||||
return (arguments && 1 < arguments.length) ? '\n' + Utils.trim(arguments[1].replace(/[\s]/, '')) + ' ' : '';
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
convertDivs = function () {
|
||||
if (arguments && 1 < arguments.length)
|
||||
{
|
||||
var sText = Utils.trim(arguments[1]);
|
||||
if (0 < sText.length)
|
||||
{
|
||||
sText = sText.replace(/<div[^>]*>([\s\S]*)<\/div>/gmi, convertDivs);
|
||||
sText = '\n' + Utils.trim(sText) + '\n';
|
||||
}
|
||||
return sText;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
convertLinks = function () {
|
||||
if (arguments && 1 < arguments.length)
|
||||
{
|
||||
var
|
||||
sName = Utils.trim(arguments[1])
|
||||
// sHref = Utils.trim(arguments[0].replace(/<a [\s\S]*href[ ]?=[ ]?["']?([^"']+).+<\/a>/gmi, '$1'))
|
||||
;
|
||||
|
||||
return sName;
|
||||
// sName = (0 === Utils.trim(sName).length) ? '' : sName;
|
||||
// sHref = ('mailto:' === sHref.substr(0, 7)) ? '' : sHref;
|
||||
// sHref = ('http' === sHref.substr(0, 4)) ? sHref : '';
|
||||
// sHref = (sName === sHref) ? '' : sHref;
|
||||
// sHref = (0 < sHref.length) ? ' (' + sHref + ') ' : '';
|
||||
// return (0 < sName.length) ? sName + sHref : sName;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
;
|
||||
|
||||
bWrap = Utils.isUnd(bWrap) ? true : !!bWrap;
|
||||
|
||||
sText = this.toHtmlString()
|
||||
.replace(/[\s]+/gm, ' ')
|
||||
.replace(/<br\s?\/?>/gmi, '\n')
|
||||
.replace(/<\/h\d>/gi, '\n')
|
||||
.replace(/<\/p>/gi, '\n\n')
|
||||
.replace(/<\/li>/gi, '\n')
|
||||
.replace(/<\/td>/gi, '\n')
|
||||
.replace(/<\/tr>/gi, '\n')
|
||||
.replace(/<hr[^>]*>/gmi, '\n_______________________________\n\n')
|
||||
.replace(/<img [^>]*>/gmi, '')
|
||||
.replace(/<div[^>]*>([\s\S]*)<\/div>/gmi, convertDivs)
|
||||
.replace(/<blockquote[^>]*>/gmi, '\n__bq__start__\n')
|
||||
.replace(/<\/blockquote>/gmi, '\n__bq__end__\n')
|
||||
.replace(/<a [^>]*>([\s\S]*?)<\/a>/gmi, convertLinks)
|
||||
.replace(/ /gi, ' ')
|
||||
.replace(/<[^>]*>/gm, '')
|
||||
.replace(/>/gi, '>')
|
||||
.replace(/</gi, '<')
|
||||
.replace(/&/gi, '&')
|
||||
.replace(/&\w{2,6};/gi, '')
|
||||
;
|
||||
|
||||
return (bWrap ? Utils.splitPlainText(sText) : sText)
|
||||
.replace(/\n[ \t]+/gm, '\n')
|
||||
.replace(/[\n]{3,}/gm, '\n\n')
|
||||
.replace(/__bq__start__([\s\S]*)__bq__end__/gm, convertBlockquote)
|
||||
.replace(/__bq__start__/gm, '')
|
||||
.replace(/__bq__end__/gm, '')
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.getHtmlFromText = function ()
|
||||
{
|
||||
return Utils.convertPlainTextToHtml(this.textarea.val());
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.switchToggle = function ()
|
||||
{
|
||||
if (this.isHtml())
|
||||
{
|
||||
this.switchToPlain();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.switchToHtml();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean=} bWithConfirm
|
||||
*/
|
||||
HtmlEditor.prototype.switchToPlain = function (bWithConfirm)
|
||||
{
|
||||
bWithConfirm = Utils.isUnd(bWithConfirm) ? true : bWithConfirm;
|
||||
|
||||
var
|
||||
sText = this.getTextFromHtml(),
|
||||
fSwitch = _.bind(function (bValue) {
|
||||
|
||||
if (bValue)
|
||||
{
|
||||
this.toolbar.addClass('editorHideToolbar');
|
||||
$('.editorSwitcher', this.toolbar).text(this.switcherLinkText(false));
|
||||
|
||||
this.textarea.val(sText);
|
||||
this.textarea.show();
|
||||
// this.textarea.css({'display': ''});
|
||||
this.htmlarea.hide();
|
||||
if (this.fOnSwitch)
|
||||
{
|
||||
this.fOnSwitch(false);
|
||||
}
|
||||
}
|
||||
|
||||
}, this)
|
||||
;
|
||||
|
||||
if (!bWithConfirm || 0 === Utils.trim(sText).length)
|
||||
{
|
||||
fSwitch(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fSwitch(window.confirm(this.oOptions.LangSwitcherConferm));
|
||||
// Utils.dialogConfirm(this.oOptions.LangSwitcherConferm, fSwitch);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} bIsPlain
|
||||
* @return {string}
|
||||
*/
|
||||
HtmlEditor.prototype.switcherLinkText = function (bIsPlain)
|
||||
{
|
||||
return (bIsPlain) ? this.oOptions.LangSwitcherTextLabel : this.oOptions.LangSwitcherHtmlLabel;
|
||||
// return (bIsPlain) ? '« ' + 'EDITOR_TEXT_SWITCHER_PLAINT_TEXT' : 'EDITOR_TEXT_SWITCHER_RICH_FORMATTING' + ' »';
|
||||
};
|
||||
|
||||
HtmlEditor.prototype.switchToHtml = function ()
|
||||
{
|
||||
this.toolbar.removeClass('editorHideToolbar');
|
||||
$('.editorSwitcher', this.toolbar).text(this.switcherLinkText(true));
|
||||
|
||||
this.textarea.val(this.getHtmlFromText());
|
||||
this.updateHtmlArea();
|
||||
this.textarea.hide();
|
||||
this.htmlarea.show();
|
||||
// this.htmlarea.css({'display': ''});
|
||||
|
||||
if (this.fOnSwitch)
|
||||
{
|
||||
this.fOnSwitch(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
* @param {string} sTitle
|
||||
*/
|
||||
HtmlEditor.prototype.addButton = function (sName, sTitle)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
$('<div />').addClass('editorToolbarButtom').append($('<a tabindex="-1" href="javascript:void(0);"></a>').addClass(sName)).attr('title', sTitle).click(function (oEvent) {
|
||||
if (!Utils.isUnd(HtmlEditor.htmlFunctions[sName]))
|
||||
{
|
||||
HtmlEditor.htmlFunctions[sName].apply(self, [$(this), oEvent]);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.alert(sName);
|
||||
}
|
||||
}).appendTo(this.toolbar);
|
||||
};
|
||||
|
||||
HtmlEditor.htmlInitToolbar = function ()
|
||||
{
|
||||
if (!this.bOnlyPlain)
|
||||
{
|
||||
// this.addButton('fontname', 'Select font');
|
||||
this.addButton('bold', 'Bold');
|
||||
this.addButton('italic', 'Italic');
|
||||
this.addButton('underline', 'Underline');
|
||||
this.addButton('strikethrough', 'Strikethrough');
|
||||
|
||||
this.addButton('removeformat', 'removeformat');
|
||||
|
||||
this.addButton('justifyleft', 'justifyleft');
|
||||
this.addButton('justifycenter', 'justifycenter');
|
||||
this.addButton('justifyright', 'justifyright');
|
||||
|
||||
this.addButton('horizontalrule', 'horizontalrule');
|
||||
|
||||
this.addButton('orderedlist', 'orderedlist');
|
||||
this.addButton('unorderedlist', 'unorderedlist');
|
||||
|
||||
this.addButton('indent', 'indent');
|
||||
this.addButton('outdent', 'outdent');
|
||||
|
||||
this.addButton('forecolor', 'forecolor');
|
||||
// this.addButton('backcolor', 'backcolor');
|
||||
|
||||
(function ($, that) {
|
||||
|
||||
$('<span />').addClass('editorSwitcher').text(that.switcherLinkText(true)).click(function () {
|
||||
that.switchToggle();
|
||||
}).appendTo(that.toolbar);
|
||||
|
||||
}($, this));
|
||||
}
|
||||
};
|
||||
|
||||
HtmlEditor.htmlInitEditor = function ()
|
||||
{
|
||||
this.editor = this.htmlarea[0];
|
||||
this.editor.innerHTML = this.textarea.val();
|
||||
};
|
||||
|
||||
HtmlEditor.htmlAttachEditorEvents = function ()
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
|
||||
fIsImage = function (oItem) {
|
||||
return oItem && oItem.type && 0 === oItem.type.indexOf('image/');
|
||||
},
|
||||
|
||||
// fUpdateHtmlArea = function () {
|
||||
// self.updateHtmlArea();
|
||||
// },
|
||||
//
|
||||
// fUpdateTextArea = function () {
|
||||
// self.updateTextArea();
|
||||
// },
|
||||
|
||||
fHandleFileSelect = function (oEvent) {
|
||||
|
||||
oEvent = (oEvent && oEvent.originalEvent ?
|
||||
oEvent.originalEvent : oEvent) || window.event;
|
||||
|
||||
if (oEvent)
|
||||
{
|
||||
oEvent.stopPropagation();
|
||||
oEvent.preventDefault();
|
||||
|
||||
var
|
||||
oReader = null,
|
||||
oFile = null,
|
||||
aFiles = (oEvent.files || (oEvent.dataTransfer ? oEvent.dataTransfer.files : null))
|
||||
;
|
||||
|
||||
if (aFiles && 1 === aFiles.length && fIsImage(aFiles[0]))
|
||||
{
|
||||
oFile = aFiles[0];
|
||||
|
||||
oReader = new window.FileReader();
|
||||
oReader.onload = (function (oLocalFile) {
|
||||
return function (oEvent) {
|
||||
self.insertImage(oEvent.target.result, oLocalFile.name);
|
||||
};
|
||||
}(oFile));
|
||||
|
||||
oReader.readAsDataURL(oFile);
|
||||
}
|
||||
}
|
||||
|
||||
self.htmlarea.removeClass('editorDragOver');
|
||||
},
|
||||
|
||||
fHandleDragLeave = function () {
|
||||
self.htmlarea.removeClass('editorDragOver');
|
||||
},
|
||||
|
||||
fHandleDragOver = function (oEvent) {
|
||||
oEvent.stopPropagation();
|
||||
oEvent.preventDefault();
|
||||
|
||||
self.htmlarea.addClass('editorDragOver');
|
||||
},
|
||||
|
||||
fHandlePaste = function (oEvent) {
|
||||
|
||||
var oClipboardData = oEvent && oEvent.clipboardData ? oEvent.clipboardData :
|
||||
(oEvent && oEvent.originalEvent && oEvent.originalEvent.clipboardData ? oEvent.originalEvent.clipboardData : null);
|
||||
|
||||
if (oClipboardData && oClipboardData.items)
|
||||
{
|
||||
_.each(oClipboardData.items, function (oItem) {
|
||||
if (fIsImage(oItem) && oItem['getAsFile']) {
|
||||
var oReader = null, oFile = oItem['getAsFile']();
|
||||
if (oFile)
|
||||
{
|
||||
oReader = new window.FileReader();
|
||||
oReader.onload = (function (oLocalFile) {
|
||||
return function (oEvent) {
|
||||
self.insertImage(oEvent.target.result, oLocalFile.name);
|
||||
};
|
||||
}(oFile));
|
||||
|
||||
oReader.readAsDataURL(oFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
if (!this.bOnlyPlain)
|
||||
{
|
||||
// this.textarea.on('click keyup keydown mousedown blur', fUpdateHtmlArea);
|
||||
// this.htmlarea.on('click keyup keydown mousedown blur', fUpdateTextArea);
|
||||
//
|
||||
if (window.File && window.FileReader && window.FileList)
|
||||
{
|
||||
this.htmlarea.bind('dragover', fHandleDragOver);
|
||||
this.htmlarea.bind('dragleave', fHandleDragLeave);
|
||||
this.htmlarea.bind('drop', fHandleFileSelect);
|
||||
this.htmlarea.bind('paste', fHandlePaste);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HtmlEditor.htmlColorPickerColors = (function () {
|
||||
|
||||
var
|
||||
aMaps = [],
|
||||
aColors = [],
|
||||
iIndex = 0,
|
||||
iIndexSub = 0,
|
||||
iIndexSubSub = 0,
|
||||
iLen = 0,
|
||||
sMap = ''
|
||||
;
|
||||
|
||||
for (iIndex = 0; iIndex < 256; iIndex += 85)
|
||||
{
|
||||
sMap = iIndex.toString(16);
|
||||
aMaps.push(1 === sMap.length ? '0' + sMap : sMap);
|
||||
}
|
||||
|
||||
iLen = aMaps.length;
|
||||
for (iIndex = 0; iIndex < iLen; iIndex++)
|
||||
{
|
||||
for (iIndexSub = 0; iIndexSub < iLen; iIndexSub++)
|
||||
{
|
||||
for (iIndexSubSub = 0; iIndexSubSub < iLen; iIndexSubSub++)
|
||||
{
|
||||
aColors.push('#' + aMaps[iIndex] + '' + aMaps[iIndexSub] + '' + aMaps[iIndexSubSub]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aColors;
|
||||
|
||||
}());
|
||||
|
||||
HtmlEditor.htmlFontPicker = (function () {
|
||||
|
||||
var
|
||||
jqDoc = $(window.document),
|
||||
bIsAppented = false,
|
||||
oFontPickerHolder = $('<div style="position: absolute;" class="editorFontStylePicker"><div class="editorFpFonts"></div></div>'),
|
||||
oFonts = oFontPickerHolder.find('.editorFpFonts'),
|
||||
fCurrentFunc = function () {}
|
||||
;
|
||||
|
||||
$.each(['Arial', 'Arial Black', 'Courier New', 'Tahoma', 'Times New Roman', 'Verdana'], function (iIndex, sFont) {
|
||||
oFonts.append(
|
||||
$('<a href="javascript:void(0);" tabindex="-1" class="editorFpFont" style="font-family: ' + sFont + ';">' + sFont + '</a>').click(function () {
|
||||
fCurrentFunc(sFont);
|
||||
})
|
||||
);
|
||||
oFonts.append('<br />');
|
||||
});
|
||||
|
||||
oFontPickerHolder.hide();
|
||||
|
||||
return function (oClickObject, fSelectFunc, oTollbar) {
|
||||
|
||||
if (!bIsAppented)
|
||||
{
|
||||
oFontPickerHolder.appendTo(oTollbar);
|
||||
bIsAppented = true;
|
||||
}
|
||||
|
||||
fCurrentFunc = fSelectFunc;
|
||||
|
||||
jqDoc.unbind('click.fpNamespace');
|
||||
window.setTimeout(function () {
|
||||
jqDoc.one('click.fpNamespace', function () {
|
||||
oFontPickerHolder.hide();
|
||||
});
|
||||
}, 500);
|
||||
|
||||
var oPos = $(oClickObject).position();
|
||||
oFontPickerHolder
|
||||
.css('top', (5 + oPos.top + $(oClickObject).height()) + 'px')
|
||||
.css('left', oPos.left + 'px')
|
||||
.show();
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
HtmlEditor.htmlColorPicker = (function () {
|
||||
|
||||
var
|
||||
jqDoc = $(window.document),
|
||||
bIsAppented = false,
|
||||
oColorPickerHolder = $('<div style="position: absolute;" class="editorColorPicker"><div class="editorCpColors"></div></div>'),
|
||||
oColors = oColorPickerHolder.find('.editorCpColors'),
|
||||
fCurrentFunc = function () {}
|
||||
;
|
||||
|
||||
$.each(HtmlEditor.htmlColorPickerColors, function (iIndex, sColor) {
|
||||
oColors.append('<a href="javascript:void(0);" tabindex="-1" class="editorCpColor" style="background-color: ' + sColor + ';"></a>');
|
||||
});
|
||||
|
||||
oColorPickerHolder.hide();
|
||||
|
||||
$('.editorCpColor', oColors).click(function (oEvent) {
|
||||
|
||||
var
|
||||
iIndex = 1,
|
||||
sSelectedColor = '#000000',
|
||||
sRgbString = $(oEvent.target).css('background-color'),
|
||||
aParts = sRgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
||||
;
|
||||
|
||||
if (aParts !== null)
|
||||
{
|
||||
delete (aParts[0]);
|
||||
for (; iIndex <= 3; ++iIndex)
|
||||
{
|
||||
aParts[iIndex] = Utils.pInt(aParts[iIndex]).toString(16);
|
||||
if (1 === aParts[iIndex].length)
|
||||
{
|
||||
aParts[iIndex] = '0' + aParts[iIndex];
|
||||
}
|
||||
}
|
||||
|
||||
sSelectedColor = '#' + aParts.join('');
|
||||
}
|
||||
else
|
||||
{
|
||||
sSelectedColor = sRgbString;
|
||||
}
|
||||
|
||||
fCurrentFunc(sSelectedColor);
|
||||
});
|
||||
|
||||
return function (oClickObject, fSelectFunc, oTollbar) {
|
||||
|
||||
if (!bIsAppented)
|
||||
{
|
||||
oColorPickerHolder.appendTo(oTollbar);
|
||||
bIsAppented = true;
|
||||
}
|
||||
|
||||
var oPos = $(oClickObject).position();
|
||||
fCurrentFunc = fSelectFunc;
|
||||
|
||||
jqDoc.unbind('click.cpNamespace');
|
||||
window.setTimeout(function () {
|
||||
jqDoc.one('click.cpNamespace', function () {
|
||||
oColorPickerHolder.hide();
|
||||
});
|
||||
}, 100);
|
||||
|
||||
oColorPickerHolder
|
||||
.css('top', (5 + oPos.top + $(oClickObject).height()) + 'px')
|
||||
.css('left', oPos.left + 'px')
|
||||
.show();
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
/* ----------- */
|
||||
|
||||
HtmlEditor.htmlFunctions = {
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'bold': function ()
|
||||
{
|
||||
this.ec('bold');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'italic': function ()
|
||||
{
|
||||
this.ec('italic');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'underline': function ()
|
||||
{
|
||||
this.ec('underline');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'strikethrough': function ()
|
||||
{
|
||||
this.ec('strikethrough');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'indent': function ()
|
||||
{
|
||||
this.ec('indent');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'outdent': function ()
|
||||
{
|
||||
this.ec('outdent');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'justifyleft': function ()
|
||||
{
|
||||
this.ec('justifyLeft');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'justifycenter': function ()
|
||||
{
|
||||
this.ec('justifyCenter');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'justifyright': function ()
|
||||
{
|
||||
this.ec('justifyRight');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'horizontalrule': function ()
|
||||
{
|
||||
this.ec('insertHorizontalRule', false, 'ht');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'removeformat': function ()
|
||||
{
|
||||
this.ec('removeFormat');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'orderedlist': function ()
|
||||
{
|
||||
this.ec('insertorderedlist');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'unorderedlist': function ()
|
||||
{
|
||||
this.ec('insertunorderedlist');
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'forecolor': function (oClickObject)
|
||||
{
|
||||
HtmlEditor.htmlColorPicker(oClickObject, _.bind(function (sValue) {
|
||||
this.setcolor('forecolor', sValue);
|
||||
}, this), this.toolbar);
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'backcolor': function (oClickObject)
|
||||
{
|
||||
HtmlEditor.htmlColorPicker(oClickObject, _.bind(function (sValue) {
|
||||
this.setcolor('backcolor', sValue);
|
||||
}, this), this.toolbar);
|
||||
},
|
||||
|
||||
/**
|
||||
* @this {HtmlEditor}
|
||||
*/
|
||||
'fontname': function (oClickObject)
|
||||
{
|
||||
HtmlEditor.htmlFontPicker(oClickObject, _.bind(function (sValue) {
|
||||
this.ec('fontname', false, sValue);
|
||||
}, this), this.toolbar);
|
||||
}
|
||||
};
|
||||
787
dev/Common/Knockout.js
Normal file
787
dev/Common/Knockout.js
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
ko.bindingHandlers.tooltip = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
if (!Globals.bMobileDevice)
|
||||
{
|
||||
var
|
||||
sClass = $(oElement).data('tooltip-class') || '',
|
||||
sPlacement = $(oElement).data('tooltip-placement') || 'top'
|
||||
;
|
||||
|
||||
$(oElement).tooltip({
|
||||
'delay': {
|
||||
'show': 500,
|
||||
'hide': 100
|
||||
},
|
||||
'html': true,
|
||||
'placement': sPlacement,
|
||||
'trigger': 'hover',
|
||||
'title': function () {
|
||||
return '<span class="tooltip-class ' + sClass + '">' +
|
||||
Utils.i18n(ko.utils.unwrapObservable(fValueAccessor())) + '</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.tooltip2 = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
var
|
||||
sClass = $(oElement).data('tooltip-class') || '',
|
||||
sPlacement = $(oElement).data('tooltip-placement') || 'top'
|
||||
;
|
||||
$(oElement).tooltip({
|
||||
'delay': {
|
||||
'show': 500,
|
||||
'hide': 100
|
||||
},
|
||||
'html': true,
|
||||
'placement': sPlacement,
|
||||
'title': function () {
|
||||
return '<span class="tooltip-class ' + sClass + '">' + fValueAccessor()() + '</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.dropdown = {
|
||||
'init': function (oElement) {
|
||||
$(oElement).closest('.dropdown').on('click', '.e-item', function () {
|
||||
$(oElement).dropdown('toggle');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.popover = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
$(oElement).popover(ko.utils.unwrapObservable(fValueAccessor()));
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.resizecrop = {
|
||||
'init': function (oElement) {
|
||||
$(oElement).addClass('resizecrop').resizecrop({
|
||||
'width': '100',
|
||||
'height': '100',
|
||||
'wrapperCSS': {
|
||||
'border-radius': '10px'
|
||||
}
|
||||
});
|
||||
},
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
fValueAccessor()();
|
||||
$(oElement).resizecrop({
|
||||
'width': '100',
|
||||
'height': '100'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onEnter = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keypress', function (oEvent) {
|
||||
if (oEvent && 13 === window.parseInt(oEvent.keyCode, 10))
|
||||
{
|
||||
$(oElement).trigger('change');
|
||||
fValueAccessor().call(oViewModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.onEsc = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
$(oElement).on('keypress', function (oEvent) {
|
||||
if (oEvent && 27 === window.parseInt(oEvent.keyCode, 10))
|
||||
{
|
||||
$(oElement).trigger('change');
|
||||
fValueAccessor().call(oViewModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.modal = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
$(oElement).modal({
|
||||
'keyboard': false,
|
||||
'show': ko.utils.unwrapObservable(fValueAccessor())
|
||||
}).on('hidden', function () {
|
||||
fValueAccessor()(false);
|
||||
});
|
||||
},
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
var bValue = ko.utils.unwrapObservable(fValueAccessor());
|
||||
$(oElement).modal(bValue ? 'show' : 'hide');
|
||||
|
||||
_.delay(function () {
|
||||
$(oElement).toggleClass('popup-active', bValue);
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.i18nInit = {
|
||||
'init': function (oElement) {
|
||||
Utils.i18nToNode(oElement);
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.i18nUpdate = {
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
ko.utils.unwrapObservable(fValueAccessor());
|
||||
Utils.i18nToNode(oElement);
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.link = {
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
$(oElement).attr('href', ko.utils.unwrapObservable(fValueAccessor()));
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.title = {
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
$(oElement).attr('title', ko.utils.unwrapObservable(fValueAccessor()));
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.textF = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
$(oElement).text(ko.utils.unwrapObservable(fValueAccessor()));
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.initDom = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
fValueAccessor()(oElement);
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.initResizeTrigger = {
|
||||
'init': function (oElement, fValueAccessor) {
|
||||
var aValues = ko.utils.unwrapObservable(fValueAccessor());
|
||||
$(oElement).css({
|
||||
'height': aValues[1],
|
||||
'min-height': aValues[1]
|
||||
});
|
||||
},
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
var
|
||||
aValues = ko.utils.unwrapObservable(fValueAccessor()),
|
||||
iValue = Utils.pInt(aValues[1]),
|
||||
iSize = 0,
|
||||
iOffset = $(oElement).offset().top
|
||||
;
|
||||
|
||||
if (0 < iOffset)
|
||||
{
|
||||
iOffset += Utils.pInt(aValues[2]);
|
||||
iSize = $window.height() - iOffset;
|
||||
|
||||
if (iValue < iSize)
|
||||
{
|
||||
iValue = iSize;
|
||||
}
|
||||
|
||||
$(oElement).css({
|
||||
'height': iValue,
|
||||
'min-height': iValue
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.appendDom = {
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
$(oElement).hide().empty().append(ko.utils.unwrapObservable(fValueAccessor())).show();
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.draggable = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor) {
|
||||
|
||||
if (!Globals.bMobileDevice)
|
||||
{
|
||||
var
|
||||
iTriggerZone = 100,
|
||||
iScrollSpeed = 3,
|
||||
fAllValueFunc = fAllBindingsAccessor(),
|
||||
sDroppableSelector = fAllValueFunc && fAllValueFunc['droppableSelector'] ? fAllValueFunc['droppableSelector'] : '',
|
||||
oConf = {
|
||||
'distance': 20,
|
||||
'handle': '.dragHandle',
|
||||
'cursorAt': {'top': 22, 'left': 3},
|
||||
'refreshPositions': true,
|
||||
'scroll': true
|
||||
}
|
||||
;
|
||||
|
||||
if (sDroppableSelector)
|
||||
{
|
||||
oConf['drag'] = function (oEvent) {
|
||||
|
||||
$(sDroppableSelector).each(function () {
|
||||
var
|
||||
moveUp = null,
|
||||
moveDown = null,
|
||||
$this = $(this),
|
||||
oOffset = $this.offset(),
|
||||
bottomPos = oOffset.top + $this.height()
|
||||
;
|
||||
|
||||
window.clearInterval($this.data('timerScroll'));
|
||||
$this.data('timerScroll', false);
|
||||
|
||||
if (oEvent.pageX >= oOffset.left && oEvent.pageX <= oOffset.left + $this.width())
|
||||
{
|
||||
if (oEvent.pageY >= bottomPos - iTriggerZone && oEvent.pageY <= bottomPos)
|
||||
{
|
||||
moveUp = function() {
|
||||
$this.scrollTop($this.scrollTop() + iScrollSpeed);
|
||||
Utils.windowResize();
|
||||
};
|
||||
|
||||
$this.data('timerScroll', window.setInterval(moveUp, 10));
|
||||
moveUp();
|
||||
}
|
||||
|
||||
if(oEvent.pageY >= oOffset.top && oEvent.pageY <= oOffset.top + iTriggerZone)
|
||||
{
|
||||
moveDown = function() {
|
||||
$this.scrollTop($this.scrollTop() - iScrollSpeed);
|
||||
Utils.windowResize();
|
||||
};
|
||||
|
||||
$this.data('timerScroll', window.setInterval(moveDown, 10));
|
||||
moveDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
oConf['stop'] = function() {
|
||||
$(sDroppableSelector).each(function () {
|
||||
window.clearInterval($(this).data('timerScroll'));
|
||||
$(this).data('timerScroll', false);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
oConf['helper'] = function (oEvent) {
|
||||
return fValueAccessor()(oEvent && oEvent.target ? ko.dataFor(oEvent.target) : null);
|
||||
};
|
||||
|
||||
$(oElement).draggable(oConf).on('mousedown', function () {
|
||||
Utils.removeInFocus();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.droppable = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor) {
|
||||
|
||||
if (!Globals.bMobileDevice)
|
||||
{
|
||||
var
|
||||
fValueFunc = fValueAccessor(),
|
||||
fAllValueFunc = fAllBindingsAccessor(),
|
||||
fOverCallback = fAllValueFunc && fAllValueFunc['droppableOver'] ? fAllValueFunc['droppableOver'] : null,
|
||||
fOutCallback = fAllValueFunc && fAllValueFunc['droppableOut'] ? fAllValueFunc['droppableOut'] : null,
|
||||
oConf = {
|
||||
'tolerance': 'pointer',
|
||||
'hoverClass': 'droppableHover'
|
||||
}
|
||||
;
|
||||
|
||||
if (fValueFunc)
|
||||
{
|
||||
oConf['drop'] = function (oEvent, oUi) {
|
||||
fValueFunc(oEvent, oUi);
|
||||
};
|
||||
|
||||
if (fOverCallback)
|
||||
{
|
||||
oConf['over'] = function (oEvent, oUi) {
|
||||
fOverCallback(oEvent, oUi);
|
||||
};
|
||||
}
|
||||
|
||||
if (fOutCallback)
|
||||
{
|
||||
oConf['out'] = function (oEvent, oUi) {
|
||||
fOutCallback(oEvent, oUi);
|
||||
};
|
||||
}
|
||||
|
||||
$(oElement).droppable(oConf);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.nano = {
|
||||
'init': function (oElement) {
|
||||
if (!Globals.bDisableNanoScroll)
|
||||
{
|
||||
$(oElement)
|
||||
.addClass('nano')
|
||||
.nanoScroller({
|
||||
'iOSNativeScrolling': false,
|
||||
'preventPageScrolling': true
|
||||
})
|
||||
;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.saveTrigger1 = {
|
||||
'init': function (oElement) {
|
||||
|
||||
var $oEl = $(oElement);
|
||||
|
||||
$oEl.data('save-trigger-type', $oEl.is('input[type=text]') ? 'input' : 'custom');
|
||||
|
||||
if ('custom' === $oEl.data('save-trigger-type'))
|
||||
{
|
||||
$oEl.append(
|
||||
' <i class="icon-spinner-2 animated"></i><i class="icon-remove error"></i><i class="icon-ok success"></i>'
|
||||
).addClass('settings-saved-trigger');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEl.addClass('settings-saved-trigger-input');
|
||||
}
|
||||
},
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
var
|
||||
mValue = ko.utils.unwrapObservable(fValueAccessor()),
|
||||
$oEl = $(oElement),
|
||||
bCustom = 'custom' === $oEl.data('save-trigger-type'),
|
||||
sSuffix = bCustom ? '' : '-input'
|
||||
;
|
||||
|
||||
switch (mValue.toString())
|
||||
{
|
||||
case '1':
|
||||
$oEl
|
||||
.find('.sst-animated' + sSuffix + ',.sst-error' + sSuffix).hide().removeClass('sst-visible' + sSuffix)
|
||||
.end()
|
||||
.find('.sst-success' + sSuffix).show().addClass('sst-visible' + sSuffix)
|
||||
;
|
||||
break;
|
||||
case '0':
|
||||
$oEl
|
||||
.find('.sst-animated' + sSuffix + ',.sst-success' + sSuffix).hide().removeClass('sst-visible' + sSuffix)
|
||||
.end()
|
||||
.find('.sst-error' + sSuffix).show().addClass('sst-visible' + sSuffix)
|
||||
;
|
||||
break;
|
||||
case '-2':
|
||||
$oEl
|
||||
.find('.sst-error' + sSuffix + ',.sst-success' + sSuffix).hide().removeClass('sst-visible' + sSuffix)
|
||||
.end()
|
||||
.find('.sst-animated' + sSuffix).show().addClass('sst-visible' + sSuffix)
|
||||
;
|
||||
break;
|
||||
default:
|
||||
$oEl
|
||||
.find('.sst-animated' + sSuffix).hide()
|
||||
.end()
|
||||
.find('.sst-error' + sSuffix + ',.sst-success' + sSuffix).removeClass('sst-visible' + sSuffix)
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.saveTrigger = {
|
||||
'init': function (oElement) {
|
||||
|
||||
var $oEl = $(oElement);
|
||||
|
||||
$oEl.data('save-trigger-type', $oEl.is('input[type=text],select,textarea') ? 'input' : 'custom');
|
||||
|
||||
if ('custom' === $oEl.data('save-trigger-type'))
|
||||
{
|
||||
$oEl.append(
|
||||
' <i class="icon-spinner-2 animated"></i><i class="icon-remove error"></i><i class="icon-ok success"></i>'
|
||||
).addClass('settings-saved-trigger');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEl.addClass('settings-saved-trigger-input');
|
||||
}
|
||||
},
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
var
|
||||
mValue = ko.utils.unwrapObservable(fValueAccessor()),
|
||||
$oEl = $(oElement)
|
||||
;
|
||||
|
||||
if ('custom' === $oEl.data('save-trigger-type'))
|
||||
{
|
||||
switch (mValue.toString())
|
||||
{
|
||||
case '1':
|
||||
$oEl
|
||||
.find('.animated,.error').hide().removeClass('visible')
|
||||
.end()
|
||||
.find('.success').show().addClass('visible')
|
||||
;
|
||||
break;
|
||||
case '0':
|
||||
$oEl
|
||||
.find('.animated,.success').hide().removeClass('visible')
|
||||
.end()
|
||||
.find('.error').show().addClass('visible')
|
||||
;
|
||||
break;
|
||||
case '-2':
|
||||
$oEl
|
||||
.find('.error,.success').hide().removeClass('visible')
|
||||
.end()
|
||||
.find('.animated').show().addClass('visible')
|
||||
;
|
||||
break;
|
||||
default:
|
||||
$oEl
|
||||
.find('.animated').hide()
|
||||
.end()
|
||||
.find('.error,.success').removeClass('visible')
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (mValue.toString())
|
||||
{
|
||||
case '1':
|
||||
$oEl.addClass('success').removeClass('error');
|
||||
break;
|
||||
case '0':
|
||||
$oEl.addClass('error').removeClass('success');
|
||||
break;
|
||||
case '-2':
|
||||
// $oEl;
|
||||
break;
|
||||
default:
|
||||
$oEl.removeClass('error success');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.select2 = {
|
||||
'init': function(oElement, fValueAccessor) {
|
||||
|
||||
var
|
||||
iTimer = 0,
|
||||
iTimeout = 100,
|
||||
oMatch = null,
|
||||
oReg = new window.RegExp(/[a-zA-Z0-9\.\-_]+@[a-zA-Z0-9\.\-_]+/),
|
||||
oReg2 = new window.RegExp(/(.+) [<]?([^\s<@]+@[a-zA-Z0-9\.\-_]+)[>]?/),
|
||||
sEmptyTranslateFunction = function () {
|
||||
return '';
|
||||
},
|
||||
/**
|
||||
* @param {{term:string, callback:Function, matcher:Function}} oCall
|
||||
*/
|
||||
fLazyAutocomplete = function (oCall) {
|
||||
|
||||
RL.getAutocomplete(oCall['term'], oCall['page'], function (aData, bMore) {
|
||||
oCall.callback({
|
||||
'more': !!bMore,
|
||||
'results': _.map(aData, function (oEmailItem) {
|
||||
var sName = oEmailItem.toLine(false);
|
||||
return {
|
||||
'id': sName,
|
||||
'text': sName,
|
||||
'c': oEmailItem
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
$(oElement).addClass('ko-select2').select2({
|
||||
|
||||
/**
|
||||
* @param {{term:string, callback:Function, matcher:Function}} oCall
|
||||
*/
|
||||
'query': function (oCall) {
|
||||
if (!oCall)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if (RL.isLocalAutocomplete && false)
|
||||
// {
|
||||
// fLazyAutocomplete(oCall);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
if (0 === iTimer)
|
||||
{
|
||||
fLazyAutocomplete(oCall);
|
||||
iTimer = window.setTimeout(Utils.emptyFunction, iTimeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.clearInterval(iTimer);
|
||||
iTimer = window.setTimeout(function () {
|
||||
fLazyAutocomplete(oCall);
|
||||
}, iTimeout);
|
||||
}
|
||||
// }
|
||||
},
|
||||
'formatSelection': function (oItem, oContainer) {
|
||||
var sR = oItem && oItem.c ? oItem.c.select2Selection(oContainer) : oItem.text;
|
||||
if (null !== sR)
|
||||
{
|
||||
return sR;
|
||||
}
|
||||
},
|
||||
'formatResult': function (oItem, oContainer, oQuery, fEscapeMarkup) {
|
||||
var sR = oItem && oItem.c ? oItem.c.select2Result(oContainer) : '';
|
||||
return '' === sR ? fEscapeMarkup(oItem.text) : sR;
|
||||
},
|
||||
'createSearchChoice': function (sTerm, aList) {
|
||||
return 0 === aList.length && oReg.test(sTerm) ? {
|
||||
'id': sTerm,
|
||||
'text': sTerm
|
||||
} : null;
|
||||
},
|
||||
'formatNoMatches': sEmptyTranslateFunction,
|
||||
'formatSearching': function () {
|
||||
return Utils.i18n('SUGGESTIONS/SEARCHING_DESC');
|
||||
},
|
||||
'formatInputTooShort': sEmptyTranslateFunction,
|
||||
'formatSelectionTooBig': sEmptyTranslateFunction,
|
||||
'multiple': true,
|
||||
'tokenSeparators': [',', ';'],
|
||||
'minimumInputLength': 2,
|
||||
'selectOnBlur': false,
|
||||
'closeOnSelect': true,
|
||||
'openOnEnter': false
|
||||
});
|
||||
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(oElement, function() {
|
||||
$(oElement).select2('destroy');
|
||||
});
|
||||
|
||||
$(oElement).on('change', function () {
|
||||
|
||||
var
|
||||
aTags = $(this).select2('data'),
|
||||
iIndex = 0,
|
||||
iLen = aTags.length,
|
||||
oItem = null,
|
||||
aResult = []
|
||||
;
|
||||
|
||||
for (; iIndex < iLen; iIndex++)
|
||||
{
|
||||
oItem = aTags[iIndex];
|
||||
if (oItem && oItem.id)
|
||||
{
|
||||
if (!oItem.c)
|
||||
{
|
||||
oItem.c = new EmailModel();
|
||||
oMatch = oReg2.exec(Utils.trim(oItem.id));
|
||||
if (oMatch && !Utils.isUnd(oMatch[2]))
|
||||
{
|
||||
oItem.c.name = oMatch[1];
|
||||
oItem.c.email = oMatch[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
oItem.c.email = oItem.id;
|
||||
}
|
||||
}
|
||||
|
||||
aResult.push(oItem.c);
|
||||
}
|
||||
}
|
||||
|
||||
fValueAccessor()(aResult);
|
||||
});
|
||||
},
|
||||
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
|
||||
var
|
||||
aTags = ko.utils.unwrapObservable(fValueAccessor()),
|
||||
iIndex = 0,
|
||||
iLen = aTags.length,
|
||||
oItem = null,
|
||||
sName = '',
|
||||
aResult = []
|
||||
;
|
||||
|
||||
for (; iIndex < iLen; iIndex++)
|
||||
{
|
||||
oItem = aTags[iIndex];
|
||||
sName = oItem.toLine(false);
|
||||
|
||||
aResult.push({
|
||||
'id': sName,
|
||||
'text': sName,
|
||||
'c': oItem
|
||||
});
|
||||
}
|
||||
|
||||
$(oElement).select2('data', aResult);
|
||||
}
|
||||
};
|
||||
|
||||
ko.bindingHandlers.command = {
|
||||
'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) {
|
||||
var
|
||||
jqElement = $(oElement),
|
||||
oCommand = fValueAccessor()
|
||||
;
|
||||
|
||||
if (!oCommand || !oCommand.enabled || !oCommand.canExecute)
|
||||
{
|
||||
throw new Error('You are not using command function');
|
||||
}
|
||||
|
||||
jqElement.addClass('command');
|
||||
ko.bindingHandlers[jqElement.is('form') ? 'submit' : 'click'].init.apply(oViewModel, arguments);
|
||||
},
|
||||
|
||||
'update': function (oElement, fValueAccessor) {
|
||||
|
||||
var
|
||||
bResult = true,
|
||||
jqElement = $(oElement),
|
||||
oCommand = fValueAccessor()
|
||||
;
|
||||
|
||||
bResult = oCommand.enabled();
|
||||
jqElement.toggleClass('command-not-enabled', !bResult);
|
||||
|
||||
if (bResult)
|
||||
{
|
||||
bResult = oCommand.canExecute();
|
||||
jqElement.toggleClass('command-can-not-be-execute', !bResult);
|
||||
}
|
||||
|
||||
jqElement.toggleClass('command-disabled disable disabled', !bResult);
|
||||
|
||||
if (jqElement.is('input') || jqElement.is('button'))
|
||||
{
|
||||
jqElement.prop('disabled', !bResult);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ko.extenders.trimmer = function (oTarget)
|
||||
{
|
||||
var oResult = ko.computed({
|
||||
'read': oTarget,
|
||||
'write': function (sNewValue) {
|
||||
oTarget(Utils.trim(sNewValue.toString()));
|
||||
},
|
||||
'owner': this
|
||||
});
|
||||
|
||||
oResult(oTarget());
|
||||
return oResult;
|
||||
};
|
||||
|
||||
ko.extenders.reversible = function (oTarget)
|
||||
{
|
||||
var mValue = oTarget();
|
||||
|
||||
oTarget.commit = function ()
|
||||
{
|
||||
mValue = oTarget();
|
||||
};
|
||||
|
||||
oTarget.reverse = function ()
|
||||
{
|
||||
oTarget(mValue);
|
||||
};
|
||||
|
||||
oTarget.commitedValue = function ()
|
||||
{
|
||||
return mValue;
|
||||
};
|
||||
|
||||
return oTarget;
|
||||
};
|
||||
|
||||
ko.extenders.toggleSubscribe = function (oTarget, oOptions)
|
||||
{
|
||||
oTarget.subscribe(oOptions[1], oOptions[0], 'beforeChange');
|
||||
oTarget.subscribe(oOptions[2], oOptions[0]);
|
||||
|
||||
return oTarget;
|
||||
};
|
||||
|
||||
ko.extenders.falseTimeout = function (oTarget, iOption)
|
||||
{
|
||||
oTarget.iTimeout = 0;
|
||||
oTarget.subscribe(function (bValue) {
|
||||
if (bValue)
|
||||
{
|
||||
window.clearTimeout(oTarget.iTimeout);
|
||||
oTarget.iTimeout = window.setTimeout(function () {
|
||||
oTarget(false);
|
||||
oTarget.iTimeout = 0;
|
||||
}, Utils.pInt(iOption));
|
||||
}
|
||||
});
|
||||
|
||||
return oTarget;
|
||||
};
|
||||
|
||||
ko.observable.fn.validateEmail = function ()
|
||||
{
|
||||
this.hasError = ko.observable(false);
|
||||
|
||||
this.subscribe(function (sValue) {
|
||||
sValue = Utils.trim(sValue);
|
||||
this.hasError('' !== sValue && !(/^[^@\s]+@[^@\s]+$/.test(sValue)));
|
||||
}, this);
|
||||
|
||||
this.valueHasMutated();
|
||||
return this;
|
||||
};
|
||||
|
||||
ko.observable.fn.validateSimpleEmail = function ()
|
||||
{
|
||||
this.hasError = ko.observable(false);
|
||||
|
||||
this.subscribe(function (sValue) {
|
||||
sValue = Utils.trim(sValue);
|
||||
this.hasError('' !== sValue && !(/^.+@.+$/.test(sValue)));
|
||||
}, this);
|
||||
|
||||
this.valueHasMutated();
|
||||
return this;
|
||||
};
|
||||
|
||||
ko.observable.fn.validateFunc = function (fFunc)
|
||||
{
|
||||
this.hasFuncError = ko.observable(false);
|
||||
|
||||
if (Utils.isFunc(fFunc))
|
||||
{
|
||||
this.subscribe(function (sValue) {
|
||||
this.hasFuncError(!fFunc(sValue));
|
||||
}, this);
|
||||
|
||||
this.valueHasMutated();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
274
dev/Common/LinkBuilder.js
Normal file
274
dev/Common/LinkBuilder.js
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function LinkBuilder()
|
||||
{
|
||||
this.sBase = '#/';
|
||||
this.sCdnStaticDomain = RL.settingsGet('CdnStaticDomain');
|
||||
this.sVersion = RL.settingsGet('Version');
|
||||
this.sSpecSuffix = RL.settingsGet('AuthAccountHash') || '0';
|
||||
|
||||
this.sServer = (RL.settingsGet('IndexFile') || './') + '?';
|
||||
this.sCdnStaticDomain = '' === this.sCdnStaticDomain ? this.sCdnStaticDomain :
|
||||
('/' === this.sCdnStaticDomain.substr(-1) ? this.sCdnStaticDomain : this.sCdnStaticDomain + '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.root = function ()
|
||||
{
|
||||
return this.sBase;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.attachmentDownload = function (sDownload)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/Download/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.attachmentPreview = function (sDownload)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/View/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sDownload
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.attachmentPreviewAsPlain = function (sDownload)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/ViewAsPlain/' + sDownload;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.upload = function ()
|
||||
{
|
||||
return this.sServer + '/Upload/' + this.sSpecSuffix + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.uploadBackground = function ()
|
||||
{
|
||||
return this.sServer + '/UploadBackground/' + this.sSpecSuffix + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.append = function ()
|
||||
{
|
||||
return this.sServer + '/Append/' + this.sSpecSuffix + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sEmail
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.change = function (sEmail)
|
||||
{
|
||||
return this.sServer + '/Change/' + this.sSpecSuffix + '/' + window.encodeURIComponent(sEmail) + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string=} sAdd
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.ajax = function (sAdd)
|
||||
{
|
||||
return this.sServer + '/Ajax/' + this.sSpecSuffix + '/' + sAdd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sRequestHash
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.messageViewLink = function (sRequestHash)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/ViewAsPlain/' + sRequestHash;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sRequestHash
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.messageDownloadLink = function (sRequestHash)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/Download/' + sRequestHash;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.inbox = function ()
|
||||
{
|
||||
return this.sBase + 'mailbox/Inbox';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string=} sScreenName
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.settings = function (sScreenName)
|
||||
{
|
||||
var sResult = this.sBase + 'settings';
|
||||
if (!Utils.isUnd(sScreenName) && '' !== sScreenName)
|
||||
{
|
||||
sResult += '/' + sScreenName;
|
||||
}
|
||||
|
||||
return sResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sScreenName
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.admin = function (sScreenName)
|
||||
{
|
||||
var sResult = this.sBase;
|
||||
switch (sScreenName) {
|
||||
case 'AdminDomains':
|
||||
sResult += 'domains';
|
||||
break;
|
||||
case 'AdminSecurity':
|
||||
sResult += 'security';
|
||||
break;
|
||||
case 'AdminLicensing':
|
||||
sResult += 'licensing';
|
||||
break;
|
||||
}
|
||||
|
||||
return sResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sFolder
|
||||
* @param {number=} iPage
|
||||
* @param {string=} sSearch
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.mailBox = function (sFolder, iPage, sSearch)
|
||||
{
|
||||
iPage = Utils.isNormal(iPage) ? Utils.pInt(iPage) : 1;
|
||||
sSearch = Utils.pString(sSearch);
|
||||
|
||||
var sResult = this.sBase + 'mailbox/';
|
||||
if ('' !== sFolder)
|
||||
{
|
||||
sResult += encodeURI(sFolder);
|
||||
}
|
||||
if (1 < iPage)
|
||||
{
|
||||
sResult = sResult.replace(/[\/]+$/, '');
|
||||
sResult += '/p' + iPage;
|
||||
}
|
||||
if ('' !== sSearch)
|
||||
{
|
||||
sResult = sResult.replace(/[\/]+$/, '');
|
||||
sResult += '/' + encodeURI(sSearch);
|
||||
}
|
||||
|
||||
return sResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.phpInfo = function ()
|
||||
{
|
||||
return this.sServer + 'Info';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sLang
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.langLink = function (sLang)
|
||||
{
|
||||
return this.sServer + '/Lang/0/' + encodeURI(sLang) + '/' + this.sVersion + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sHash
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.getUserPicUrlFromHash = function (sHash)
|
||||
{
|
||||
return this.sServer + '/Raw/' + this.sSpecSuffix + '/UserPic/' + sHash + '/' + this.sVersion + '/';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.emptyContactPic = function ()
|
||||
{
|
||||
return ('' === this.sCdnStaticDomain ? 'rainloop/v/' : this.sCdnStaticDomain) +
|
||||
this.sVersion + '/static/css/images/empty-contact.png';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sFileName
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.sound = function (sFileName)
|
||||
{
|
||||
return ('' === this.sCdnStaticDomain ? 'rainloop/v/' : this.sCdnStaticDomain) +
|
||||
this.sVersion + '/static/sounds/' + sFileName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sTheme
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.themePreviewLink = function (sTheme)
|
||||
{
|
||||
return ('' === this.sCdnStaticDomain ? 'rainloop/v/' : this.sCdnStaticDomain) +
|
||||
this.sVersion + '/themes/' + encodeURI(sTheme) + '/images/preview.png';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.notificationMailIcon = function ()
|
||||
{
|
||||
return ('' === this.sCdnStaticDomain ? 'rainloop/v/' : this.sCdnStaticDomain) +
|
||||
this.sVersion + '/static/css/images/icom-message-notification.png';
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.socialGoogle = function ()
|
||||
{
|
||||
return this.sServer + 'SocialGoogle' + ('' !== this.sSpecSuffix ? '/' + this.sSpecSuffix + '/' : '');
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.socialTwitter = function ()
|
||||
{
|
||||
return this.sServer + 'SocialTwitter' + ('' !== this.sSpecSuffix ? '/' + this.sSpecSuffix + '/' : '');
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
LinkBuilder.prototype.socialFacebook = function ()
|
||||
{
|
||||
return this.sServer + 'SocialFacebook' + ('' !== this.sSpecSuffix ? '/' + this.sSpecSuffix + '/' : '');
|
||||
};
|
||||
95
dev/Common/Plugins.js
Normal file
95
dev/Common/Plugins.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
Plugins.oViewModelsHooks = {};
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
Plugins.oSimpleHooks = {};
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
* @param {Function} ViewModel
|
||||
*/
|
||||
Plugins.regViewModelHook = function (sName, ViewModel)
|
||||
{
|
||||
if (ViewModel)
|
||||
{
|
||||
ViewModel.__hookName = sName;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
* @param {Function} fCallback
|
||||
*/
|
||||
Plugins.addHook = function (sName, fCallback)
|
||||
{
|
||||
if (Utils.isFunc(fCallback))
|
||||
{
|
||||
if (!Utils.isArray(Plugins.oSimpleHooks[sName]))
|
||||
{
|
||||
Plugins.oSimpleHooks[sName] = [];
|
||||
}
|
||||
|
||||
Plugins.oSimpleHooks[sName].push(fCallback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
* @param {Array=} aArguments
|
||||
*/
|
||||
Plugins.runHook = function (sName, aArguments)
|
||||
{
|
||||
if (Utils.isArray(Plugins.oSimpleHooks[sName]))
|
||||
{
|
||||
aArguments = aArguments || [];
|
||||
|
||||
_.each(Plugins.oSimpleHooks[sName], function (fCallback) {
|
||||
fCallback.apply(null, aArguments);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sName
|
||||
* @return {?}
|
||||
*/
|
||||
Plugins.mainSettingsGet = function (sName)
|
||||
{
|
||||
return RL ? RL.settingsGet(sName) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Function} fCallback
|
||||
* @param {string} sAction
|
||||
* @param {Object=} oParameters
|
||||
* @param {?number=} iTimeout
|
||||
* @param {string=} sGetAdd = ''
|
||||
* @param {Array=} aAbortActions = []
|
||||
*/
|
||||
Plugins.remoteRequest = function (fCallback, sAction, oParameters, iTimeout, sGetAdd, aAbortActions)
|
||||
{
|
||||
if (RL)
|
||||
{
|
||||
RL.remote().defaultRequest(fCallback, sAction, oParameters, iTimeout, sGetAdd, aAbortActions);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sPluginSection
|
||||
* @param {string} sName
|
||||
* @return {?}
|
||||
*/
|
||||
Plugins.settingsGet = function (sPluginSection, sName)
|
||||
{
|
||||
var oPlugin = Plugins.mainSettingsGet('Plugins');
|
||||
oPlugin = oPlugin && Utils.isUnd(oPlugin[sPluginSection]) ? null : oPlugin[sPluginSection];
|
||||
return oPlugin ? (Utils.isUnd(oPlugin[sName]) ? null : oPlugin[sName]) : null;
|
||||
};
|
||||
|
||||
|
||||
525
dev/Common/Selector.js
Normal file
525
dev/Common/Selector.js
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {koProperty} oKoList
|
||||
* @param {koProperty} oKoSelectedItem
|
||||
* @param {string} sItemSelector
|
||||
* @param {string} sItemSelectedSelector
|
||||
* @param {string} sItemCheckedSelector
|
||||
*/
|
||||
function Selector(oKoList, oKoSelectedItem, sItemSelector, sItemSelectedSelector, sItemCheckedSelector)
|
||||
{
|
||||
this.list = oKoList;
|
||||
this.selectedItem = oKoSelectedItem;
|
||||
|
||||
this.selectedItem.extend({'toggleSubscribe': [null,
|
||||
function (oPrev) {
|
||||
if (oPrev)
|
||||
{
|
||||
oPrev.selected(false);
|
||||
}
|
||||
}, function (oNext) {
|
||||
if (oNext)
|
||||
{
|
||||
oNext.selected(true);
|
||||
}
|
||||
}
|
||||
]});
|
||||
|
||||
this.oContentVisible = null;
|
||||
this.oContentScrollable = null;
|
||||
|
||||
this.sItemSelector = sItemSelector;
|
||||
this.sItemSelectedSelector = sItemSelectedSelector;
|
||||
this.sItemCheckedSelector = sItemCheckedSelector;
|
||||
|
||||
this.sLastUid = '';
|
||||
this.oCallbacks = {};
|
||||
this.iSelectTimer = 0;
|
||||
this.bUseKeyboard = true;
|
||||
|
||||
this.emptyFunction = function () {};
|
||||
|
||||
this.useItemSelectCallback = true;
|
||||
this.throttleSelection = false;
|
||||
|
||||
this.selectedItem.subscribe(function (oItem) {
|
||||
if (this.useItemSelectCallback)
|
||||
{
|
||||
if (this.throttleSelection)
|
||||
{
|
||||
this.throttleSelection = false;
|
||||
this.selectItemCallbacksThrottle(oItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.selectItemCallbacks(oItem);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
var
|
||||
self = this,
|
||||
aCheckedCache = [],
|
||||
mSelected = null
|
||||
;
|
||||
|
||||
this.list.subscribe(function () {
|
||||
var self = this, aItems = this.list();
|
||||
if (Utils.isArray(aItems))
|
||||
{
|
||||
_.each(aItems, function (oItem) {
|
||||
if (oItem.checked())
|
||||
{
|
||||
aCheckedCache.push(self.getItemUid(oItem));
|
||||
}
|
||||
|
||||
if (null === mSelected && oItem.selected())
|
||||
{
|
||||
mSelected = self.getItemUid(oItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, this, 'beforeChange');
|
||||
|
||||
this.list.subscribe(function (aItems) {
|
||||
|
||||
this.useItemSelectCallback = false;
|
||||
|
||||
this.selectedItem(null);
|
||||
|
||||
if (Utils.isArray(aItems))
|
||||
{
|
||||
var self = this, iLen = aCheckedCache.length;
|
||||
_.each(aItems, function (oItem) {
|
||||
if (0 < iLen && -1 < Utils.inArray(self.getItemUid(oItem), aCheckedCache))
|
||||
{
|
||||
oItem.checked(true);
|
||||
iLen--;
|
||||
}
|
||||
|
||||
if (null !== mSelected && mSelected === self.getItemUid(oItem))
|
||||
{
|
||||
oItem.selected(true);
|
||||
mSelected = null;
|
||||
|
||||
self.selectedItem(oItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.useItemSelectCallback = true;
|
||||
|
||||
aCheckedCache = [];
|
||||
mSelected = null;
|
||||
}, this);
|
||||
|
||||
this.list.setSelectedByUid = function (sUid) {
|
||||
self.selectByUid(sUid, false);
|
||||
};
|
||||
|
||||
this.selectItemCallbacksThrottle = _.debounce(this.selectItemCallbacks, 300);
|
||||
}
|
||||
|
||||
Selector.prototype.selectItemCallbacks = function (oItem)
|
||||
{
|
||||
(this.oCallbacks['onItemSelect'] || this.emptyFunction)(oItem);
|
||||
};
|
||||
|
||||
Selector.prototype.init = function (oContentVisible, oContentScrollable)
|
||||
{
|
||||
this.oContentVisible = oContentVisible;
|
||||
this.oContentScrollable = oContentScrollable;
|
||||
|
||||
if (this.oContentVisible && this.oContentScrollable)
|
||||
{
|
||||
var
|
||||
self = this
|
||||
;
|
||||
|
||||
$(this.oContentVisible)
|
||||
.on('click', this.sItemSelector, function (oEvent) {
|
||||
self.actionClick(ko.dataFor(this), oEvent);
|
||||
}).on('click', this.sItemCheckedSelector, function (oEvent) {
|
||||
var oItem = ko.dataFor(this);
|
||||
if (oItem)
|
||||
{
|
||||
if (oEvent && oEvent.shiftKey)
|
||||
{
|
||||
self.actionClick(oItem, oEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.sLastUid = self.getItemUid(oItem);
|
||||
if (oItem.selected())
|
||||
{
|
||||
oItem.checked(false);
|
||||
self.selectedItem(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
oItem.checked(!oItem.checked());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
$(window.document).on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.bUseKeyboard && !Utils.inFocus())
|
||||
{
|
||||
if (-1 < Utils.inArray(oEvent.keyCode, [Enums.EventKeyCode.Up, Enums.EventKeyCode.Down, Enums.EventKeyCode.Insert,
|
||||
Enums.EventKeyCode.Home, Enums.EventKeyCode.End, Enums.EventKeyCode.PageUp, Enums.EventKeyCode.PageDown]))
|
||||
{
|
||||
self.newSelectPosition(oEvent.keyCode, oEvent.shiftKey);
|
||||
bResult = false;
|
||||
}
|
||||
else if (Enums.EventKeyCode.Delete === oEvent.keyCode && !oEvent.ctrlKey && !oEvent.shiftKey)
|
||||
{
|
||||
if (self.oCallbacks['onDelete'])
|
||||
{
|
||||
self.oCallbacks['onDelete']();
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Selector.prototype.selectByUid = function (mUid, bUseCallback)
|
||||
{
|
||||
bUseCallback = Utils.isUnd(bUseCallback) ? true : !!bUseCallback;
|
||||
this.useItemSelectCallback = bUseCallback;
|
||||
|
||||
var
|
||||
oItem = _.find(this.list(), function (oItem) {
|
||||
return mUid === this.getItemUid(oItem);
|
||||
}, this)
|
||||
;
|
||||
|
||||
if (oItem)
|
||||
{
|
||||
this.selectedItem(oItem);
|
||||
}
|
||||
|
||||
this.useItemSelectCallback = true;
|
||||
};
|
||||
|
||||
Selector.prototype.useKeyboard = function (bValue)
|
||||
{
|
||||
this.bUseKeyboard = !!bValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} oItem
|
||||
* @returns {string}
|
||||
*/
|
||||
Selector.prototype.getItemUid = function (oItem)
|
||||
{
|
||||
var
|
||||
sUid = '',
|
||||
fGetItemUidCallback = this.oCallbacks['onItemGetUid'] || null
|
||||
;
|
||||
|
||||
if (fGetItemUidCallback && oItem)
|
||||
{
|
||||
sUid = fGetItemUidCallback(oItem);
|
||||
}
|
||||
|
||||
return sUid.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} iEventKeyCode
|
||||
* @param {boolean} bShiftKey
|
||||
*/
|
||||
Selector.prototype.newSelectPosition = function (iEventKeyCode, bShiftKey)
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
iIndex = 0,
|
||||
iPageStep = 10,
|
||||
bNext = false,
|
||||
bStop = false,
|
||||
oResult = null,
|
||||
aList = this.list(),
|
||||
iListLen = aList ? aList.length : 0,
|
||||
oSelected = this.selectedItem()
|
||||
;
|
||||
|
||||
if (0 < iListLen)
|
||||
{
|
||||
if (!oSelected)
|
||||
{
|
||||
if (Enums.EventKeyCode.Down === iEventKeyCode || Enums.EventKeyCode.Insert === iEventKeyCode || Enums.EventKeyCode.Home === iEventKeyCode || Enums.EventKeyCode.PageUp === iEventKeyCode)
|
||||
{
|
||||
oResult = aList[0];
|
||||
}
|
||||
else if (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.End === iEventKeyCode || Enums.EventKeyCode.PageDown === iEventKeyCode)
|
||||
{
|
||||
oResult = aList[aList.length - 1];
|
||||
}
|
||||
}
|
||||
else if (oSelected)
|
||||
{
|
||||
if (Enums.EventKeyCode.Down === iEventKeyCode || Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Insert === iEventKeyCode)
|
||||
{
|
||||
_.each(aList, function (oItem) {
|
||||
if (!bStop)
|
||||
{
|
||||
switch (iEventKeyCode) {
|
||||
case Enums.EventKeyCode.Up:
|
||||
if (oSelected === oItem)
|
||||
{
|
||||
bStop = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
oResult = oItem;
|
||||
}
|
||||
break;
|
||||
case Enums.EventKeyCode.Down:
|
||||
case Enums.EventKeyCode.Insert:
|
||||
if (bNext)
|
||||
{
|
||||
oResult = oItem;
|
||||
bStop = true;
|
||||
}
|
||||
else if (oSelected === oItem)
|
||||
{
|
||||
bNext = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (Enums.EventKeyCode.Home === iEventKeyCode || Enums.EventKeyCode.End === iEventKeyCode)
|
||||
{
|
||||
if (Enums.EventKeyCode.Home === iEventKeyCode)
|
||||
{
|
||||
oResult = aList[0];
|
||||
}
|
||||
else if (Enums.EventKeyCode.End === iEventKeyCode)
|
||||
{
|
||||
oResult = aList[aList.length - 1];
|
||||
}
|
||||
}
|
||||
else if (Enums.EventKeyCode.PageDown === iEventKeyCode)
|
||||
{
|
||||
for (; iIndex < iListLen; iIndex++)
|
||||
{
|
||||
if (oSelected === aList[iIndex])
|
||||
{
|
||||
iIndex += iPageStep;
|
||||
iIndex = iListLen - 1 < iIndex ? iListLen - 1 : iIndex;
|
||||
oResult = aList[iIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Enums.EventKeyCode.PageUp === iEventKeyCode)
|
||||
{
|
||||
for (iIndex = iListLen; iIndex >= 0; iIndex--)
|
||||
{
|
||||
if (oSelected === aList[iIndex])
|
||||
{
|
||||
iIndex -= iPageStep;
|
||||
iIndex = 0 > iIndex ? 0 : iIndex;
|
||||
oResult = aList[iIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oResult)
|
||||
{
|
||||
if (oSelected)
|
||||
{
|
||||
if (bShiftKey)
|
||||
{
|
||||
if (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Down === iEventKeyCode)
|
||||
{
|
||||
oSelected.checked(!oSelected.checked());
|
||||
}
|
||||
}
|
||||
else if (Enums.EventKeyCode.Insert === iEventKeyCode)
|
||||
{
|
||||
oSelected.checked(!oSelected.checked());
|
||||
}
|
||||
}
|
||||
|
||||
this.throttleSelection = true;
|
||||
this.selectedItem(oResult);
|
||||
this.throttleSelection = true;
|
||||
|
||||
if (0 !== this.iSelectTimer)
|
||||
{
|
||||
window.clearTimeout(this.iSelectTimer);
|
||||
this.iSelectTimer = window.setTimeout(function () {
|
||||
self.iSelectTimer = 0;
|
||||
self.actionClick(oResult);
|
||||
}, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.iSelectTimer = window.setTimeout(function () {
|
||||
self.iSelectTimer = 0;
|
||||
}, 200);
|
||||
|
||||
this.actionClick(oResult);
|
||||
}
|
||||
|
||||
this.scrollToSelected();
|
||||
}
|
||||
else if (oSelected)
|
||||
{
|
||||
if (bShiftKey && (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Down === iEventKeyCode))
|
||||
{
|
||||
oSelected.checked(!oSelected.checked());
|
||||
}
|
||||
else if (Enums.EventKeyCode.Insert === iEventKeyCode)
|
||||
{
|
||||
oSelected.checked(!oSelected.checked());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
Selector.prototype.scrollToSelected = function ()
|
||||
{
|
||||
if (!this.oContentVisible || !this.oContentScrollable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var
|
||||
iOffset = 20,
|
||||
oSelected = $(this.sItemSelectedSelector, this.oContentScrollable),
|
||||
oPos = oSelected.position(),
|
||||
iVisibleHeight = this.oContentVisible.height(),
|
||||
iSelectedHeight = oSelected.outerHeight()
|
||||
;
|
||||
|
||||
if (oPos && (oPos.top < 0 || oPos.top + iSelectedHeight > iVisibleHeight))
|
||||
{
|
||||
if (oPos.top < 0)
|
||||
{
|
||||
this.oContentScrollable.scrollTop(this.oContentScrollable.scrollTop() + oPos.top - iOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.oContentScrollable.scrollTop(this.oContentScrollable.scrollTop() + oPos.top - iVisibleHeight + iSelectedHeight + iOffset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Selector.prototype.eventClickFunction = function (oItem, oEvent)
|
||||
{
|
||||
var
|
||||
sUid = this.getItemUid(oItem),
|
||||
iIndex = 0,
|
||||
iLength = 0,
|
||||
oListItem = null,
|
||||
sLineUid = '',
|
||||
bChangeRange = false,
|
||||
bIsInRange = false,
|
||||
aList = [],
|
||||
bChecked = false
|
||||
;
|
||||
|
||||
if (oEvent && oEvent.shiftKey)
|
||||
{
|
||||
if ('' !== sUid && '' !== this.sLastUid && sUid !== this.sLastUid)
|
||||
{
|
||||
aList = this.list();
|
||||
bChecked = oItem.checked();
|
||||
|
||||
for (iIndex = 0, iLength = aList.length; iIndex < iLength; iIndex++)
|
||||
{
|
||||
oListItem = aList[iIndex];
|
||||
sLineUid = this.getItemUid(oListItem);
|
||||
|
||||
bChangeRange = false;
|
||||
if (sLineUid === this.sLastUid || sLineUid === sUid)
|
||||
{
|
||||
bChangeRange = true;
|
||||
}
|
||||
|
||||
if (bChangeRange)
|
||||
{
|
||||
bIsInRange = !bIsInRange;
|
||||
}
|
||||
|
||||
if (bIsInRange || bChangeRange)
|
||||
{
|
||||
oListItem.checked(bChecked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.sLastUid = '' === sUid ? '' : sUid;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} oItem
|
||||
* @param {Object=} oEvent
|
||||
*/
|
||||
Selector.prototype.actionClick = function (oItem, oEvent)
|
||||
{
|
||||
if (oItem)
|
||||
{
|
||||
var
|
||||
bClick = true,
|
||||
sUid = this.getItemUid(oItem)
|
||||
;
|
||||
|
||||
if (oEvent)
|
||||
{
|
||||
if (oEvent.shiftKey)
|
||||
{
|
||||
bClick = false;
|
||||
if ('' === this.sLastUid)
|
||||
{
|
||||
this.sLastUid = sUid;
|
||||
}
|
||||
|
||||
oItem.checked(!oItem.checked());
|
||||
this.eventClickFunction(oItem, oEvent);
|
||||
}
|
||||
else if (oEvent.ctrlKey)
|
||||
{
|
||||
bClick = false;
|
||||
this.sLastUid = sUid;
|
||||
|
||||
oItem.checked(!oItem.checked());
|
||||
}
|
||||
}
|
||||
|
||||
if (bClick)
|
||||
{
|
||||
this.selectedItem(oItem);
|
||||
this.sLastUid = sUid;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Selector.prototype.on = function (sEventName, fCallback)
|
||||
{
|
||||
this.oCallbacks[sEventName] = fCallback;
|
||||
};
|
||||
1343
dev/Common/Utils.js
Normal file
1343
dev/Common/Utils.js
Normal file
File diff suppressed because it is too large
Load diff
71
dev/Common/_Begin.js
Normal file
71
dev/Common/_Begin.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
Consts = {},
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
Enums = {},
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
NotificationI18N = {},
|
||||
|
||||
/**
|
||||
* @type {Object.<Function>}
|
||||
*/
|
||||
Utils = {},
|
||||
|
||||
/**
|
||||
* @type {Object.<Function>}
|
||||
*/
|
||||
Plugins = {},
|
||||
|
||||
/**
|
||||
* @type {Object.<Function>}
|
||||
*/
|
||||
Base64 = {},
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
Globals = {},
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
ViewModels = {
|
||||
'settings': [],
|
||||
'settings-removed': [],
|
||||
'settings-disabled': []
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
kn = null,
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
AppData = window['rainloopAppData'] || {},
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
*/
|
||||
I18n = window['rainloopI18N'] || {},
|
||||
|
||||
$html = $('html'),
|
||||
|
||||
$window = $(window),
|
||||
|
||||
$document = $(window.document),
|
||||
|
||||
NotificationClass = window.Notification && window.Notification.requestPermission ? window.Notification : null
|
||||
;
|
||||
6
dev/Common/_BeginA.js
Normal file
6
dev/Common/_BeginA.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/*jshint onevar: false*/
|
||||
/**
|
||||
* @type {?AdminApp}
|
||||
*/
|
||||
var RL = null;
|
||||
/*jshint onevar: true*/
|
||||
6
dev/Common/_BeginW.js
Normal file
6
dev/Common/_BeginW.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/*jshint onevar: false*/
|
||||
/**
|
||||
* @type {?RainLoopApp}
|
||||
*/
|
||||
var RL = null;
|
||||
/*jshint onevar: true*/
|
||||
3
dev/Common/_BootEnd.js
Normal file
3
dev/Common/_BootEnd.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if (window.SimplePace) {
|
||||
window.SimplePace.add(10);
|
||||
}
|
||||
3
dev/Common/_CoreEnd.js
Normal file
3
dev/Common/_CoreEnd.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if (window.SimplePace) {
|
||||
window.SimplePace.add(10);
|
||||
}
|
||||
57
dev/Common/_End.js
Normal file
57
dev/Common/_End.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
$html.addClass(Globals.bMobileDevice ? 'mobile' : 'no-mobile');
|
||||
|
||||
$window.keydown(Utils.killCtrlAandS).keyup(Utils.killCtrlAandS);
|
||||
$window.unload(function () {
|
||||
Globals.bUnload = true;
|
||||
});
|
||||
|
||||
// export
|
||||
window.rl = window.rl || {};
|
||||
window.rl.addHook = Plugins.addHook;
|
||||
window.rl.settingsGet = Plugins.mainSettingsGet;
|
||||
window.rl.remoteRequest = Plugins.remoteRequest;
|
||||
window.rl.pluginSettingsGet = Plugins.settingsGet;
|
||||
window.rl.addSettingsViewModel = Utils.addSettingsViewModel;
|
||||
window.rl.createCommand = Utils.createCommand;
|
||||
|
||||
window.rl.EmailModel = EmailModel;
|
||||
window.rl.Enums = Enums;
|
||||
|
||||
window['__RLBOOT'] = function (fCall) {
|
||||
|
||||
// boot
|
||||
$(function () {
|
||||
|
||||
if (window['rainloopTEMPLATES'] && window['rainloopTEMPLATES'][0])
|
||||
{
|
||||
$('#rl-templates').html(window['rainloopTEMPLATES'][0]);
|
||||
|
||||
window.setInterval(function () {
|
||||
Globals.minuteTick(!Globals.minuteTick());
|
||||
}, 1000 * 60);
|
||||
|
||||
window.setInterval(function () {
|
||||
Globals.fiveMinuteTick(!Globals.fiveMinuteTick());
|
||||
}, 1000 * 60 * 5);
|
||||
|
||||
_.delay(function () {
|
||||
window['rainloopAppData'] = {};
|
||||
window['rainloopI18N'] = {};
|
||||
window['rainloopTEMPLATES'] = {};
|
||||
|
||||
kn.setBoot(RL).bootstart();
|
||||
|
||||
$html.addClass('rl-started');
|
||||
|
||||
}, 50);
|
||||
}
|
||||
else
|
||||
{
|
||||
fCall(false);
|
||||
}
|
||||
|
||||
window['__RLBOOT'] = null;
|
||||
});
|
||||
};
|
||||
3
dev/Common/_LibsEnd.js
Normal file
3
dev/Common/_LibsEnd.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if (window.SimplePace) {
|
||||
window.SimplePace.add(20);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue