diff --git a/mailadmin/static/app/scripts/main.js b/mailadmin/static/app/scripts/main.js
index 1e90705..7fb3180 100644
--- a/mailadmin/static/app/scripts/main.js
+++ b/mailadmin/static/app/scripts/main.js
@@ -29,11 +29,7 @@
return _.unique(emails);
}
- function renderAlert(msg, alertClass, iconClass) {
- return nunjucks.render('alert.html', {'msg': msg, 'alertClass': alertClass, 'iconClass': iconClass});
- }
- function filter_lists_with_members(term, pushState) {
- console.log(pushState);
+ function filter_lists(term, pushState, with_members) {
if(pushState) {
/* Update query string */
window.history.pushState(null, null, '/lists/?q=' + term);
@@ -47,7 +43,7 @@
var hits = _.filter(members, function(el) {
return re_term.test($(el).text());
});
- if(re_term.test(cur_name) || hits.length !== 0) {
+ if(re_term.test(cur_name) || (hits.length !== 0 && with_members) ) {
$(this).removeClass('hidden');
} else {
$(this).addClass('hidden');
@@ -55,21 +51,14 @@
});
}
- // TODO combine with above
- function filter_lists(prefix, pushState) {
- if(pushState) {
- /* Update query string */
- window.history.pushState(null, null, '/lists/?q=^' + prefix+'-');
- }
- /* Filter lists */
- var re_prefix = new RegExp('^'+prefix+'-');
- $(".fwdlist").each(function() {
- var cur_name = $(this).attr('data-list-name');
- if(re_prefix.test(cur_name)) {
- $(this).removeClass('hidden');
- } else {
- $(this).addClass('hidden');
- }
+ function notify(title, text, type) {
+ /* types: info, success, error */
+ new PNotify({
+ title: title,
+ text: text,
+ type: type,
+ delay: 3000,
+ animate_speed: 'fast'
});
}
@@ -93,6 +82,12 @@
$(".orgunit-list").append(ou_html);
var ous_html = nunjucks.render('orgunits_select.html', { orgunits: data });
$(".orgunit-select-container").html(ous_html);
+ var q_term = getParameterByName('q');
+ if(q_term && q_term[0] === '^') {
+ var prefix = q_term.slice(1, -1);
+ $(".orgunit-list li").removeClass('active');
+ $('.orgunit-list li a[data-prefix="'+prefix+'"]').parent().toggleClass('active');
+ }
/* New list */
var new_list_html = nunjucks.render('new_list.html', {
@@ -100,6 +95,7 @@
api_urls: api_urls
});
+
$('.new-list').html(new_list_html);
$('.new-list .js-new-list-name').on('keyup', function() {
var new_list_name = $('.new-list .js-new-list-name').val();
@@ -135,13 +131,13 @@
/* Validate */
var new_list_name = $('.new-list .js-new-list-name').val();
if(new_list_name.length === 0) {
- $('.new-list-result').html(renderAlert('Navn på epostliste mangler', 'danger'));
+ notify('Ny epostliste', 'Navn på epostliste mangler', 'error');
return;
}
var text = $('.new-list textarea').val();
var emails = parseEmails(text);
if(emails.length === 0) {
- $('.new-list-result').html(renderAlert('Ingen eposter i epost-feltet...', 'danger'));
+ notify('Ny epostliste', 'Ingen eposter i epost-feltet...', 'error');
return;
}
// Valid form
@@ -157,7 +153,7 @@
api_urls.fwd_create,
new_list[dest],
function() {
- $('.new-list-result').html(renderAlert('Lagt til ny liste: ' + dest, 'success'));
+ notify('Lagt til', 'Ny liste: ' + dest + 'lagt til', 'success');
$('.new-list textarea').val('');
$('.new-list .js-new-list-name').val('');
},
@@ -178,7 +174,7 @@
$(this).parent().toggleClass('active');
$('.orgunits-select').val(prefix);
- filter_lists(prefix, true);
+ filter_lists('^'+prefix+'-', true);
$('.js-lists-filter-field').val('');
});
$(".orgunits-select").on('change', function() {
@@ -188,7 +184,7 @@
$(".orgunit-list li").removeClass('active');
$(".orgunit-list li a[data-prefix="+prefix+"]").parent().toggleClass('active');
- filter_lists(prefix, true);
+ filter_lists('^'+prefix+'-', true);
$('.js-lists-filter-field').val('');
});
});
@@ -196,11 +192,11 @@
/* Load forwards from API and put on page */
$.getJSON(api_urls.fwd_list, function(data) {
if( !data.cpanelresult ) {
- forwards_container.html(renderAlert("No results.", 'info'));
+ notify("Ingen lister.", 'Fant ingen tilhørende lister', 'info');
return;
}
if( 'error' in data.cpanelresult ) {
- forwards_container.html(renderAlert("Klarte ikke hente epostlister fra APIet: "+data.cpanelresult.error, 'danger'));
+ notify('Oops!', 'Klarte ikke hente epostlister fra APIet: '+data.cpanelresult.error, 'error');
return;
}
@@ -219,13 +215,13 @@
if(q_term[0] === '^') {
filter_lists(q_term, false);
} else {
- filter_lists_with_members(q_term, false);
+ filter_lists(q_term, false, true);
}
}
/* Filter by search query */
$('.js-lists-filter-field').on('keyup', function() {
var term = $(this).val();
- filter_lists_with_members(term, true);
+ filter_lists(term, true, true);
$(".orgunit-list li").removeClass('active');
$(".orgunit-list li:first-child").toggleClass('active');
});
diff --git a/mailadmin/static/app/styles/_pnotify.core.scss b/mailadmin/static/app/styles/_pnotify.core.scss
new file mode 100644
index 0000000..3f6b7a7
--- /dev/null
+++ b/mailadmin/static/app/styles/_pnotify.core.scss
@@ -0,0 +1,56 @@
+/*
+Author : Hunter Perrin
+Version : 2.0.1
+Link : http://sciactive.com/pnotify/
+*/
+/* -- Notice */
+.ui-pnotify {
+top: 25px;
+right: 25px;
+position: absolute;
+height: auto;
+/* Ensures notices are above everything */
+z-index: 9999;
+}
+/* Hides position: fixed from IE6 */
+html > body > .ui-pnotify {
+position: fixed;
+}
+.ui-pnotify .ui-pnotify-shadow {
+-webkit-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+-moz-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+}
+.ui-pnotify-container {
+background-position: 0 0;
+padding: .8em;
+height: 100%;
+margin: 0;
+}
+.ui-pnotify-sharp {
+-webkit-border-radius: 0;
+-moz-border-radius: 0;
+border-radius: 0;
+}
+.ui-pnotify-title {
+display: block;
+margin-bottom: .4em;
+margin-top: 0;
+}
+.ui-pnotify-text {
+display: block;
+}
+.ui-pnotify-icon, .ui-pnotify-icon span {
+display: block;
+float: left;
+margin-right: .2em;
+}
+/* Alternate stack initial positioning. */
+.ui-pnotify.stack-topleft, .ui-pnotify.stack-bottomleft {
+left: 25px;
+right: auto;
+}
+.ui-pnotify.stack-bottomright, .ui-pnotify.stack-bottomleft {
+bottom: 25px;
+top: auto;
+}
diff --git a/mailadmin/static/app/styles/main.scss b/mailadmin/static/app/styles/main.scss
index c628c9e..84894d9 100644
--- a/mailadmin/static/app/styles/main.scss
+++ b/mailadmin/static/app/styles/main.scss
@@ -1,6 +1,7 @@
$icon-font-path: "../fonts/";
@import "../../bower_components/bootstrap-sass-official/vendor/assets/stylesheets/bootstrap";
+@import "pnotify.core";
@import "animations";
.login-form {
@@ -14,10 +15,12 @@ $icon-font-path: "../fonts/";
/* Hide for mobile, show later */
.sidebar {
- display: none;
- @media (min-width: 768px) {
- display: block;
- }
+ .orgunit-list {
+ display: none;
+ @media (min-width: 768px) {
+ display: block;
+ }
+ }
}
.search-field {
margin-bottom: 20px;
diff --git a/mailadmin/static/app/templates/orgunits.html b/mailadmin/static/app/templates/orgunits.html
index 635108e..b8e326c 100644
--- a/mailadmin/static/app/templates/orgunits.html
+++ b/mailadmin/static/app/templates/orgunits.html
@@ -1,4 +1,4 @@
-
Alle
+Alle
{% for ou in orgunits %}
-{{ ou.name }}
+{{ ou.name }} ({{ ou.prefix }})
{% endfor %}
\ No newline at end of file
diff --git a/mailadmin/static/bower.json b/mailadmin/static/bower.json
index 33c568f..e7ed933 100644
--- a/mailadmin/static/bower.json
+++ b/mailadmin/static/bower.json
@@ -5,7 +5,8 @@
"bootstrap-sass-official": "~3.1.1",
"jquery": "~1.11.0",
"underscore": "~1.7.0",
- "nunjucks": "~1.1.0"
+ "nunjucks": "~1.1.0",
+ "pnotify": "~2.0.1"
},
"devDependencies": {}
}
diff --git a/mailadmin/static/bower_components/pnotify/.bower.json b/mailadmin/static/bower_components/pnotify/.bower.json
new file mode 100644
index 0000000..3ab279c
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/.bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "pnotify",
+ "version": "2.0.1",
+ "description": "JavaScript notification plugin.",
+ "main": [
+ "pnotify.core.js",
+ "pnotify.buttons.js",
+ "pnotify.callbacks.js",
+ "pnotify.confirm.js",
+ "pnotify.desktop.js",
+ "pnotify.history.js",
+ "pnotify.nonblock.js",
+ "pnotify.core.css",
+ "pnotify.buttons.css",
+ "pnotify.history.css"
+ ],
+ "ignore": [
+ "build-cache/",
+ "build-tools/",
+ "includes/",
+ "oxygen/",
+ "devnote*.*",
+ "index.html",
+ "README.md",
+ "testing.html",
+ "buildcustom.php",
+ "pnotify.tooltip.js",
+ "pnotify.mobile.js"
+ ],
+ "homepage": "https://github.com/sciactive/pnotify",
+ "_release": "2.0.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "2.0.1",
+ "commit": "30d5ba591a20982d701ea28ad740e8f67823c137"
+ },
+ "_source": "git://github.com/sciactive/pnotify.git",
+ "_target": "~2.0.1",
+ "_originalSource": "pnotify",
+ "_direct": true
+}
\ No newline at end of file
diff --git a/mailadmin/static/bower_components/pnotify/.gitignore b/mailadmin/static/bower_components/pnotify/.gitignore
new file mode 100644
index 0000000..835500e
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/.gitignore
@@ -0,0 +1,4 @@
+/pnotify.*.min.js
+/pnotify.*.min.css
+/build-cache/*.js
+/build-cache/*.css
\ No newline at end of file
diff --git a/mailadmin/static/bower_components/pnotify/bower.json b/mailadmin/static/bower_components/pnotify/bower.json
new file mode 100644
index 0000000..4db4ab0
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "pnotify",
+ "version": "2.0.1",
+ "description": "JavaScript notification plugin.",
+ "main": [
+ "pnotify.core.js",
+ "pnotify.buttons.js",
+ "pnotify.callbacks.js",
+ "pnotify.confirm.js",
+ "pnotify.desktop.js",
+ "pnotify.history.js",
+ "pnotify.nonblock.js",
+ "pnotify.core.css",
+ "pnotify.buttons.css",
+ "pnotify.history.css"
+ ],
+ "ignore": [
+ "build-cache/",
+ "build-tools/",
+ "includes/",
+ "oxygen/",
+ "devnote*.*",
+ "index.html",
+ "README.md",
+ "testing.html",
+ "buildcustom.php",
+ "pnotify.tooltip.js",
+ "pnotify.mobile.js"
+ ]
+}
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.buttons.css b/mailadmin/static/bower_components/pnotify/pnotify.buttons.css
new file mode 100644
index 0000000..68f3a12
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.buttons.css
@@ -0,0 +1,4 @@
+.ui-pnotify-closer, .ui-pnotify-sticker {
+float: right;
+margin-left: .2em;
+}
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.buttons.js b/mailadmin/static/bower_components/pnotify/pnotify.buttons.js
new file mode 100644
index 0000000..1e5006d
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.buttons.js
@@ -0,0 +1,132 @@
+// Buttons
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.buttons', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ PNotify.prototype.options.buttons = {
+ // Provide a button for the user to manually close the notice.
+ closer: true,
+ // Only show the closer button on hover.
+ closer_hover: true,
+ // Provide a button for the user to manually stick the notice.
+ sticker: true,
+ // Only show the sticker button on hover.
+ sticker_hover: true,
+ // The various displayed text, helps facilitating internationalization.
+ labels: {
+ close: "Close",
+ stick: "Stick"
+ }
+ };
+ PNotify.prototype.modules.buttons = {
+ // This lets us update the options available in the closures.
+ myOptions: null,
+
+ closer: null,
+ sticker: null,
+
+ init: function(notice, options){
+ var that = this;
+ this.myOptions = options;
+ notice.elem.on({
+ "mouseenter": function(e){
+ // Show the buttons.
+ if (that.myOptions.sticker && !(notice.options.nonblock && notice.options.nonblock.nonblock)) that.sticker.trigger("pnotify_icon").css("visibility", "visible");
+ if (that.myOptions.closer && !(notice.options.nonblock && notice.options.nonblock.nonblock)) that.closer.css("visibility", "visible");
+ },
+ "mouseleave": function(e){
+ // Hide the buttons.
+ if (that.myOptions.sticker_hover)
+ that.sticker.css("visibility", "hidden");
+ if (that.myOptions.closer_hover)
+ that.closer.css("visibility", "hidden");
+ }
+ });
+
+ // Provide a button to stick the notice.
+ this.sticker = $("", {
+ "class": "ui-pnotify-sticker",
+ "css": {"cursor": "pointer", "visibility": options.sticker_hover ? "hidden" : "visible"},
+ "click": function(){
+ notice.options.hide = !notice.options.hide;
+ if (notice.options.hide)
+ notice.queueRemove();
+ else
+ notice.cancelRemove();
+ $(this).trigger("pnotify_icon");
+ }
+ })
+ .bind("pnotify_icon", function(){
+ $(this).children().removeClass(notice.styles.pin_up+" "+notice.styles.pin_down).addClass(notice.options.hide ? notice.styles.pin_up : notice.styles.pin_down);
+ })
+ .append($("", {"class": notice.styles.pin_up, "title": options.labels.stick}))
+ .prependTo(notice.container);
+ if (!options.sticker || (notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.sticker.css("display", "none");
+
+ // Provide a button to close the notice.
+ this.closer = $("", {
+ "class": "ui-pnotify-closer",
+ "css": {"cursor": "pointer", "visibility": options.closer_hover ? "hidden" : "visible"},
+ "click": function(){
+ notice.remove(false);
+ that.sticker.css("visibility", "hidden");
+ that.closer.css("visibility", "hidden");
+ }
+ })
+ .append($("", {"class": notice.styles.closer, "title": options.labels.close}))
+ .prependTo(notice.container);
+ if (!options.closer || (notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.closer.css("display", "none");
+ },
+ update: function(notice, options){
+ this.myOptions = options;
+ // Update the sticker and closer buttons.
+ if (!options.closer || (notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.closer.css("display", "none");
+ else if (options.closer)
+ this.closer.css("display", "block");
+ if (!options.sticker || (notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.sticker.css("display", "none");
+ else if (options.sticker)
+ this.sticker.css("display", "block");
+ // Update the sticker icon.
+ this.sticker.trigger("pnotify_icon");
+ // Update the hover status of the buttons.
+ if (options.sticker_hover)
+ this.sticker.css("visibility", "hidden");
+ else if (!(notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.sticker.css("visibility", "visible");
+ if (options.closer_hover)
+ this.closer.css("visibility", "hidden");
+ else if (!(notice.options.nonblock && notice.options.nonblock.nonblock))
+ this.closer.css("visibility", "visible");
+ }
+ };
+ $.extend(PNotify.styling.jqueryui, {
+ closer: "ui-icon ui-icon-close",
+ pin_up: "ui-icon ui-icon-pin-w",
+ pin_down: "ui-icon ui-icon-pin-s"
+ });
+ $.extend(PNotify.styling.bootstrap2, {
+ closer: "icon-remove",
+ pin_up: "icon-pause",
+ pin_down: "icon-play"
+ });
+ $.extend(PNotify.styling.bootstrap3, {
+ closer: "glyphicon glyphicon-remove",
+ pin_up: "glyphicon glyphicon-pause",
+ pin_down: "glyphicon glyphicon-play"
+ });
+ $.extend(PNotify.styling.fontawesome, {
+ closer: "fa fa-times",
+ pin_up: "fa fa-pause",
+ pin_down: "fa fa-play"
+ });
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.callbacks.js b/mailadmin/static/bower_components/pnotify/pnotify.callbacks.js
new file mode 100644
index 0000000..a1563a3
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.callbacks.js
@@ -0,0 +1,48 @@
+// Callbacks
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.callbacks', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ var _init = PNotify.prototype.init,
+ _open = PNotify.prototype.open,
+ _remove = PNotify.prototype.remove;
+ PNotify.prototype.init = function(){
+ if (this.options.before_init) {
+ this.options.before_init(this.options);
+ }
+ _init.apply(this, arguments);
+ if (this.options.after_init) {
+ this.options.after_init(this);
+ }
+ };
+ PNotify.prototype.open = function(){
+ var ret;
+ if (this.options.before_open) {
+ ret = this.options.before_open(this);
+ }
+ if (ret !== false) {
+ _open.apply(this, arguments);
+ if (this.options.after_open) {
+ this.options.after_open(this);
+ }
+ }
+ };
+ PNotify.prototype.remove = function(timer_hide){
+ var ret;
+ if (this.options.before_close) {
+ ret = this.options.before_close(this, timer_hide);
+ }
+ if (ret !== false) {
+ _remove.apply(this, arguments);
+ if (this.options.after_close) {
+ this.options.after_close(this, timer_hide);
+ }
+ }
+ };
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.confirm.js b/mailadmin/static/bower_components/pnotify/pnotify.confirm.js
new file mode 100644
index 0000000..99156aa
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.confirm.js
@@ -0,0 +1,152 @@
+// Confirm
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.confirm', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ PNotify.prototype.options.confirm = {
+ // Make a confirmation box.
+ confirm: false,
+ // Make a prompt.
+ prompt: false,
+ // Classes to add to the input element of the prompt.
+ prompt_class: "",
+ // The default value of the prompt.
+ prompt_default: "",
+ // Whether the prompt should accept multiple lines of text.
+ prompt_multi_line: false,
+ // Where to align the buttons. (right, center, left, justify)
+ align: "right",
+ // The buttons to display, and their callbacks.
+ buttons: [
+ {
+ text: "Ok",
+ addClass: "",
+ // Whether to trigger this button when the user hits enter in a single line prompt.
+ promptTrigger: true,
+ click: function(notice, value){
+ notice.remove();
+ notice.get().trigger("pnotify.confirm", [notice, value]);
+ }
+ },
+ {
+ text: "Cancel",
+ addClass: "",
+ click: function(notice){
+ notice.remove();
+ notice.get().trigger("pnotify.cancel", notice);
+ }
+ }
+ ]
+ };
+ PNotify.prototype.modules.confirm = {
+ // The div that contains the buttons.
+ container: null,
+ // The input element of a prompt.
+ prompt: null,
+
+ init: function(notice, options){
+ this.container = $('').css('text-align', options.align).appendTo(notice.container);
+
+ if (options.confirm || options.prompt)
+ this.makeDialog(notice, options);
+ else
+ this.container.hide();
+ },
+
+ update: function(notice, options){
+ if (options.confirm) {
+ this.makeDialog(notice, options);
+ this.container.show();
+ } else {
+ this.container.hide().empty();
+ }
+ },
+
+ afterOpen: function(notice, options){
+ if (options.prompt)
+ this.prompt.focus();
+ },
+
+ makeDialog: function(notice, options) {
+ var already = false, that = this, btn, elem;
+ this.container.empty();
+ if (options.prompt) {
+ this.prompt = $('<'+(options.prompt_multi_line ? 'textarea rows="5"' : 'input type="text"')+' style="margin-bottom:5px;clear:both;" />')
+ .addClass(notice.styles.input+' '+options.prompt_class)
+ .val(options.prompt_default)
+ .appendTo(this.container);
+ }
+ for (var i in options.buttons) {
+ btn = options.buttons[i];
+ if (already)
+ this.container.append(' ');
+ else
+ already = true;
+ elem = $('')
+ .addClass(notice.styles.btn+' '+btn.addClass)
+ .text(btn.text)
+ .appendTo(this.container)
+ .on("click", (function(btn){ return function(){
+ if (typeof btn.click == "function") {
+ btn.click(notice, options.prompt ? that.prompt.val() : null);
+ }
+ }})(btn));
+ if (options.prompt && !options.prompt_multi_line && btn.promptTrigger)
+ this.prompt.keypress((function(elem){ return function(e){
+ if (e.keyCode == 13)
+ elem.click();
+ }})(elem));
+ if (notice.styles.text) {
+ elem.wrapInner('');
+ }
+ if (notice.styles.btnhover) {
+ elem.hover((function(elem){ return function(){
+ elem.addClass(notice.styles.btnhover);
+ }})(elem), (function(elem){ return function(){
+ elem.removeClass(notice.styles.btnhover);
+ }})(elem));
+ }
+ if (notice.styles.btnactive) {
+ elem.on("mousedown", (function(elem){ return function(){
+ elem.addClass(notice.styles.btnactive);
+ }})(elem)).on("mouseup", (function(elem){ return function(){
+ elem.removeClass(notice.styles.btnactive);
+ }})(elem));
+ }
+ if (notice.styles.btnfocus) {
+ elem.on("focus", (function(elem){ return function(){
+ elem.addClass(notice.styles.btnfocus);
+ }})(elem)).on("blur", (function(elem){ return function(){
+ elem.removeClass(notice.styles.btnfocus);
+ }})(elem));
+ }
+ }
+ }
+ };
+ $.extend(PNotify.styling.jqueryui, {
+ btn: "ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only",
+ btnhover: "ui-state-hover",
+ btnactive: "ui-state-active",
+ btnfocus: "ui-state-focus",
+ input: "",
+ text: "ui-button-text"
+ });
+ $.extend(PNotify.styling.bootstrap2, {
+ btn: "btn",
+ input: ""
+ });
+ $.extend(PNotify.styling.bootstrap3, {
+ btn: "btn btn-default",
+ input: "form-control"
+ });
+ $.extend(PNotify.styling.fontawesome, {
+ btn: "btn btn-default",
+ input: "form-control"
+ });
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.core.css b/mailadmin/static/bower_components/pnotify/pnotify.core.css
new file mode 100644
index 0000000..3f6b7a7
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.core.css
@@ -0,0 +1,56 @@
+/*
+Author : Hunter Perrin
+Version : 2.0.1
+Link : http://sciactive.com/pnotify/
+*/
+/* -- Notice */
+.ui-pnotify {
+top: 25px;
+right: 25px;
+position: absolute;
+height: auto;
+/* Ensures notices are above everything */
+z-index: 9999;
+}
+/* Hides position: fixed from IE6 */
+html > body > .ui-pnotify {
+position: fixed;
+}
+.ui-pnotify .ui-pnotify-shadow {
+-webkit-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+-moz-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+}
+.ui-pnotify-container {
+background-position: 0 0;
+padding: .8em;
+height: 100%;
+margin: 0;
+}
+.ui-pnotify-sharp {
+-webkit-border-radius: 0;
+-moz-border-radius: 0;
+border-radius: 0;
+}
+.ui-pnotify-title {
+display: block;
+margin-bottom: .4em;
+margin-top: 0;
+}
+.ui-pnotify-text {
+display: block;
+}
+.ui-pnotify-icon, .ui-pnotify-icon span {
+display: block;
+float: left;
+margin-right: .2em;
+}
+/* Alternate stack initial positioning. */
+.ui-pnotify.stack-topleft, .ui-pnotify.stack-bottomleft {
+left: 25px;
+right: auto;
+}
+.ui-pnotify.stack-bottomright, .ui-pnotify.stack-bottomleft {
+bottom: 25px;
+top: auto;
+}
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.core.js b/mailadmin/static/bower_components/pnotify/pnotify.core.js
new file mode 100644
index 0000000..c54d93f
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.core.js
@@ -0,0 +1,778 @@
+/*
+PNotify 2.0.1 sciactive.com/pnotify/
+(C) 2014 Hunter Perrin
+license GPL/LGPL/MPL
+*/
+/*
+ * ====== PNotify ======
+ *
+ * http://sciactive.com/pnotify/
+ *
+ * Copyright 2009-2014 Hunter Perrin
+ *
+ * Triple licensed under the GPL, LGPL, and MPL.
+ * http://gnu.org/licenses/gpl.html
+ * http://gnu.org/licenses/lgpl.html
+ * http://mozilla.org/MPL/MPL-1.1.html
+ */
+
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify', ['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function($){
+ var default_stack = {
+ dir1: "down",
+ dir2: "left",
+ push: "bottom",
+ spacing1: 25,
+ spacing2: 25,
+ context: $("body")
+ };
+ var timer, // Position all timer.
+ body,
+ jwindow = $(window);
+ // Set global variables.
+ var do_when_ready = function(){
+ body = $("body");
+ PNotify.prototype.options.stack.context = body;
+ jwindow = $(window);
+ // Reposition the notices when the window resizes.
+ jwindow.bind('resize', function(){
+ if (timer)
+ clearTimeout(timer);
+ timer = setTimeout(function(){ PNotify.positionAll(true) }, 10);
+ });
+ };
+ PNotify = function(options){
+ this.parseOptions(options);
+ this.init();
+ };
+ $.extend(PNotify.prototype, {
+ // The current version of PNotify.
+ version: "2.0.1",
+
+ // === Options ===
+
+ // Options defaults.
+ options: {
+ // The notice's title.
+ title: false,
+ // Whether to escape the content of the title. (Not allow HTML.)
+ title_escape: false,
+ // The notice's text.
+ text: false,
+ // Whether to escape the content of the text. (Not allow HTML.)
+ text_escape: false,
+ // What styling classes to use. (Can be either jqueryui or bootstrap.)
+ styling: "bootstrap3",
+ // Additional classes to be added to the notice. (For custom styling.)
+ addclass: "",
+ // Class to be added to the notice for corner styling.
+ cornerclass: "",
+ // Display the notice when it is created.
+ auto_display: true,
+ // Width of the notice.
+ width: "300px",
+ // Minimum height of the notice. It will expand to fit content.
+ min_height: "16px",
+ // Type of the notice. "notice", "info", "success", or "error".
+ type: "notice",
+ // Set icon to true to use the default icon for the selected
+ // style/type, false for no icon, or a string for your own icon class.
+ icon: true,
+ // Opacity of the notice.
+ opacity: 1,
+ // The animation to use when displaying and hiding the notice. "none",
+ // "show", "fade", and "slide" are built in to jQuery. Others require jQuery
+ // UI. Use an object with effect_in and effect_out to use different effects.
+ animation: "fade",
+ // Speed at which the notice animates in and out. "slow", "def" or "normal",
+ // "fast" or number of milliseconds.
+ animate_speed: "slow",
+ // Specify a specific duration of position animation
+ position_animate_speed: 500,
+ // Display a drop shadow.
+ shadow: true,
+ // After a delay, remove the notice.
+ hide: true,
+ // Delay in milliseconds before the notice is removed.
+ delay: 8000,
+ // Reset the hide timer if the mouse moves over the notice.
+ mouse_reset: true,
+ // Remove the notice's elements from the DOM after it is removed.
+ remove: true,
+ // Change new lines to br tags.
+ insert_brs: true,
+ // Whether to remove notices from the global array.
+ destroy: true,
+ // The stack on which the notices will be placed. Also controls the
+ // direction the notices stack.
+ stack: default_stack
+ },
+
+ // === Modules ===
+
+ // This object holds all the PNotify modules. They are used to provide
+ // additional functionality.
+ modules: {},
+ // This runs an event on all the modules.
+ runModules: function(event, arg){
+ var curArg;
+ for (var module in this.modules) {
+ curArg = ((typeof arg === "object" && module in arg) ? arg[module] : arg);
+ if (typeof this.modules[module][event] === 'function')
+ this.modules[module][event](this, typeof this.options[module] === 'object' ? this.options[module] : {}, curArg);
+ }
+ },
+
+ // === Class Variables ===
+
+ state: "initializing", // The state can be "initializing", "opening", "open", "closing", and "closed".
+ timer: null, // Auto close timer.
+ styles: null,
+ elem: null,
+ container: null,
+ title_container: null,
+ text_container: null,
+ animating: false, // Stores what is currently being animated (in or out).
+ timerHide: false, // Stores whether the notice was hidden by a timer.
+
+ // === Events ===
+
+ init: function(){
+ var that = this;
+
+ // First and foremost, we don't want our module objects all referencing the prototype.
+ this.modules = {};
+ $.extend(true, this.modules, PNotify.prototype.modules);
+
+ // Get our styling object.
+ if (typeof this.options.styling === "object") {
+ this.styles = this.options.styling;
+ } else {
+ this.styles = PNotify.styling[this.options.styling];
+ }
+
+ // Create our widget.
+ // Stop animation, reset the removal timer when the user mouses over.
+ this.elem = $("", {
+ "class": "ui-pnotify "+this.options.addclass,
+ "css": {"display": "none"},
+ "mouseenter": function(e){
+ if (that.options.mouse_reset && that.animating === "out") {
+ if (!that.timerHide)
+ return;
+ that.cancelRemove();
+ }
+ // Stop the close timer.
+ if (that.options.hide && that.options.mouse_reset) that.cancelRemove();
+ },
+ "mouseleave": function(e){
+ // Start the close timer.
+ if (that.options.hide && that.options.mouse_reset) that.queueRemove();
+ PNotify.positionAll();
+ }
+ });
+ // Create a container for the notice contents.
+ this.container = $("", {"class": this.styles.container+" ui-pnotify-container "+(this.options.type === "error" ? this.styles.error : (this.options.type === "info" ? this.styles.info : (this.options.type === "success" ? this.styles.success : this.styles.notice)))})
+ .appendTo(this.elem);
+ if (this.options.cornerclass !== "")
+ this.container.removeClass("ui-corner-all").addClass(this.options.cornerclass);
+ // Create a drop shadow.
+ if (this.options.shadow)
+ this.container.addClass("ui-pnotify-shadow");
+
+
+ // Add the appropriate icon.
+ if (this.options.icon !== false) {
+ $("", {"class": "ui-pnotify-icon"})
+ .append($("", {"class": this.options.icon === true ? (this.options.type === "error" ? this.styles.error_icon : (this.options.type === "info" ? this.styles.info_icon : (this.options.type === "success" ? this.styles.success_icon : this.styles.notice_icon))) : this.options.icon}))
+ .prependTo(this.container);
+ }
+
+ // Add a title.
+ this.title_container = $("", {
+ "class": "ui-pnotify-title"
+ })
+ .appendTo(this.container);
+ if (this.options.title === false)
+ this.title_container.hide();
+ else if (this.options.title_escape)
+ this.title_container.text(this.options.title);
+ else
+ this.title_container.html(this.options.title);
+
+ // Add text.
+ this.text_container = $("", {
+ "class": "ui-pnotify-text"
+ })
+ .appendTo(this.container);
+ if (this.options.text === false)
+ this.text_container.hide();
+ else if (this.options.text_escape)
+ this.text_container.text(this.options.text);
+ else
+ this.text_container.html(this.options.insert_brs ? String(this.options.text).replace(/\n/g, "
") : this.options.text);
+
+ // Set width and min height.
+ if (typeof this.options.width === "string")
+ this.elem.css("width", this.options.width);
+ if (typeof this.options.min_height === "string")
+ this.container.css("min-height", this.options.min_height);
+
+
+ // Add the notice to the notice array.
+ if (this.options.stack.push === "top")
+ PNotify.notices = $.merge([this], PNotify.notices);
+ else
+ PNotify.notices = $.merge(PNotify.notices, [this]);
+ // Now position all the notices if they are to push to the top.
+ if (this.options.stack.push === "top")
+ this.queuePosition(false, 1);
+
+
+
+
+ // Mark the stack so it won't animate the new notice.
+ this.options.stack.animation = false;
+
+ // Run the modules.
+ this.runModules('init');
+
+ // Display the notice.
+ if (this.options.auto_display)
+ this.open();
+ return this;
+ },
+
+ // This function is for updating the notice.
+ update: function(options){
+ // Save old options.
+ var oldOpts = this.options;
+ // Then update to the new options.
+ this.parseOptions(oldOpts, options);
+ // Update the corner class.
+ if (this.options.cornerclass !== oldOpts.cornerclass)
+ this.container.removeClass("ui-corner-all "+oldOpts.cornerclass).addClass(this.options.cornerclass);
+ // Update the shadow.
+ if (this.options.shadow !== oldOpts.shadow) {
+ if (this.options.shadow)
+ this.container.addClass("ui-pnotify-shadow");
+ else
+ this.container.removeClass("ui-pnotify-shadow");
+ }
+ // Update the additional classes.
+ if (this.options.addclass === false)
+ this.elem.removeClass(oldOpts.addclass);
+ else if (this.options.addclass !== oldOpts.addclass)
+ this.elem.removeClass(oldOpts.addclass).addClass(this.options.addclass);
+ // Update the title.
+ if (this.options.title === false)
+ this.title_container.slideUp("fast");
+ else if (this.options.title !== oldOpts.title) {
+ if (this.options.title_escape)
+ this.title_container.text(this.options.title);
+ else
+ this.title_container.html(this.options.title);
+ if (oldOpts.title === false)
+ this.title_container.slideDown(200)
+ }
+ // Update the text.
+ if (this.options.text === false) {
+ this.text_container.slideUp("fast");
+ } else if (this.options.text !== oldOpts.text) {
+ if (this.options.text_escape)
+ this.text_container.text(this.options.text);
+ else
+ this.text_container.html(this.options.insert_brs ? String(this.options.text).replace(/\n/g, "
") : this.options.text);
+ if (oldOpts.text === false)
+ this.text_container.slideDown(200)
+ }
+ // Change the notice type.
+ if (this.options.type !== oldOpts.type)
+ this.container.removeClass(
+ this.styles.error+" "+this.styles.notice+" "+this.styles.success+" "+this.styles.info
+ ).addClass(this.options.type === "error" ?
+ this.styles.error :
+ (this.options.type === "info" ?
+ this.styles.info :
+ (this.options.type === "success" ?
+ this.styles.success :
+ this.styles.notice
+ )
+ )
+ );
+ if (this.options.icon !== oldOpts.icon || (this.options.icon === true && this.options.type !== oldOpts.type)) {
+ // Remove any old icon.
+ this.container.find("div.ui-pnotify-icon").remove();
+ if (this.options.icon !== false) {
+ // Build the new icon.
+ $("", {"class": "ui-pnotify-icon"})
+ .append($("", {"class": this.options.icon === true ? (this.options.type === "error" ? this.styles.error_icon : (this.options.type === "info" ? this.styles.info_icon : (this.options.type === "success" ? this.styles.success_icon : this.styles.notice_icon))) : this.options.icon}))
+ .prependTo(this.container);
+ }
+ }
+ // Update the width.
+ if (this.options.width !== oldOpts.width)
+ this.elem.animate({width: this.options.width});
+ // Update the minimum height.
+ if (this.options.min_height !== oldOpts.min_height)
+ this.container.animate({minHeight: this.options.min_height});
+ // Update the opacity.
+ if (this.options.opacity !== oldOpts.opacity)
+ this.elem.fadeTo(this.options.animate_speed, this.options.opacity);
+ // Update the timed hiding.
+ if (!this.options.hide)
+ this.cancelRemove();
+ else if (!oldOpts.hide)
+ this.queueRemove();
+ this.queuePosition(true);
+
+ // Run the modules.
+ this.runModules('update', oldOpts);
+ return this;
+ },
+
+ // Display the notice.
+ open: function(){
+ this.state = "opening";
+ // Run the modules.
+ this.runModules('beforeOpen');
+
+ var that = this;
+ // If the notice is not in the DOM, append it.
+ if (!this.elem.parent().length)
+ this.elem.appendTo(this.options.stack.context ? this.options.stack.context : body);
+ // Try to put it in the right position.
+ if (this.options.stack.push !== "top")
+ this.position(true);
+ // First show it, then set its opacity, then hide it.
+ if (this.options.animation === "fade" || this.options.animation.effect_in === "fade") {
+ // If it's fading in, it should start at 0.
+ this.elem.show().fadeTo(0, 0).hide();
+ } else {
+ // Or else it should be set to the opacity.
+ if (this.options.opacity !== 1)
+ this.elem.show().fadeTo(0, this.options.opacity).hide();
+ }
+ this.animateIn(function(){
+ that.queuePosition(true);
+
+ // Now set it to hide.
+ if (that.options.hide)
+ that.queueRemove();
+
+ that.state = "open";
+
+ // Run the modules.
+ that.runModules('afterOpen');
+ });
+
+ return this;
+ },
+
+ // Remove the notice.
+ remove: function(timer_hide) {
+ this.state = "closing";
+ this.timerHide = !!timer_hide; // Make sure it's a boolean.
+ // Run the modules.
+ this.runModules('beforeClose');
+
+ var that = this;
+ if (this.timer) {
+ window.clearTimeout(this.timer);
+ this.timer = null;
+ }
+ this.animateOut(function(){
+ that.state = "closed";
+ // Run the modules.
+ that.runModules('afterClose');
+ that.queuePosition(true);
+ // If we're supposed to remove the notice from the DOM, do it.
+ if (that.options.remove)
+ that.elem.detach();
+ // Run the modules.
+ that.runModules('beforeDestroy');
+ // Remove object from PNotify.notices to prevent memory leak (issue #49)
+ // unless destroy is off
+ if (that.options.destroy) {
+ if (PNotify.notices !== null) {
+ var idx = $.inArray(that,PNotify.notices);
+ if (idx !== -1) {
+ PNotify.notices.splice(idx,1);
+ }
+ }
+ }
+ // Run the modules.
+ that.runModules('afterDestroy');
+ });
+
+ return this;
+ },
+
+ // === Class Methods ===
+
+ // Get the DOM element.
+ get: function(){ return this.elem; },
+
+ // Put all the options in the right places.
+ parseOptions: function(options, moreOptions){
+ this.options = $.extend(true, {}, PNotify.prototype.options);
+ // This is the only thing that *should* be copied by reference.
+ this.options.stack = PNotify.prototype.options.stack;
+ var optArray = [options, moreOptions], curOpts;
+ for (var curIndex in optArray) {
+ curOpts = optArray[curIndex];
+ if (typeof curOpts == "undefined")
+ break;
+ if (typeof curOpts !== 'object') {
+ this.options.text = curOpts;
+ } else {
+ for (var option in curOpts) {
+ if (this.modules[option]) {
+ // Avoid overwriting module defaults.
+ $.extend(true, this.options[option], curOpts[option]);
+ } else {
+ this.options[option] = curOpts[option];
+ }
+ }
+ }
+ }
+ },
+
+ // Animate the notice in.
+ animateIn: function(callback){
+ // Declare that the notice is animating in. (Or has completed animating in.)
+ this.animating = "in";
+ var animation;
+ if (typeof this.options.animation.effect_in !== "undefined")
+ animation = this.options.animation.effect_in;
+ else
+ animation = this.options.animation;
+ if (animation === "none") {
+ this.elem.show();
+ callback();
+ } else if (animation === "show")
+ this.elem.show(this.options.animate_speed, callback);
+ else if (animation === "fade")
+ this.elem.show().fadeTo(this.options.animate_speed, this.options.opacity, callback);
+ else if (animation === "slide")
+ this.elem.slideDown(this.options.animate_speed, callback);
+ else if (typeof animation === "function")
+ animation("in", callback, this.elem);
+ else
+ this.elem.show(animation, (typeof this.options.animation.options_in === "object" ? this.options.animation.options_in : {}), this.options.animate_speed, callback);
+ if (this.elem.parent().hasClass('ui-effects-wrapper'))
+ this.elem.parent().css({"position": "fixed", "overflow": "visible"});
+ if (animation !== "slide")
+ this.elem.css("overflow", "visible");
+ this.container.css("overflow", "hidden");
+ },
+
+ // Animate the notice out.
+ animateOut: function(callback){
+ // Declare that the notice is animating out. (Or has completed animating out.)
+ this.animating = "out";
+ var animation;
+ if (typeof this.options.animation.effect_out !== "undefined")
+ animation = this.options.animation.effect_out;
+ else
+ animation = this.options.animation;
+ if (animation === "none") {
+ this.elem.hide();
+ callback();
+ } else if (animation === "show")
+ this.elem.hide(this.options.animate_speed, callback);
+ else if (animation === "fade")
+ this.elem.fadeOut(this.options.animate_speed, callback);
+ else if (animation === "slide")
+ this.elem.slideUp(this.options.animate_speed, callback);
+ else if (typeof animation === "function")
+ animation("out", callback, this.elem);
+ else
+ this.elem.hide(animation, (typeof this.options.animation.options_out === "object" ? this.options.animation.options_out : {}), this.options.animate_speed, callback);
+ if (this.elem.parent().hasClass('ui-effects-wrapper'))
+ this.elem.parent().css({"position": "fixed", "overflow": "visible"});
+ if (animation !== "slide")
+ this.elem.css("overflow", "visible");
+ this.container.css("overflow", "hidden");
+ },
+
+ // Position the notice. dont_skip_hidden causes the notice to
+ // position even if it's not visible.
+ position: function(dontSkipHidden){
+ // Get the notice's stack.
+ var s = this.options.stack,
+ e = this.elem;
+ if (e.parent().hasClass('ui-effects-wrapper'))
+ e = this.elem.css({"left": "0", "top": "0", "right": "0", "bottom": "0"}).parent();
+ if (typeof s.context === "undefined")
+ s.context = body;
+ if (!s) return;
+ if (typeof s.nextpos1 !== "number")
+ s.nextpos1 = s.firstpos1;
+ if (typeof s.nextpos2 !== "number")
+ s.nextpos2 = s.firstpos2;
+ if (typeof s.addpos2 !== "number")
+ s.addpos2 = 0;
+ var hidden = e.css("display") === "none";
+ // Skip this notice if it's not shown.
+ if (!hidden || dontSkipHidden) {
+ var curpos1, curpos2;
+ // Store what will need to be animated.
+ var animate = {};
+ // Calculate the current pos1 value.
+ var csspos1;
+ switch (s.dir1) {
+ case "down":
+ csspos1 = "top";
+ break;
+ case "up":
+ csspos1 = "bottom";
+ break;
+ case "left":
+ csspos1 = "right";
+ break;
+ case "right":
+ csspos1 = "left";
+ break;
+ }
+ curpos1 = parseInt(e.css(csspos1).replace(/(?:\..*|[^0-9.])/g, ''));
+ if (isNaN(curpos1))
+ curpos1 = 0;
+ // Remember the first pos1, so the first visible notice goes there.
+ if (typeof s.firstpos1 === "undefined" && !hidden) {
+ s.firstpos1 = curpos1;
+ s.nextpos1 = s.firstpos1;
+ }
+ // Calculate the current pos2 value.
+ var csspos2;
+ switch (s.dir2) {
+ case "down":
+ csspos2 = "top";
+ break;
+ case "up":
+ csspos2 = "bottom";
+ break;
+ case "left":
+ csspos2 = "right";
+ break;
+ case "right":
+ csspos2 = "left";
+ break;
+ }
+ curpos2 = parseInt(e.css(csspos2).replace(/(?:\..*|[^0-9.])/g, ''));
+ if (isNaN(curpos2))
+ curpos2 = 0;
+ // Remember the first pos2, so the first visible notice goes there.
+ if (typeof s.firstpos2 === "undefined" && !hidden) {
+ s.firstpos2 = curpos2;
+ s.nextpos2 = s.firstpos2;
+ }
+ // Check that it's not beyond the viewport edge.
+ if ((s.dir1 === "down" && s.nextpos1 + e.height() > (s.context.is(body) ? jwindow.height() : s.context.prop('scrollHeight')) ) ||
+ (s.dir1 === "up" && s.nextpos1 + e.height() > (s.context.is(body) ? jwindow.height() : s.context.prop('scrollHeight')) ) ||
+ (s.dir1 === "left" && s.nextpos1 + e.width() > (s.context.is(body) ? jwindow.width() : s.context.prop('scrollWidth')) ) ||
+ (s.dir1 === "right" && s.nextpos1 + e.width() > (s.context.is(body) ? jwindow.width() : s.context.prop('scrollWidth')) ) ) {
+ // If it is, it needs to go back to the first pos1, and over on pos2.
+ s.nextpos1 = s.firstpos1;
+ s.nextpos2 += s.addpos2 + (typeof s.spacing2 === "undefined" ? 25 : s.spacing2);
+ s.addpos2 = 0;
+ }
+ // Animate if we're moving on dir2.
+ if (s.animation && s.nextpos2 < curpos2) {
+ switch (s.dir2) {
+ case "down":
+ animate.top = s.nextpos2+"px";
+ break;
+ case "up":
+ animate.bottom = s.nextpos2+"px";
+ break;
+ case "left":
+ animate.right = s.nextpos2+"px";
+ break;
+ case "right":
+ animate.left = s.nextpos2+"px";
+ break;
+ }
+ } else {
+ if(typeof s.nextpos2 === "number")
+ e.css(csspos2, s.nextpos2+"px");
+ }
+ // Keep track of the widest/tallest notice in the column/row, so we can push the next column/row.
+ switch (s.dir2) {
+ case "down":
+ case "up":
+ if (e.outerHeight(true) > s.addpos2)
+ s.addpos2 = e.height();
+ break;
+ case "left":
+ case "right":
+ if (e.outerWidth(true) > s.addpos2)
+ s.addpos2 = e.width();
+ break;
+ }
+ // Move the notice on dir1.
+ if (typeof s.nextpos1 === "number") {
+ // Animate if we're moving toward the first pos.
+ if (s.animation && (curpos1 > s.nextpos1 || animate.top || animate.bottom || animate.right || animate.left)) {
+ switch (s.dir1) {
+ case "down":
+ animate.top = s.nextpos1+"px";
+ break;
+ case "up":
+ animate.bottom = s.nextpos1+"px";
+ break;
+ case "left":
+ animate.right = s.nextpos1+"px";
+ break;
+ case "right":
+ animate.left = s.nextpos1+"px";
+ break;
+ }
+ } else
+ e.css(csspos1, s.nextpos1+"px");
+ }
+ // Run the animation.
+ if (animate.top || animate.bottom || animate.right || animate.left)
+ e.animate(animate, {duration: this.options.position_animate_speed, queue: false});
+ // Calculate the next dir1 position.
+ switch (s.dir1) {
+ case "down":
+ case "up":
+ s.nextpos1 += e.height() + (typeof s.spacing1 === "undefined" ? 25 : s.spacing1);
+ break;
+ case "left":
+ case "right":
+ s.nextpos1 += e.width() + (typeof s.spacing1 === "undefined" ? 25 : s.spacing1);
+ break;
+ }
+ }
+ return this;
+ },
+ // Queue the position all function so it doesn't run repeatedly and
+ // use up resources.
+ queuePosition: function(animate, milliseconds){
+ if (timer)
+ clearTimeout(timer);
+ if (!milliseconds)
+ milliseconds = 10;
+ timer = setTimeout(function(){ PNotify.positionAll(animate) }, milliseconds);
+ return this;
+ },
+
+
+ // Cancel any pending removal timer.
+ cancelRemove: function(){
+ if (this.timer)
+ window.clearTimeout(this.timer);
+ if (this.state === "closing") {
+ // If it's animating out, animate back in really quickly.
+ this.elem.stop(true);
+ this.state = "open";
+ this.animating = "in";
+ this.elem.css("height", "auto").animate({"width": this.options.width, "opacity": this.options.opacity}, "fast");
+ }
+ return this;
+ },
+ // Queue a removal timer.
+ queueRemove: function(){
+ var that = this;
+ // Cancel any current removal timer.
+ this.cancelRemove();
+ this.timer = window.setTimeout(function(){
+ that.remove(true);
+ }, (isNaN(this.options.delay) ? 0 : this.options.delay));
+ return this;
+ }
+ });
+ // These functions affect all notices.
+ $.extend(PNotify, {
+ // This holds all the notices.
+ notices: [],
+ removeAll: function () {
+ $.each(PNotify.notices, function(){
+ if (this.remove)
+ this.remove();
+ });
+ },
+ positionAll: function (animate) {
+ // This timer is used for queueing this function so it doesn't run
+ // repeatedly.
+ if (timer)
+ clearTimeout(timer);
+ timer = null;
+ // Reset the next position data.
+ $.each(PNotify.notices, function(){
+ var s = this.options.stack;
+ if (!s) return;
+ s.nextpos1 = s.firstpos1;
+ s.nextpos2 = s.firstpos2;
+ s.addpos2 = 0;
+ s.animation = animate;
+ });
+ $.each(PNotify.notices, function(){
+ this.position();
+ });
+ },
+ styling: {
+ jqueryui: {
+ container: "ui-widget ui-widget-content ui-corner-all",
+ notice: "ui-state-highlight",
+ // (The actual jQUI notice icon looks terrible.)
+ notice_icon: "ui-icon ui-icon-info",
+ info: "",
+ info_icon: "ui-icon ui-icon-info",
+ success: "ui-state-default",
+ success_icon: "ui-icon ui-icon-circle-check",
+ error: "ui-state-error",
+ error_icon: "ui-icon ui-icon-alert"
+ },
+ bootstrap2: {
+ container: "alert",
+ notice: "",
+ notice_icon: "icon-exclamation-sign",
+ info: "alert-info",
+ info_icon: "icon-info-sign",
+ success: "alert-success",
+ success_icon: "icon-ok-sign",
+ error: "alert-error",
+ error_icon: "icon-warning-sign"
+ },
+ bootstrap3: {
+ container: "alert",
+ notice: "alert-warning",
+ notice_icon: "glyphicon glyphicon-exclamation-sign",
+ info: "alert-info",
+ info_icon: "glyphicon glyphicon-info-sign",
+ success: "alert-success",
+ success_icon: "glyphicon glyphicon-ok-sign",
+ error: "alert-danger",
+ error_icon: "glyphicon glyphicon-warning-sign"
+ }
+ }
+ });
+ /*
+ * uses icons from http://fontawesome.io/
+ * version 4.0.3
+ */
+ PNotify.styling.fontawesome = $.extend({}, PNotify.styling.bootstrap3);
+ $.extend(PNotify.styling.fontawesome, {
+ notice_icon: "fa fa-exclamation-circle",
+ info_icon: "fa fa-info",
+ success_icon: "fa fa-check",
+ error_icon: "fa fa-warning"
+ });
+
+ if (document.body)
+ do_when_ready();
+ else
+ $(do_when_ready);
+ return PNotify;
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.desktop.js b/mailadmin/static/bower_components/pnotify/pnotify.desktop.js
new file mode 100644
index 0000000..1402f38
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.desktop.js
@@ -0,0 +1,143 @@
+// Desktop
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.desktop', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ var permission;
+ var notify = function(title, options){
+ // Memoize based on feature detection.
+ if ("Notification" in window) {
+ notify = function (title, options) {
+ return new Notification(title, options);
+ };
+ } else if ("mozNotification" in navigator) {
+ notify = function (title, options) {
+ // Gecko < 22
+ return navigator.mozNotification
+ .createNotification(title, options.body, options.icon)
+ .show();
+ };
+ } else if ("webkitNotifications" in window) {
+ notify = function (title, options) {
+ return window.webkitNotifications.createNotification(
+ options.icon,
+ title,
+ options.body
+ );
+ };
+ } else {
+ notify = function (title, options) {
+ return null;
+ };
+ }
+ return notify(title, options);
+ };
+
+
+ PNotify.prototype.options.desktop = {
+ // Display the notification as a desktop notification.
+ desktop: false,
+ // The URL of the icon to display. If false, no icon will show. If null, a default icon will show.
+ icon: null,
+ // Using a tag lets you update an existing notice, or keep from duplicating notices between tabs.
+ // If you leave tag null, one will be generated, facilitating the "update" function.
+ // see: http://www.w3.org/TR/notifications/#tags-example
+ tag: null
+ };
+ PNotify.prototype.modules.desktop = {
+ tag: null,
+ icon: null,
+ genNotice: function(notice, options){
+ if (options.icon === null) {
+ this.icon = "http://sciactive.com/pnotify/includes/desktop/"+notice.options.type+".png";
+ } else if (options.icon === false) {
+ this.icon = null;
+ } else {
+ this.icon = options.icon;
+ }
+ if (this.tag === null || options.tag !== null) {
+ this.tag = options.tag === null ? "PNotify-"+Math.round(Math.random() * 1000000) : options.tag;
+ }
+ notice.desktop = notify(notice.options.title, {
+ icon: this.icon,
+ body: notice.options.text,
+ tag: this.tag
+ });
+ if (!("close" in notice.desktop)) {
+ notice.desktop.close = function(){
+ notice.desktop.cancel();
+ };
+ }
+ notice.desktop.onclick = function(){
+ notice.elem.trigger("click");
+ };
+ notice.desktop.onclose = function(){
+ if (notice.state !== "closing" && notice.state !== "closed") {
+ notice.remove();
+ }
+ };
+ },
+ init: function(notice, options){
+ if (!options.desktop)
+ return;
+ permission = PNotify.desktop.checkPermission();
+ if (permission != 0)
+ return;
+ this.genNotice(notice, options);
+ },
+ update: function(notice, options, oldOpts){
+ if (permission != 0 || !options.desktop)
+ return;
+ this.genNotice(notice, options);
+ },
+ beforeOpen: function(notice, options){
+ if (permission != 0 || !options.desktop)
+ return;
+ notice.elem.css({'left': '-10000px', 'display': 'none'});
+ },
+ afterOpen: function(notice, options){
+ if (permission != 0 || !options.desktop)
+ return;
+ notice.elem.css({'left': '-10000px', 'display': 'none'});
+ if ("show" in notice.desktop) {
+ notice.desktop.show();
+ }
+ },
+ beforeClose: function(notice, options){
+ if (permission != 0 || !options.desktop)
+ return;
+ notice.elem.css({'left': '-10000px', 'display': 'none'});
+ },
+ afterClose: function(notice, options){
+ if (permission != 0 || !options.desktop)
+ return;
+ notice.elem.css({'left': '-10000px', 'display': 'none'});
+ notice.desktop.close();
+ }
+ };
+ PNotify.desktop = {
+ permission: function(){
+ if (typeof Notification !== "undefined" && "requestPermission" in Notification) {
+ Notification.requestPermission();
+ } else if ("webkitNotifications" in window) {
+ window.webkitNotifications.requestPermission();
+ }
+ },
+ checkPermission: function(){
+ if (typeof Notification !== "undefined" && "permission" in Notification) {
+ return (Notification.permission == "granted" ? 0 : 1);
+ } else if ("webkitNotifications" in window) {
+ return window.webkitNotifications.checkPermission();
+ } else {
+ return 1;
+ }
+ }
+ };
+ permission = PNotify.desktop.checkPermission();
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.history.css b/mailadmin/static/bower_components/pnotify/pnotify.history.css
new file mode 100644
index 0000000..4aaf086
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.history.css
@@ -0,0 +1,33 @@
+/* -- Pulldown */
+.ui-pnotify-history-container {
+position: absolute;
+top: 0;
+right: 18px;
+width: 70px;
+border-top: none;
+padding: 0;
+-webkit-border-top-left-radius: 0;
+-moz-border-top-left-radius: 0;
+border-top-left-radius: 0;
+-webkit-border-top-right-radius: 0;
+-moz-border-top-right-radius: 0;
+border-top-right-radius: 0;
+/* Ensures history container is above notices. */
+z-index: 10000;
+}
+.ui-pnotify-history-container.ui-pnotify-history-fixed {
+position: fixed;
+}
+.ui-pnotify-history-container .ui-pnotify-history-header {
+padding: 2px;
+text-align: center;
+}
+.ui-pnotify-history-container button {
+cursor: pointer;
+display: block;
+width: 100%;
+}
+.ui-pnotify-history-container .ui-pnotify-history-pulldown {
+display: block;
+margin: 0 auto;
+}
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.history.js b/mailadmin/static/bower_components/pnotify/pnotify.history.js
new file mode 100644
index 0000000..5ef888b
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.history.js
@@ -0,0 +1,189 @@
+// History
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.history', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ var history_menu,
+ history_handle_top;
+ $(function(){
+ $("body").on("pnotify.history-all", function(){
+ // Display all notices. (Disregarding non-history notices.)
+ $.each(PNotify.notices, function(){
+ if (this.modules.history.inHistory) {
+ if (this.elem.is(":visible")) {
+ // The hide variable controls whether the history pull down should
+ // queue a removal timer.
+ if (this.options.hide)
+ this.queueRemove();
+ } else if (this.open)
+ this.open();
+ }
+ });
+ }).on("pnotify.history-last", function(){
+ var pushTop = (PNotify.prototype.options.stack.push === "top");
+
+ // Look up the last history notice, and display it.
+ var i = (pushTop ? 0 : -1);
+
+ var notice;
+ do {
+ if (i === -1)
+ notice = PNotify.notices.slice(i);
+ else
+ notice = PNotify.notices.slice(i, i+1);
+ if (!notice[0])
+ return false;
+
+ i = (pushTop ? i + 1 : i - 1);
+ } while (!notice[0].modules.history.inHistory || notice[0].elem.is(":visible"));
+ if (notice[0].open)
+ notice[0].open();
+ });
+ });
+ PNotify.prototype.options.history = {
+ // Place the notice in the history.
+ history: true,
+ // Display a pull down menu to redisplay previous notices.
+ menu: false,
+ // Make the pull down menu fixed to the top of the viewport.
+ fixed: true,
+ // Maximum number of notifications to have onscreen.
+ maxonscreen: Infinity,
+ // The various displayed text, helps facilitating internationalization.
+ labels: {
+ redisplay: "Redisplay",
+ all: "All",
+ last: "Last"
+ }
+ };
+ PNotify.prototype.modules.history = {
+ // The history variable controls whether the notice gets redisplayed
+ // by the history pull down.
+ inHistory: false,
+
+ init: function(notice, options){
+ // Make sure that no notices get destroyed.
+ notice.options.destroy = false;
+
+ this.inHistory = options.history;
+
+ if (options.menu) {
+ // If there isn't a history pull down, create one.
+ if (typeof history_menu === "undefined") {
+ history_menu = $("", {
+ "class": "ui-pnotify-history-container "+notice.styles.hi_menu,
+ "mouseleave": function(){
+ history_menu.animate({top: "-"+history_handle_top+"px"}, {duration: 100, queue: false});
+ }
+ })
+ .append($("", {"class": "ui-pnotify-history-header", "text": options.labels.redisplay}))
+ .append($("", {
+ "class": "ui-pnotify-history-all "+notice.styles.hi_btn,
+ "text": options.labels.all,
+ "mouseenter": function(){
+ $(this).addClass(notice.styles.hi_btnhov);
+ },
+ "mouseleave": function(){
+ $(this).removeClass(notice.styles.hi_btnhov);
+ },
+ "click": function(){
+ $(this).trigger("pnotify.history-all");
+ return false;
+ }
+ }))
+ .append($("", {
+ "class": "ui-pnotify-history-last "+notice.styles.hi_btn,
+ "text": options.labels.last,
+ "mouseenter": function(){
+ $(this).addClass(notice.styles.hi_btnhov);
+ },
+ "mouseleave": function(){
+ $(this).removeClass(notice.styles.hi_btnhov);
+ },
+ "click": function(){
+ $(this).trigger("pnotify.history-last");
+ return false;
+ }
+ }))
+ .appendTo("body");
+
+ // Make a handle so the user can pull down the history tab.
+ var handle = $("", {
+ "class": "ui-pnotify-history-pulldown "+notice.styles.hi_hnd,
+ "mouseenter": function(){
+ history_menu.animate({top: "0"}, {duration: 100, queue: false});
+ }
+ })
+ .appendTo(history_menu);
+
+ // Get the top of the handle.
+ console.log(handle.offset());
+ history_handle_top = handle.offset().top + 2;
+ // Hide the history pull down up to the top of the handle.
+ history_menu.css({top: "-"+history_handle_top+"px"});
+
+ // Apply the fixed styling.
+ if (options.fixed) {
+ history_menu.addClass('ui-pnotify-history-fixed');
+ }
+ }
+ }
+ },
+ update: function(notice, options){
+ // Update values for history menu access.
+ this.inHistory = options.history;
+ if (options.fixed && history_menu) {
+ history_menu.addClass('ui-pnotify-history-fixed');
+ } else if (history_menu) {
+ history_menu.removeClass('ui-pnotify-history-fixed');
+ }
+ },
+ beforeOpen: function(notice, options){
+ // Remove oldest notifications leaving only options.maxonscreen on screen
+ if (PNotify.notices && (PNotify.notices.length > options.maxonscreen)) {
+ // Oldest are normally in front of array, or if stack.push=="top" then
+ // they are at the end of the array! (issue #98)
+ var el;
+ if (notice.options.stack.push !== "top")
+ el = PNotify.notices.slice(0, PNotify.notices.length - options.maxonscreen);
+ else
+ el = PNotify.notices.slice(options.maxonscreen, PNotify.notices.length);
+
+ $.each(el, function(){
+ if (this.remove)
+ this.remove();
+ });
+ }
+ }
+ };
+ $.extend(PNotify.styling.jqueryui, {
+ hi_menu: "ui-state-default ui-corner-bottom",
+ hi_btn: "ui-state-default ui-corner-all",
+ hi_btnhov: "ui-state-hover",
+ hi_hnd: "ui-icon ui-icon-grip-dotted-horizontal"
+ });
+ $.extend(PNotify.styling.bootstrap2, {
+ hi_menu: "well",
+ hi_btn: "btn",
+ hi_btnhov: "",
+ hi_hnd: "icon-chevron-down"
+ });
+ $.extend(PNotify.styling.bootstrap3, {
+ hi_menu: "well",
+ hi_btn: "btn btn-default",
+ hi_btnhov: "",
+ hi_hnd: "glyphicon glyphicon-chevron-down"
+ });
+ $.extend(PNotify.styling.fontawesome, {
+ hi_menu: "well",
+ hi_btn: "btn btn-default",
+ hi_btnhov: "",
+ hi_hnd: "fa fa-chevron-down"
+ });
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.nonblock.js b/mailadmin/static/bower_components/pnotify/pnotify.nonblock.js
new file mode 100644
index 0000000..4464f14
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.nonblock.js
@@ -0,0 +1,151 @@
+// Nonblock
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.nonblock', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ // Some useful regexes.
+ var re_on = /^on/,
+ re_mouse_events = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/,
+ re_ui_events = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/,
+ re_html_events = /^(scroll|resize|(un)?load|abort|error)$/;
+ // Fire a DOM event.
+ var dom_event = function(e, orig_e){
+ var event_object;
+ e = e.toLowerCase();
+ if (document.createEvent && this.dispatchEvent) {
+ // FireFox, Opera, Safari, Chrome
+ e = e.replace(re_on, '');
+ if (e.match(re_mouse_events)) {
+ // This allows the click event to fire on the notice. There is
+ // probably a much better way to do it.
+ $(this).offset();
+ event_object = document.createEvent("MouseEvents");
+ event_object.initMouseEvent(
+ e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail,
+ orig_e.screenX, orig_e.screenY, orig_e.clientX, orig_e.clientY,
+ orig_e.ctrlKey, orig_e.altKey, orig_e.shiftKey, orig_e.metaKey, orig_e.button, orig_e.relatedTarget
+ );
+ } else if (e.match(re_ui_events)) {
+ event_object = document.createEvent("UIEvents");
+ event_object.initUIEvent(e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail);
+ } else if (e.match(re_html_events)) {
+ event_object = document.createEvent("HTMLEvents");
+ event_object.initEvent(e, orig_e.bubbles, orig_e.cancelable);
+ }
+ if (!event_object) return;
+ this.dispatchEvent(event_object);
+ } else {
+ // Internet Explorer
+ if (!e.match(re_on)) e = "on"+e;
+ event_object = document.createEventObject(orig_e);
+ this.fireEvent(e, event_object);
+ }
+ };
+
+
+ // This keeps track of the last element the mouse was over, so
+ // mouseleave, mouseenter, etc can be called.
+ var nonblock_last_elem;
+ // This is used to pass events through the notice if it is non-blocking.
+ var nonblock_pass = function(notice, e, e_name){
+ notice.elem.css("display", "none");
+ var element_below = document.elementFromPoint(e.clientX, e.clientY);
+ notice.elem.css("display", "block");
+ var jelement_below = $(element_below);
+ var cursor_style = jelement_below.css("cursor");
+ notice.elem.css("cursor", cursor_style !== "auto" ? cursor_style : "default");
+ // If the element changed, call mouseenter, mouseleave, etc.
+ if (!nonblock_last_elem || nonblock_last_elem.get(0) != element_below) {
+ if (nonblock_last_elem) {
+ dom_event.call(nonblock_last_elem.get(0), "mouseleave", e.originalEvent);
+ dom_event.call(nonblock_last_elem.get(0), "mouseout", e.originalEvent);
+ }
+ dom_event.call(element_below, "mouseenter", e.originalEvent);
+ dom_event.call(element_below, "mouseover", e.originalEvent);
+ }
+ dom_event.call(element_below, e_name, e.originalEvent);
+ // Remember the latest element the mouse was over.
+ nonblock_last_elem = jelement_below;
+ };
+
+
+ PNotify.prototype.options.nonblock = {
+ // Create a non-blocking notice. It lets the user click elements underneath it.
+ nonblock: false,
+ // The opacity of the notice (if it's non-blocking) when the mouse is over it.
+ nonblock_opacity: .2
+ };
+ PNotify.prototype.modules.nonblock = {
+ // This lets us update the options available in the closures.
+ myOptions: null,
+
+ init: function(notice, options){
+ var that = this;
+ this.myOptions = options;
+ notice.elem.on({
+ "mouseenter": function(e){
+ if (that.myOptions.nonblock) e.stopPropagation();
+ if (that.myOptions.nonblock) {
+ // If it's non-blocking, animate to the other opacity.
+ notice.elem.stop().animate({"opacity": that.myOptions.nonblock_opacity}, "fast");
+ }
+ },
+ "mouseleave": function(e){
+ if (that.myOptions.nonblock) e.stopPropagation();
+ nonblock_last_elem = null;
+ notice.elem.css("cursor", "auto");
+ // Animate back to the normal opacity.
+ if (that.myOptions.nonblock && notice.animating !== "out")
+ notice.elem.stop().animate({"opacity": notice.options.opacity}, "fast");
+ },
+ "mouseover": function(e){
+ if (that.myOptions.nonblock) e.stopPropagation();
+ },
+ "mouseout": function(e){
+ if (that.myOptions.nonblock) e.stopPropagation();
+ },
+ "mousemove": function(e){
+ if (that.myOptions.nonblock) {
+ e.stopPropagation();
+ nonblock_pass(notice, e, "onmousemove");
+ }
+ },
+ "mousedown": function(e){
+ if (that.myOptions.nonblock) {
+ e.stopPropagation();
+ e.preventDefault();
+ nonblock_pass(notice, e, "onmousedown");
+ }
+ },
+ "mouseup": function(e){
+ if (that.myOptions.nonblock) {
+ e.stopPropagation();
+ e.preventDefault();
+ nonblock_pass(notice, e, "onmouseup");
+ }
+ },
+ "click": function(e){
+ if (that.myOptions.nonblock) {
+ e.stopPropagation();
+ nonblock_pass(notice, e, "onclick");
+ }
+ },
+ "dblclick": function(e){
+ if (that.myOptions.nonblock) {
+ e.stopPropagation();
+ nonblock_pass(notice, e, "ondblclick");
+ }
+ }
+ });
+ },
+ update: function(notice, options){
+ this.myOptions = options;
+ }
+ };
+}));
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.picon.css b/mailadmin/static/bower_components/pnotify/pnotify.picon.css
new file mode 100644
index 0000000..5b76350
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.picon.css
@@ -0,0 +1,11 @@
+.ui-pnotify .picon {
+background-color: transparent;
+background-repeat: no-repeat;
+background-position: center center;
+width: 17px;
+height: 17px;
+}
+.ui-pnotify-title {
+line-height: 17px;
+min-height: 17px;
+}
diff --git a/mailadmin/static/bower_components/pnotify/pnotify.reference.js b/mailadmin/static/bower_components/pnotify/pnotify.reference.js
new file mode 100644
index 0000000..2f707c3
--- /dev/null
+++ b/mailadmin/static/bower_components/pnotify/pnotify.reference.js
@@ -0,0 +1,132 @@
+// Reference
+// This file is for referencing while you are making a notify module.
+// Uses AMD or browser globals for jQuery.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as a module.
+ define('pnotify.reference', ['jquery', 'pnotify'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery, PNotify);
+ }
+}(function($, PNotify){
+ // This if the default values of your options.
+ PNotify.prototype.options.reference = {
+ // Provide a thing for stuff. Turned off by default.
+ putThing: false,
+ // If you are displaying any text, you should use a labels options to
+ // support internationalization.
+ labels: {
+ text: "Spin Around"
+ }
+ };
+ PNotify.prototype.modules.reference = {
+ // You can put variables here that are specific to a notice instance.
+ thingElem: null,
+
+ // This function is called when the notice is being created, after the
+ // core has done all of its work.
+ init: function(notice /* the notice object */, options /* this module's options */){
+ var that = this; // This line will allow you to access instance variables
+ // like "this.thingElem" from within closures.
+
+ // Note that options only contains the options specific to our modules.
+ // To access global options, we would use notice.options.
+
+ // We want to check to make sure the notice should include our thing.
+ if (!options.putThing)
+ return;
+
+ // We're going to create a button that will be appended to the notice.
+ // It will be disabled by default, so we can enable it on mouseover.
+ // You should try to keep elements inside the notice container.
+ this.thingElem = $('').appendTo(notice.container);
+ // Since our button is floated, we have to add a clearing div.
+ notice.container.append('')
+
+ // Now we're going to enable the button on mouseenter.
+ notice.elem.on({
+ "mouseenter": function(e){
+ // Enable the button.
+ // Notice that we have to use "that" to access thingElem, because
+ // we are in a different scope inside this function.
+ that.thingElem.prop("disabled", false);
+ },
+ "mouseleave": function(e){
+ // Disable the button.
+ that.thingElem.prop("disabled", true);
+ }
+ });
+
+ // Now we're going to make our button do something.
+ this.thingElem.on("click", function(){
+ // Spin the notice around.
+ var cur_angle = 0;
+ var timer = setInterval(function(){
+ cur_angle += 10;
+ if (cur_angle == 360) {
+ cur_angle = 0;
+ clearInterval(timer);
+ }
+ notice.elem.css({
+ '-moz-transform': ('rotate('+cur_angle+'deg)'),
+ '-webkit-transform': ('rotate('+cur_angle+'deg)'),
+ '-o-transform': ('rotate('+cur_angle+'deg)'),
+ '-ms-transform': ('rotate('+cur_angle+'deg)'),
+ 'filter': ('progid:DXImageTransform.Microsoft.BasicImage(rotation='+(cur_angle / 360 * 4)+')')
+ });
+ }, 20);
+ });
+ },
+
+ // This is called when the notice is updating its options.
+ update: function(notice, options /* the new options for our module */, oldOpts /* the old options for our module */){
+ // We need to remove the button if it's now disabled, and show it again if it's enabled.
+ if (options.putThing && this.thingElem)
+ this.thingElem.show();
+ else if (!options.putThing && this.thingElem)
+ this.thingElem.hide();
+ // You may notice that if the user creates a notice without our button,
+ // then updates it to enable our button, they will be out of luck.
+ // Whatever, I don't want to write that much code.
+
+ // Now we update the icon, which may have changed.
+ // Note that as of right now, PNotify doesn't support updating styling.
+ if (this.thingElem)
+ this.thingElem.find('i').attr("class", notice.styles.athing);
+ },
+ // I have nothing to put in these, just showing you that they exist. You
+ // won't need to include them if you aren't using them.
+ beforeOpen: function(notice, options){
+ // Called before the notice is opened.
+ },
+ afterOpen: function(notice, options){
+ // Called after the notice is opened.
+ },
+ beforeClose: function(notice, options){
+ // Called before the notice is closed.
+ },
+ afterClose: function(notice, options){
+ // Called after the notice is closed.
+ },
+ beforeDestroy: function(notice, options){
+ // Called before the notice is destroyed.
+ },
+ afterDestroy: function(notice, options){
+ // Called after the notice is destroyed.
+ }
+ };
+ // This is where you would add any styling options you are using in your code.
+ $.extend(PNotify.styling.jqueryui, {
+ athing: "ui-icon ui-icon-refresh"
+ });
+ $.extend(PNotify.styling.bootstrap2, {
+ athing: "icon-refresh"
+ });
+ $.extend(PNotify.styling.bootstrap3, {
+ athing: "glyphicon glyphicon-refresh"
+ });
+ $.extend(PNotify.styling.fontawesome, {
+ athing: "fa fa-refresh"
+ });
+}));
diff --git a/mailadmin/static/dist/scripts/main.js b/mailadmin/static/dist/scripts/main.js
index 1e90705..7fb3180 100644
--- a/mailadmin/static/dist/scripts/main.js
+++ b/mailadmin/static/dist/scripts/main.js
@@ -29,11 +29,7 @@
return _.unique(emails);
}
- function renderAlert(msg, alertClass, iconClass) {
- return nunjucks.render('alert.html', {'msg': msg, 'alertClass': alertClass, 'iconClass': iconClass});
- }
- function filter_lists_with_members(term, pushState) {
- console.log(pushState);
+ function filter_lists(term, pushState, with_members) {
if(pushState) {
/* Update query string */
window.history.pushState(null, null, '/lists/?q=' + term);
@@ -47,7 +43,7 @@
var hits = _.filter(members, function(el) {
return re_term.test($(el).text());
});
- if(re_term.test(cur_name) || hits.length !== 0) {
+ if(re_term.test(cur_name) || (hits.length !== 0 && with_members) ) {
$(this).removeClass('hidden');
} else {
$(this).addClass('hidden');
@@ -55,21 +51,14 @@
});
}
- // TODO combine with above
- function filter_lists(prefix, pushState) {
- if(pushState) {
- /* Update query string */
- window.history.pushState(null, null, '/lists/?q=^' + prefix+'-');
- }
- /* Filter lists */
- var re_prefix = new RegExp('^'+prefix+'-');
- $(".fwdlist").each(function() {
- var cur_name = $(this).attr('data-list-name');
- if(re_prefix.test(cur_name)) {
- $(this).removeClass('hidden');
- } else {
- $(this).addClass('hidden');
- }
+ function notify(title, text, type) {
+ /* types: info, success, error */
+ new PNotify({
+ title: title,
+ text: text,
+ type: type,
+ delay: 3000,
+ animate_speed: 'fast'
});
}
@@ -93,6 +82,12 @@
$(".orgunit-list").append(ou_html);
var ous_html = nunjucks.render('orgunits_select.html', { orgunits: data });
$(".orgunit-select-container").html(ous_html);
+ var q_term = getParameterByName('q');
+ if(q_term && q_term[0] === '^') {
+ var prefix = q_term.slice(1, -1);
+ $(".orgunit-list li").removeClass('active');
+ $('.orgunit-list li a[data-prefix="'+prefix+'"]').parent().toggleClass('active');
+ }
/* New list */
var new_list_html = nunjucks.render('new_list.html', {
@@ -100,6 +95,7 @@
api_urls: api_urls
});
+
$('.new-list').html(new_list_html);
$('.new-list .js-new-list-name').on('keyup', function() {
var new_list_name = $('.new-list .js-new-list-name').val();
@@ -135,13 +131,13 @@
/* Validate */
var new_list_name = $('.new-list .js-new-list-name').val();
if(new_list_name.length === 0) {
- $('.new-list-result').html(renderAlert('Navn på epostliste mangler', 'danger'));
+ notify('Ny epostliste', 'Navn på epostliste mangler', 'error');
return;
}
var text = $('.new-list textarea').val();
var emails = parseEmails(text);
if(emails.length === 0) {
- $('.new-list-result').html(renderAlert('Ingen eposter i epost-feltet...', 'danger'));
+ notify('Ny epostliste', 'Ingen eposter i epost-feltet...', 'error');
return;
}
// Valid form
@@ -157,7 +153,7 @@
api_urls.fwd_create,
new_list[dest],
function() {
- $('.new-list-result').html(renderAlert('Lagt til ny liste: ' + dest, 'success'));
+ notify('Lagt til', 'Ny liste: ' + dest + 'lagt til', 'success');
$('.new-list textarea').val('');
$('.new-list .js-new-list-name').val('');
},
@@ -178,7 +174,7 @@
$(this).parent().toggleClass('active');
$('.orgunits-select').val(prefix);
- filter_lists(prefix, true);
+ filter_lists('^'+prefix+'-', true);
$('.js-lists-filter-field').val('');
});
$(".orgunits-select").on('change', function() {
@@ -188,7 +184,7 @@
$(".orgunit-list li").removeClass('active');
$(".orgunit-list li a[data-prefix="+prefix+"]").parent().toggleClass('active');
- filter_lists(prefix, true);
+ filter_lists('^'+prefix+'-', true);
$('.js-lists-filter-field').val('');
});
});
@@ -196,11 +192,11 @@
/* Load forwards from API and put on page */
$.getJSON(api_urls.fwd_list, function(data) {
if( !data.cpanelresult ) {
- forwards_container.html(renderAlert("No results.", 'info'));
+ notify("Ingen lister.", 'Fant ingen tilhørende lister', 'info');
return;
}
if( 'error' in data.cpanelresult ) {
- forwards_container.html(renderAlert("Klarte ikke hente epostlister fra APIet: "+data.cpanelresult.error, 'danger'));
+ notify('Oops!', 'Klarte ikke hente epostlister fra APIet: '+data.cpanelresult.error, 'error');
return;
}
@@ -219,13 +215,13 @@
if(q_term[0] === '^') {
filter_lists(q_term, false);
} else {
- filter_lists_with_members(q_term, false);
+ filter_lists(q_term, false, true);
}
}
/* Filter by search query */
$('.js-lists-filter-field').on('keyup', function() {
var term = $(this).val();
- filter_lists_with_members(term, true);
+ filter_lists(term, true, true);
$(".orgunit-list li").removeClass('active');
$(".orgunit-list li:first-child").toggleClass('active');
});
diff --git a/mailadmin/static/dist/scripts/templates.js b/mailadmin/static/dist/scripts/templates.js
index 77e5f06..af85147 100644
--- a/mailadmin/static/dist/scripts/templates.js
+++ b/mailadmin/static/dist/scripts/templates.js
@@ -223,7 +223,7 @@ var lineno = null;
var colno = null;
var output = "";
try {
-output += "Alle\n";
+output += "Alle\n";
frame = frame.push();
var t_3 = runtime.contextOrFrameLookup(context, frame, "orgunits");
if(t_3) {var t_2 = t_3.length;
@@ -243,7 +243,9 @@ output += "\" data-prefix=\"";
output += runtime.suppressValue(runtime.memberLookup((t_4),"prefix", env.autoesc), env.autoesc);
output += "\">";
output += runtime.suppressValue(runtime.memberLookup((t_4),"name", env.autoesc), env.autoesc);
-output += "\n";
+output += " (";
+output += runtime.suppressValue(runtime.memberLookup((t_4),"prefix", env.autoesc), env.autoesc);
+output += ")\n";
;
}
}
diff --git a/mailadmin/static/dist/styles/main.css b/mailadmin/static/dist/styles/main.css
index 0a6823b..4c8595e 100644
--- a/mailadmin/static/dist/styles/main.css
+++ b/mailadmin/static/dist/styles/main.css
@@ -6515,6 +6515,68 @@ button.close {
display: none !important;
}
}
+/*
+Author : Hunter Perrin
+Version : 2.0.1
+Link : http://sciactive.com/pnotify/
+*/
+/* -- Notice */
+.ui-pnotify {
+ top: 25px;
+ right: 25px;
+ position: absolute;
+ height: auto;
+ /* Ensures notices are above everything */
+ z-index: 9999;
+}
+
+/* Hides position: fixed from IE6 */
+html > body > .ui-pnotify {
+ position: fixed;
+}
+
+.ui-pnotify .ui-pnotify-shadow {
+ box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5);
+}
+
+.ui-pnotify-container {
+ background-position: 0 0;
+ padding: .8em;
+ height: 100%;
+ margin: 0;
+}
+
+.ui-pnotify-sharp {
+ border-radius: 0;
+}
+
+.ui-pnotify-title {
+ display: block;
+ margin-bottom: .4em;
+ margin-top: 0;
+}
+
+.ui-pnotify-text {
+ display: block;
+}
+
+.ui-pnotify-icon, .ui-pnotify-icon span {
+ display: block;
+ float: left;
+ margin-right: .2em;
+}
+
+/* Alternate stack initial positioning. */
+.ui-pnotify.stack-topleft, .ui-pnotify.stack-bottomleft {
+ left: 25px;
+ right: auto;
+}
+
+.ui-pnotify.stack-bottomright, .ui-pnotify.stack-bottomleft {
+ bottom: 25px;
+ top: auto;
+}
+
.spin {
-webkit-animation: spin .8s infinite linear;
animation: spin .8s infinite linear;
@@ -6551,11 +6613,11 @@ button.close {
}
/* Hide for mobile, show later */
-.sidebar {
+.sidebar .orgunit-list {
display: none;
}
@media (min-width: 768px) {
- .sidebar {
+ .sidebar .orgunit-list {
display: block;
}
}
diff --git a/mailadmin/templates/base.html b/mailadmin/templates/base.html
index f38fd4b..0835254 100644
--- a/mailadmin/templates/base.html
+++ b/mailadmin/templates/base.html
@@ -24,6 +24,7 @@
+