diff --git a/build/plugin.xml b/build/plugin.xml
index 5ee0cb685..8afe74ea8 100644
--- a/build/plugin.xml
+++ b/build/plugin.xml
@@ -103,5 +103,10 @@
+
+
+
+
+
diff --git a/dev/App/User.js b/dev/App/User.js
index af86fc5bf..332884a3a 100644
--- a/dev/App/User.js
+++ b/dev/App/User.js
@@ -122,10 +122,11 @@
if (Settings.settingsGet('UserBackgroundHash'))
{
_.delay(function () {
- $('#rl-bg').backstretch(Links.publicLink(Settings.settingsGet('UserBackgroundHash')), {
+ $('#rl-bg').attr('style', 'background-image: none !important;')
+ .backstretch(Links.publicLink(Settings.settingsGet('UserBackgroundHash')), {
'fade': Globals.bAnimationSupported ? 1000 : 0
- });
- }, 3000);
+ }).removeAttr('style');
+ }, 2000);
}
this.socialUsers = _.bind(this.socialUsers, this);
@@ -1354,6 +1355,8 @@
AppUser.prototype.bootstartLoginScreen = function ()
{
+ Globals.$html.removeClass('rl-user-auth').addClass('rl-user-no-auth');
+
var sCustomLoginLink = Utils.pString(Settings.settingsGet('CustomLoginLink'));
if (!sCustomLoginLink)
{
@@ -1411,6 +1414,8 @@
if (!!Settings.settingsGet('Auth'))
{
+ Globals.$html.addClass('rl-user-auth');
+
this.setTitle(Translator.i18n('TITLES/LOADING'));
//require.ensure([], function() { // require code splitting
diff --git a/dev/Settings/Admin/Branding.js b/dev/Settings/Admin/Branding.js
index ec2636e7b..32e92da42 100644
--- a/dev/Settings/Admin/Branding.js
+++ b/dev/Settings/Admin/Branding.js
@@ -31,6 +31,9 @@
this.loginLogo = ko.observable(Settings.settingsGet('LoginLogo') || '');
this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
+ this.loginBackground = ko.observable(Settings.settingsGet('LoginBackground') || '');
+ this.loginBackground.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
+
this.userLogo = ko.observable(Settings.settingsGet('UserLogo') || '');
this.userLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
@@ -40,6 +43,9 @@
this.loginCss = ko.observable(Settings.settingsGet('LoginCss'));
this.loginCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
+ this.userCss = ko.observable(Settings.settingsGet('UserCss'));
+ this.userCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
+
this.loginPowered = ko.observable(!!Settings.settingsGet('LoginPowered'));
}
@@ -60,7 +66,9 @@
f3 = Utils.settingsSaveHelperSimpleFunction(self.loginLogo.trigger, self),
f4 = Utils.settingsSaveHelperSimpleFunction(self.loginDescription.trigger, self),
f5 = Utils.settingsSaveHelperSimpleFunction(self.loginCss.trigger, self),
- f6 = Utils.settingsSaveHelperSimpleFunction(self.userLogo.trigger, self)
+ f6 = Utils.settingsSaveHelperSimpleFunction(self.userLogo.trigger, self),
+ f7 = Utils.settingsSaveHelperSimpleFunction(self.loginBackground.trigger, self),
+ f8 = Utils.settingsSaveHelperSimpleFunction(self.userCss.trigger, self)
;
self.title.subscribe(function (sValue) {
@@ -81,6 +89,12 @@
});
});
+ self.loginBackground.subscribe(function (sValue) {
+ Remote.saveAdminConfig(f7, {
+ 'LoginBackground': Utils.trim(sValue)
+ });
+ });
+
self.userLogo.subscribe(function (sValue) {
Remote.saveAdminConfig(f6, {
'UserLogo': Utils.trim(sValue)
@@ -99,6 +113,12 @@
});
});
+ self.userCss.subscribe(function (sValue) {
+ Remote.saveAdminConfig(f8, {
+ 'UserCss': Utils.trim(sValue)
+ });
+ });
+
self.loginPowered.subscribe(function (bValue) {
Remote.saveAdminConfig(null, {
'LoginPowered': bValue ? '1' : '0'
diff --git a/dev/Styles/AdminBranding.less b/dev/Styles/AdminBranding.less
index 8a8db695c..ccad55da7 100644
--- a/dev/Styles/AdminBranding.less
+++ b/dev/Styles/AdminBranding.less
@@ -1,7 +1,12 @@
.b-admin-branding {
+
.disabled-form {
opacity: 0.8;
}
+
+ .custom-css-wrapper textarea {
+ font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
+ }
}
diff --git a/dev/Styles/Login.less b/dev/Styles/Login.less
index 628906ec9..0fc1d0095 100644
--- a/dev/Styles/Login.less
+++ b/dev/Styles/Login.less
@@ -43,7 +43,7 @@
}
.wrapper {
- padding: 40px 40px 10px 40px;
+ padding: 40px 40px 12px 40px;
}
#recaptcha_image img {
diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js
index 39ed38b48..e40aea8e6 100644
--- a/dev/View/User/Login.js
+++ b/dev/View/User/Login.js
@@ -47,9 +47,8 @@
this.additionalCodeSignMe = ko.observable(false);
this.logoImg = Utils.trim(Settings.settingsGet('LoginLogo'));
- this.loginDescription = Utils.trim(Settings.settingsGet('LoginDescription'));
- this.logoCss = Utils.trim(Settings.settingsGet('LoginCss'));
this.logoPowered = !!Settings.settingsGet('LoginPowered');
+ this.loginDescription = Utils.trim(Settings.settingsGet('LoginDescription'));
this.forgotPasswordLinkUrl = Settings.settingsGet('ForgotPasswordLinkUrl');
this.registrationLinkUrl = Settings.settingsGet('RegistrationLinkUrl');
diff --git a/gulpfile.js b/gulpfile.js
index accb4cc99..41ca2c310 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -113,7 +113,7 @@ cfg.paths.js = {
'vendors/json2.min.js',
'vendors/labjs/LAB.min.js',
'vendors/simple-pace/simple-pace-1.0.min.js',
- 'vendors/rl/rl-1.4.min.js'
+ 'vendors/rl/rl-1.5.min.js'
]
},
openpgp: {
@@ -387,8 +387,8 @@ regOtherMinTask('other:inputosaurus', 'vendors/inputosaurus/', 'inputosaurus.js'
regOtherMinTask('other:pace', 'vendors/simple-pace/', 'simple-pace.js', 'simple-pace-1.0.min.js',
'/*! RainLoop Simple Pace v1.0 (c) 2014 RainLoop Team; Licensed under MIT */\n');
-regOtherMinTask('other:rl', 'vendors/rl/', 'rl.js', 'rl-1.4.min.js',
- '/*! RainLoop Index Helper v1.4 (c) 2014 RainLoop Team; Licensed under MIT */\n');
+regOtherMinTask('other:rl', 'vendors/rl/', 'rl.js', 'rl-1.5.min.js',
+ '/*! RainLoop Index Helper v1.5 (c) 2015 RainLoop Team; Licensed under MIT */\n');
gulp.task('fontastic-fonts:clear', function() {
return cleanDir('rainloop/v/' + cfg.devVersion + '/static/css/fonts/rainloop.*');
diff --git a/package.json b/package.json
index f3ff02a91..6be243090 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "RainLoop",
"title": "RainLoop Webmail",
"version": "1.7.3",
- "release": "237",
+ "release": "240",
"description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net",
"main": "gulpfile.js",
diff --git a/plugins/video-on-login-screen/LICENSE b/plugins/video-on-login-screen/LICENSE
new file mode 100644
index 000000000..4aed64b3a
--- /dev/null
+++ b/plugins/video-on-login-screen/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 RainLoop Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plugins/video-on-login-screen/README b/plugins/video-on-login-screen/README
new file mode 100644
index 000000000..ec8c5bbdb
--- /dev/null
+++ b/plugins/video-on-login-screen/README
@@ -0,0 +1 @@
+Fullscreen background video on login screen.
\ No newline at end of file
diff --git a/plugins/video-on-login-screen/VERSION b/plugins/video-on-login-screen/VERSION
new file mode 100644
index 000000000..9f8e9b69a
--- /dev/null
+++ b/plugins/video-on-login-screen/VERSION
@@ -0,0 +1 @@
+1.0
\ No newline at end of file
diff --git a/plugins/video-on-login-screen/index.php b/plugins/video-on-login-screen/index.php
new file mode 100644
index 000000000..ad4c51b45
--- /dev/null
+++ b/plugins/video-on-login-screen/index.php
@@ -0,0 +1,38 @@
+addJs('js/vide/jquery.vide.js');
+ $this->addJs('js/video-on-login.js');
+ }
+
+ /**
+ * @return array
+ */
+ public function configMapping()
+ {
+ return array(
+ \RainLoop\Plugins\Property::NewInstance('mp4_file')->SetLabel('Url to a mp4 file')
+ ->SetPlaceholder('http://')
+ ->SetAllowedInJs(true)
+ ->SetDefaultValue(''),
+ \RainLoop\Plugins\Property::NewInstance('webm_file')->SetLabel('Url to a webm file')
+ ->SetPlaceholder('http://')
+ ->SetAllowedInJs(true)
+ ->SetDefaultValue(''),
+ \RainLoop\Plugins\Property::NewInstance('ogv_file')->SetLabel('Url to a ogv file')
+ ->SetPlaceholder('http://')
+ ->SetAllowedInJs(true)
+ ->SetDefaultValue(''),
+ \RainLoop\Plugins\Property::NewInstance('muted')->SetLabel('Muted')
+ ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
+ ->SetAllowedInJs(true)
+ ->SetDefaultValue(true),
+ );
+ }
+}
diff --git a/plugins/video-on-login-screen/js/vide/CHANGELOG.md b/plugins/video-on-login-screen/js/vide/CHANGELOG.md
new file mode 100644
index 000000000..c922ebbd8
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/CHANGELOG.md
@@ -0,0 +1,41 @@
+### 0.3.0
+* Added the `resizing` option.
+* Updated tests.
+
+### 0.2.1
+* Code refactoring.
+* Updated devDependencies.
+
+### 0.2.0
+* Lots of updates since 0.1.0.
+* Improved code linting.
+* Cleaned up the repository.
+* Added bower dependencies.
+* Updated devDependencies.
+
+### 0.1.4
+* Fixed wrong URL parsing.
+* Changed main files in bower.json.
+
+### 0.1.3
+* Path argument can receive list of sources.
+* Strings with options and pathes can be passed to the constructor directly.
+* Added `posterType: none` value.
+* Updated README.
+* Updated JSDoc.
+* Updated tests.
+* Updated examples.
+* Added CONTRIBUTING.md
+* Other small fixes and optimizations.
+
+### 0.1.2
+* Restored `posterType` option to specify poster image type.
+
+### 0.1.1
+* Support of absolute URLs (#10).
+* Fixed the CORS issue (#11).
+* Fixed the destroy method.
+* Fixed parsing of empty options.
+* Poster and video positions are the same now.
+* Syntax and behavior of the position option are similar to the CSS `background-position` property.
+* Add support of the `.jpeg` extension.
diff --git a/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md b/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md
new file mode 100644
index 000000000..3900d1fe7
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+CONTRIBUTING
+====
+
+1. Fork.
+2. Run `npm install`.
+3. Make your changes on the `src` folder.
+4. Update tests.
+5. Run `npm test`, make sure everything is okay.
+6. Submit a pull request to the master branch.
+
+Thanks.
diff --git a/plugins/video-on-login-screen/js/vide/MIT-LICENSE b/plugins/video-on-login-screen/js/vide/MIT-LICENSE
new file mode 100644
index 000000000..f03343d6d
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/MIT-LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ilya Makarov, http://vodkabears.github.io
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/plugins/video-on-login-screen/js/vide/README.md b/plugins/video-on-login-screen/js/vide/README.md
new file mode 100644
index 000000000..546d81975
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/README.md
@@ -0,0 +1,140 @@
+[](http://badge.fury.io/bo/vide)
+[](https://travis-ci.org/VodkaBears/Vide)
+[](https://david-dm.org/vodkabears/vide#info=devDependencies)
+Vide
+====
+
+Easy as hell jQuery plugin for video backgrounds.
+
+## Notes
+
+* All modern desktop browsers are supported.
+* IE9+
+* iOS plays video from a browser only in the native player. So video for iOS is disabled, only fullscreen poster will be used.
+* Some android devices play video, some not — go figure. So video for android is disabled, only fullscreen poster will be used.
+
+## Instructions
+
+Download it from [GitHub](https://github.com/VodkaBears/Vide/releases/latest) or via Bower:
+`bower install vide`
+
+Include plugin: ``
+
+Prepare your video in several formats like '.webm', '.mp4' for cross browser compatability, also add a poster with `.jpg`, `.png` or `.gif` extension:
+```
+path/
+├── to/
+│ ├── video.mp4
+│ ├── video.ogv
+│ ├── video.webm
+│ └── video.jpg
+```
+
+Add `data-vide-bg` attribute with a path to the video and poster without extension, video and poster must have the same name. Add `data-vide-options` to pass vide options, if you need it. By default video is muted, looped and starts automatically.
+```html
+
+
+```
+
+Also you can set extended path:
+```html
+
+
+```
+
+In some situations it can be helpful to initialize it with JS because Vide doesn't have mutation observers:
+```js
+$("#myBlock").vide("path/to/video");
+$("#myBlock").vide("path/to/video", {
+...options...
+});
+$("#myBlock").vide({
+ mp4: path/to/video1,
+ webm: path/to/video2,
+ ogv: path/to/video3,
+ poster: path/to/poster
+}, {
+...options...
+});
+$("#myBlock").vide("extended path as a string", "options as a string");
+```
+
+Easy as hell.
+
+## Options
+
+Below is a complete list of options and matching default values:
+
+```js
+$("#yourElement").vide({
+ volume: 1,
+ playbackRate: 1,
+ muted: true,
+ loop: true,
+ autoplay: true,
+ position: "50% 50%", // Similar to the CSS `background-position` property.
+ posterType: "detect", // Poster image type. "detect" — auto-detection; "none" — no poster; "jpg", "png", "gif",... - extensions.
+ resizing: true // Auto-resizing, read: https://github.com/VodkaBears/Vide#resizing
+});
+```
+
+## Methods
+
+Below is a complete list of methods:
+
+```js
+// Get instance of the plugin
+var instance = $("#yourElement").data("vide");
+
+// Get video element of the background. Do what you want.
+instance.getVideoObject();
+
+// Resize video background.
+// It calls automatically, if window resize (or element, if you will use something like https://github.com/cowboy/jquery-resize).
+instance.resize();
+
+// Destroy plugin instance
+instance.destroy();
+```
+
+## Resizing
+
+The Vide plugin resizes if the window resizes. If you will use something like https://github.com/cowboy/jquery-resize, it will resize automatically when the container resizes. Or simply use `resize()` method whenever you need.
+
+Set the `resizing` option to false to disable auto-resizing.
+
+## Encoding video
+
+http://diveintohtml5.info/video.html#miro
+
+## Ruby Gem
+
+[Vider](https://github.com/wazery/vider) by Islam Wazery.
+
+## License
+
+```
+The MIT License (MIT)
+
+Copyright (c) 2014 Ilya Makarov, http://vodkabears.github.io
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
diff --git a/plugins/video-on-login-screen/js/vide/bower.json b/plugins/video-on-login-screen/js/vide/bower.json
new file mode 100644
index 000000000..7d4c47379
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/bower.json
@@ -0,0 +1,40 @@
+{
+ "name": "vide",
+ "version": "0.3.0",
+ "homepage": "http://vodkabears.github.io/vide/",
+ "authors": [
+ "Ilya Makarov "
+ ],
+ "description": "Easy as hell jQuery plugin for video backgrounds.",
+ "main": "dist/jquery.vide.js",
+ "ignore": [
+ "**/.*",
+ "examples/",
+ "libs/",
+ "src/",
+ "test/",
+ "*.md",
+ "Gruntfile.js",
+ "package.json",
+ "vide.jquery.json"
+ ],
+ "keywords": [
+ "jquery",
+ "plugin",
+ "jquery-plugin",
+ "video",
+ "background",
+ "ui",
+ "responsive",
+ "declarative"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "jquery": "*"
+ },
+ "devDependencies": {
+ "qunit": "~1.15.0",
+ "jquery": "jquery#^1.11.1",
+ "jquery2": "jquery#^2.1.1"
+ }
+}
diff --git a/plugins/video-on-login-screen/js/vide/jquery.vide.js b/plugins/video-on-login-screen/js/vide/jquery.vide.js
new file mode 100644
index 000000000..cc5f29dfc
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/jquery.vide.js
@@ -0,0 +1,442 @@
+/*
+ * Vide - v0.3.0
+ * Easy as hell jQuery plugin for video backgrounds.
+ * http://vodkabears.github.io/vide/
+ *
+ * Made by Ilya Makarov
+ * Under MIT License
+ */
+!(function($, window, document, navigator) {
+ "use strict";
+
+ /**
+ * Vide settings
+ * @private
+ */
+ var pluginName = "vide",
+ defaults = {
+ volume: 1,
+ playbackRate: 1,
+ muted: true,
+ loop: true,
+ autoplay: true,
+ position: "50% 50%",
+ posterType: "detect",
+ resizing: true
+ },
+
+ // is iOs?
+ isIOS = /iPad|iPhone|iPod/i.test(navigator.userAgent),
+
+ // is Android?
+ isAndroid = /Android/i.test(navigator.userAgent);
+
+ /**
+ * Parse string with options
+ * @param {String} str
+ * @returns {Object|String}
+ * @private
+ */
+ function parseOptions(str) {
+ var obj = {},
+ delimiterIndex,
+ option,
+ prop, val,
+ arr, len,
+ i;
+
+ // remove spaces around delimiters and split
+ arr = str.replace(/\s*:\s*/g, ":").replace(/\s*,\s*/g, ",").split(",");
+
+ // parse string
+ for (i = 0, len = arr.length; i < len; i++) {
+ option = arr[i];
+
+ // Ignore urls and string without colon delimiters
+ if (option.search(/^(http|https|ftp):\/\//) !== -1 ||
+ option.search(":") === -1)
+ {
+ break;
+ }
+
+ delimiterIndex = option.indexOf(":");
+ prop = option.substring(0, delimiterIndex);
+ val = option.substring(delimiterIndex + 1);
+
+ // if val is an empty string, make it undefined
+ if (!val) {
+ val = undefined;
+ }
+
+ // convert string value if it is like a boolean
+ if (typeof val === "string") {
+ val = val === "true" || (val === "false" ? false : val);
+ }
+
+ // convert string value if it is like a number
+ if (typeof val === "string") {
+ val = !isNaN(val) ? +val : val;
+ }
+
+ obj[prop] = val;
+ }
+
+ // if nothing is parsed
+ if (prop == null && val == null) {
+ return str;
+ }
+
+ return obj;
+ }
+
+ /**
+ * Parse position option
+ * @param {String} str
+ * @returns {Object}
+ * @private
+ */
+ function parsePosition(str) {
+ str = "" + str;
+
+ // default value is a center
+ var args = str.split(/\s+/),
+ x = "50%", y = "50%",
+ len, arg,
+ i;
+
+ for (i = 0, len = args.length; i < len; i++) {
+ arg = args[i];
+
+ // convert values
+ if (arg === "left") {
+ x = "0%";
+ } else if (arg === "right") {
+ x = "100%";
+ } else if (arg === "top") {
+ y = "0%";
+ } else if (arg === "bottom") {
+ y = "100%";
+ } else if (arg === "center") {
+ if (i === 0) {
+ x = "50%";
+ } else {
+ y = "50%";
+ }
+ } else {
+ if (i === 0) {
+ x = arg;
+ } else {
+ y = arg;
+ }
+ }
+ }
+
+ return { x: x, y: y };
+ }
+
+ /**
+ * Search poster
+ * @param {String} path
+ * @param {Function} callback
+ * @private
+ */
+ function findPoster(path, callback) {
+ var onLoad = function() {
+ callback(this.src);
+ };
+
+ $("").load(onLoad);
+ $("").load(onLoad);
+ $("").load(onLoad);
+ $("").load(onLoad);
+ }
+
+ /**
+ * Vide constructor
+ * @param {HTMLElement} element
+ * @param {Object|String} path
+ * @param {Object|String} options
+ * @constructor
+ */
+ function Vide(element, path, options) {
+ this.$element = $(element);
+
+ // parse path
+ if (typeof path === "string") {
+ path = parseOptions(path);
+ }
+
+ // parse options
+ if (!options) {
+ options = {};
+ } else if (typeof options === "string") {
+ options = parseOptions(options);
+ }
+
+ // remove extension
+ if (typeof path === "string") {
+ path = path.replace(/\.\w*$/, "");
+ } else if (typeof path === "object") {
+ for (var i in path) {
+ if (path.hasOwnProperty(i)) {
+ path[i] = path[i].replace(/\.\w*$/, "");
+ }
+ }
+ }
+
+ this.settings = $.extend({}, defaults, options);
+ this.path = path;
+
+ this.init();
+ }
+
+ /**
+ * Initialization
+ * @public
+ */
+ Vide.prototype.init = function() {
+ var vide = this,
+ position = parsePosition(vide.settings.position),
+ sources,
+ poster;
+
+ // Set video wrapper styles
+ vide.$wrapper = $("
").css({
+ "position": "absolute",
+ "z-index": -1,
+ "top": 0,
+ "left": 0,
+ "bottom": 0,
+ "right": 0,
+ "overflow": "hidden",
+ "-webkit-background-size": "cover",
+ "-moz-background-size": "cover",
+ "-o-background-size": "cover",
+ "background-size": "cover",
+ "background-repeat": "no-repeat",
+ "background-position": position.x + " " + position.y
+ });
+
+ // Get poster path
+ poster = vide.path;
+ if (typeof vide.path === "object") {
+ if (vide.path.poster) {
+ poster = vide.path.poster;
+ } else {
+ if (vide.path.mp4) {
+ poster = vide.path.mp4;
+ } else if (vide.path.webm) {
+ poster = vide.path.webm;
+ } else if (vide.path.ogv) {
+ poster = vide.path.ogv;
+ }
+ }
+ }
+
+ // Set video poster
+// if (vide.settings.posterType === "detect") {
+// findPoster(poster, function(url) {
+// vide.$wrapper.css("background-image", "url(" + url + ")");
+// });
+// } else if (vide.settings.posterType !== "none") {
+// vide.$wrapper
+// .css("background-image", "url(" + poster + "." + vide.settings.posterType + ")");
+// }
+
+ // if parent element has a static position, make it relative
+ if (vide.$element.css("position") === "static") {
+ vide.$element.css("position", "relative");
+ }
+
+ vide.$element.prepend(vide.$wrapper);
+
+ if (!isIOS && !isAndroid) {
+ sources = "";
+
+ if (typeof vide.path === "object") {
+ if (vide.path.mp4) {
+ sources += "";
+ }
+ if (vide.path.webm) {
+ sources += "";
+ }
+ if (vide.path.ogv) {
+ sources += "";
+ }
+
+ vide.$video = $("");
+ } else {
+ vide.$video = $("");
+ }
+
+ // Disable visibility, while loading
+ vide.$video.css("visibility", "hidden");
+
+ // Set video properties
+ vide.$video.prop({
+ autoplay: vide.settings.autoplay,
+ loop: vide.settings.loop,
+ volume: vide.settings.volume,
+ muted: vide.settings.muted,
+ playbackRate: vide.settings.playbackRate
+ });
+
+ // Append video
+ vide.$wrapper.append(vide.$video);
+
+ // Video alignment
+ vide.$video.css({
+ "margin": "auto",
+ "position": "absolute",
+ "z-index": -1,
+ "top": position.y,
+ "left": position.x,
+ "-webkit-transform": "translate(-" + position.x + ", -" + position.y + ")",
+ "-ms-transform": "translate(-" + position.x + ", -" + position.y + ")",
+ "transform": "translate(-" + position.x + ", -" + position.y + ")"
+ });
+
+ // resize video, when it's loaded
+ vide.$video.bind("loadedmetadata." + pluginName, function() {
+ vide.$video.css("visibility", "visible");
+ vide.resize();
+ vide.$wrapper.css("background-image", "none");
+ });
+
+ // resize event is available only for 'window',
+ // use another code solutions to detect DOM elements resizing
+ vide.$element.bind("resize." + pluginName, function() {
+ if (vide.settings.resizing) {
+ vide.resize();
+ }
+ });
+ }
+ };
+
+ /**
+ * Get video element of the background
+ * @returns {HTMLVideoElement|null}
+ * @public
+ */
+ Vide.prototype.getVideoObject = function() {
+ return this.$video ? this.$video[0] : null;
+ };
+
+ /**
+ * Resize video background
+ * @public
+ */
+ Vide.prototype.resize = function() {
+ if (!this.$video) {
+ return;
+ }
+
+ var
+ // get native video size
+ videoHeight = this.$video[0].videoHeight,
+ videoWidth = this.$video[0].videoWidth,
+
+ // get wrapper size
+ wrapperHeight = this.$wrapper.height(),
+ wrapperWidth = this.$wrapper.width();
+
+ if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) {
+ this.$video.css({
+ "width": wrapperWidth + 2,
+
+ // +2 pixels to prevent empty space after transformation
+ "height": "auto"
+ });
+ } else {
+ this.$video.css({
+ "width": "auto",
+
+ // +2 pixels to prevent empty space after transformation
+ "height": wrapperHeight + 2
+ });
+ }
+ };
+
+ /**
+ * Destroy video background
+ * @public
+ */
+ Vide.prototype.destroy = function() {
+ this.$element.unbind(pluginName);
+
+ if (this.$video) {
+ this.$video.unbind(pluginName);
+ }
+
+ delete $[pluginName].lookup[this.index];
+ this.$element.removeData(pluginName);
+ this.$wrapper.remove();
+ };
+
+ /**
+ * Special plugin object for instances.
+ * @type {Object}
+ * @public
+ */
+ $[pluginName] = {
+ lookup: []
+ };
+
+ /**
+ * Plugin constructor
+ * @param {Object|String} path
+ * @param {Object|String} options
+ * @returns {JQuery}
+ * @constructor
+ */
+ $.fn[pluginName] = function(path, options) {
+ var instance;
+
+ this.each(function() {
+ instance = $.data(this, pluginName);
+
+ if (instance) {
+
+ // destroy plugin instance if exists
+ instance.destroy();
+ }
+
+ // create plugin instance
+ instance = new Vide(this, path, options);
+ instance.index = $[pluginName].lookup.push(instance) - 1;
+ $.data(this, pluginName, instance);
+ });
+
+ return this;
+ };
+
+ $(document).ready(function() {
+
+ // window resize event listener
+ $(window).bind("resize." + pluginName, function() {
+ for (var len = $[pluginName].lookup.length, i = 0, instance; i < len; i++) {
+ instance = $[pluginName].lookup[i];
+
+ if (instance && instance.settings.resizing) {
+ instance.resize();
+ }
+ }
+ });
+
+ // Auto initialization.
+ // Add 'data-vide-bg' attribute with a path to the video without extension.
+ // Also you can pass options throw the 'data-vide-options' attribute.
+ // 'data-vide-options' must be like "muted: false, volume: 0.5".
+ $(document).find("[data-" + pluginName + "-bg]").each(function(i, element) {
+ var $element = $(element),
+ options = $element.data(pluginName + "-options"),
+ path = $element.data(pluginName + "-bg");
+
+ $element[pluginName](path, options);
+ });
+ });
+})(window.jQuery, window, document, navigator);
diff --git a/plugins/video-on-login-screen/js/vide/jquery.vide.min.js b/plugins/video-on-login-screen/js/vide/jquery.vide.min.js
new file mode 100644
index 000000000..20f3182cb
--- /dev/null
+++ b/plugins/video-on-login-screen/js/vide/jquery.vide.min.js
@@ -0,0 +1,9 @@
+/*
+ * Vide - v0.3.0
+ * Easy as hell jQuery plugin for video backgrounds.
+ * http://vodkabears.github.io/vide/
+ *
+ * Made by Ilya Makarov
+ * Under MIT License
+ */
+!function(a,b,c,d){"use strict";function e(a){var b,c,d,e,f,g,h,i={};for(f=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,",").split(","),h=0,g=f.length;g>h&&(c=f[h],-1===c.search(/^(http|https|ftp):\/\//)&&-1!==c.search(":"));h++)b=c.indexOf(":"),d=c.substring(0,b),e=c.substring(b+1),e||(e=void 0),"string"==typeof e&&(e="true"===e||("false"===e?!1:e)),"string"==typeof e&&(e=isNaN(e)?e:+e),i[d]=e;return null==d&&null==e?a:i}function f(a){a=""+a;var b,c,d,e=a.split(/\s+/),f="50%",g="50%";for(d=0,b=e.length;b>d;d++)c=e[d],"left"===c?f="0%":"right"===c?f="100%":"top"===c?g="0%":"bottom"===c?g="100%":"center"===c?0===d?f="50%":g="50%":0===d?f=c:g=c;return{x:f,y:g}}function g(b,c){var d=function(){c(this.src)};a("").load(d),a("").load(d),a("").load(d),a("").load(d)}function h(b,c,d){if(this.$element=a(b),"string"==typeof c&&(c=e(c)),d?"string"==typeof d&&(d=e(d)):d={},"string"==typeof c)c=c.replace(/\.\w*$/,"");else if("object"==typeof c)for(var f in c)c.hasOwnProperty(f)&&(c[f]=c[f].replace(/\.\w*$/,""));this.settings=a.extend({},j,d),this.path=c,this.init()}var i="vide",j={volume:1,playbackRate:1,muted:!0,loop:!0,autoplay:!0,position:"50% 50%",posterType:"detect",resizing:!0},k=/iPad|iPhone|iPod/i.test(d.userAgent),l=/Android/i.test(d.userAgent);h.prototype.init=function(){var b,c,d=this,e=f(d.settings.position);d.$wrapper=a("
\ No newline at end of file
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/Login.html b/rainloop/v/0.0.0/app/templates/Views/User/Login.html
index e6c597207..2f0b2f705 100644
--- a/rainloop/v/0.0.0/app/templates/Views/User/Login.html
+++ b/rainloop/v/0.0.0/app/templates/Views/User/Login.html
@@ -1,4 +1,3 @@
-
diff --git a/rainloop/v/0.0.0/themes/RainDrops/images/background.jpg b/rainloop/v/0.0.0/themes/RainDrops/images/background.jpg
new file mode 100644
index 000000000..127eeae7f
Binary files /dev/null and b/rainloop/v/0.0.0/themes/RainDrops/images/background.jpg differ
diff --git a/rainloop/v/0.0.0/themes/RainDrops/images/preview.png b/rainloop/v/0.0.0/themes/RainDrops/images/preview.png
new file mode 100644
index 000000000..d7bbaa210
Binary files /dev/null and b/rainloop/v/0.0.0/themes/RainDrops/images/preview.png differ
diff --git a/rainloop/v/0.0.0/themes/RainDrops/styles.less b/rainloop/v/0.0.0/themes/RainDrops/styles.less
new file mode 100644
index 000000000..d893936fa
--- /dev/null
+++ b/rainloop/v/0.0.0/themes/RainDrops/styles.less
@@ -0,0 +1,58 @@
+
+// MAIN
+@main-color: #333;
+@main-background-color: #354C17;
+@main-background-image: "images/background.jpg";
+@main-background-size: cover;
+
+// LOADING
+@loading-color: #fff;
+@loading-text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5);
+
+// LOGIN
+@login-color: #eee;
+@login-background-color: #2b333d;
+@login-rgba-background-color: rgba(0,0,0,0.5);
+@login-box-shadow: 0px 2px 10px rgba(0,0,0,0.2);
+@login-border: none;
+@login-border-radius: 7px;
+@powered-color: #ddd;
+@languages-color: #ddd;
+
+// MENU
+@dropdown-menu-color: #333;
+@dropdown-menu-background-color: #fff;
+@dropdown-menu-hover-background-color: #48525C;
+@dropdown-menu-hover-color: #eee;
+@dropdown-menu-disable-color: #999;
+
+// FOLDERS
+@folders-color: #fff;
+@folders-disabled-color: #aaa;
+@folders-selected-color: #fff;
+@folders-selected-background-color: #2b333d;
+@folders-selected-rgba-background-color: rgba(255,255,255,0.3);
+@folders-focused-color: #fff;
+@folders-focused-background-color: #2b333d;
+@folders-focused-rgba-background-color: rgba(255,255,255,0.3);
+@folders-hover-color: #fff;
+@folders-hover-background-color: #2b333d;
+@folders-hover-rgba-background-color: rgba(255,255,255,0.3);
+@folders-drop-color: #fff;
+@folders-drop-background-color: #2b333d;
+@folders-drop-rgba-background-color: rgba(255,255,255,0.3);
+
+// SETTINGS
+@settings-menu-color: #fff;
+@settings-menu-disabled-color: #aaa;
+@settings-menu-selected-color: #fff;
+@settings-menu-selected-background-color: #2b333d;
+@settings-menu-selected-rgba-background-color: rgba(255,255,255,0.3);
+@settings-menu-hover-color: #fff;
+@settings-menu-hover-background-color: #2b333d;
+@settings-menu-hover-rgba-background-color: rgba(255,255,255,0.3);
+
+// MESSAGE LIST
+@message-list-toolbar-background-color: #eee;
+@message-list-toolbar-gradient-start: #f4f4f4;
+@message-list-toolbar-gradient-end: #dfdfdf;
diff --git a/vendors/rl/LICENSE b/vendors/rl/LICENSE
index 11e030da4..90075cb4d 100644
--- a/vendors/rl/LICENSE
+++ b/vendors/rl/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2014 RainLoop Team
+Copyright 2015 RainLoop Team
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/vendors/rl/rl-1.4.min.js b/vendors/rl/rl-1.4.min.js
deleted file mode 100644
index 94278f343..000000000
--- a/vendors/rl/rl-1.4.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! RainLoop Index Helper v1.4 (c) 2014 RainLoop Team; Licensed under MIT */
-!function(t,n,e){function r(){}r.prototype.s=t.sessionStorage,r.prototype.t=t.top||t,r.prototype.getHash=function(){var t=null;if(this.s)t=this.s.getItem("__rlA")||null;else if(this.t){var n=this.t.name&&e&&"{"===this.t.name.toString().substr(0,1)?e.parse(this.t.name.toString()):null;t=n?n.__rlA||null:null}return t},r.prototype.setHash=function(){var n=t.rainloopAppData,r=null;this.s?this.s.setItem("__rlA",n&&n.AuthAccountHash?n.AuthAccountHash:""):this.t&&e&&(r={},r.__rlA=n&&n.AuthAccountHash?n.AuthAccountHash:"",this.t.name=e.stringify(r))},r.prototype.clearHash=function(){this.s?this.s.setItem("__rlA",""):this.t&&(this.t.name="")},t._rlhh=new r,t.__rlah=function(){return t._rlhh?t._rlhh.getHash():null},t.__rlah_set=function(){t._rlhh&&t._rlhh.setHash()},t.__rlah_clear=function(){t._rlhh&&t._rlhh.clearHash()},t.__includeScr=function(t){n.write(unescape('%3Cscript data-cfasync="false" type="text/javascript" src="'+t+'"%3E%3C/script%3E'))},t.__showError=function(e){var r=n.getElementById("rl-loading"),s=n.getElementById("rl-loading-error"),l=n.getElementById("rl-loading-error-additional");r&&(r.style.display="none"),s&&(s.style.display="block"),l&&e&&(l.style.display="block",l.innerHTML=e),t.SimplePace&&t.SimplePace.set(100)},t.__simplePace=function(n){t.SimplePace&&t.SimplePace.add(n)},t.__runBoot=function(n,e){t.__APP_BOOT&&!n?t.__APP_BOOT(function(t){t||__showError(e)}):__showError(e)}}(window,window.document,window.JSON);
diff --git a/vendors/rl/rl-1.5.min.js b/vendors/rl/rl-1.5.min.js
new file mode 100644
index 000000000..fe74cd4d5
--- /dev/null
+++ b/vendors/rl/rl-1.5.min.js
@@ -0,0 +1,2 @@
+/*! RainLoop Index Helper v1.5 (c) 2015 RainLoop Team; Licensed under MIT */
+!function(t,e,n){function r(){}r.prototype.s=t.sessionStorage,r.prototype.t=t.top||t,r.prototype.getHash=function(){var t=null;if(this.s)t=this.s.getItem("__rlA")||null;else if(this.t){var e=this.t.name&&n&&"{"===this.t.name.toString().substr(0,1)?n.parse(this.t.name.toString()):null;t=e?e.__rlA||null:null}return t},r.prototype.setHash=function(){var e=t.rainloopAppData,r=null;this.s?this.s.setItem("__rlA",e&&e.AuthAccountHash?e.AuthAccountHash:""):this.t&&n&&(r={},r.__rlA=e&&e.AuthAccountHash?e.AuthAccountHash:"",this.t.name=n.stringify(r))},r.prototype.clearHash=function(){this.s?this.s.setItem("__rlA",""):this.t&&(this.t.name="")},t._rlhh=new r,t.__rlah=function(){return t._rlhh?t._rlhh.getHash():null},t.__rlah_set=function(){t._rlhh&&t._rlhh.setHash()},t.__rlah_clear=function(){t._rlhh&&t._rlhh.clearHash()},t.__includeScr=function(t){e.write(unescape('%3Cscript data-cfasync="false" type="text/javascript" src="'+t+'"%3E%3C/script%3E'))},t.__includeStyle=function(t){e.write(unescape("%3Cstyle%3E"+t+'"%3E%3C/style%3E'))},t.__showError=function(n){var r=e.getElementById("rl-loading"),s=e.getElementById("rl-loading-error"),l=e.getElementById("rl-loading-error-additional");r&&(r.style.display="none"),s&&(s.style.display="block"),l&&n&&(l.style.display="block",l.innerHTML=n),t.SimplePace&&t.SimplePace.set(100)},t.__simplePace=function(e){t.SimplePace&&t.SimplePace.add(e)},t.__runBoot=function(e,n){t.__APP_BOOT&&!e?t.__APP_BOOT(function(t){t||__showError(n)}):__showError(n)}}(window,window.document,window.JSON);
diff --git a/vendors/rl/rl.js b/vendors/rl/rl.js
index d960ae765..98be30315 100644
--- a/vendors/rl/rl.js
+++ b/vendors/rl/rl.js
@@ -1,4 +1,4 @@
-/*! RainLoop Index Helper v1.3 (c) 2014 RainLoop Team; Licensed under MIT */
+/*! RainLoop Index Helper v1.5 (c) 2015 RainLoop Team; Licensed under MIT */
(function (window, document, JSON, undefined) {
/**
@@ -69,6 +69,10 @@
document.write(unescape('%3Csc' + 'ript data-cfasync="false" type="text/jav' + 'ascr' + 'ipt" sr' + 'c="' + sSrc + '"%3E%3C/' + 'scr' + 'ipt%3E'));
};
+ window['__includeStyle'] = function (sStyles) {
+ document.write(unescape('%3Csty' + 'le%3E' + sStyles + '"%3E%3C/' + 'sty' + 'le%3E'));
+ };
+
window['__showError'] = function (sAdditionalError) {
var oR = document.getElementById('rl-loading'),
oL = document.getElementById('rl-loading-error'),