--- a/src/pyams_skin/resources/js/myams.js Fri Dec 08 11:40:56 2017 +0100
+++ b/src/pyams_skin/resources/js/myams.js Fri Dec 08 11:44:47 2017 +0100
@@ -2,7 +2,7 @@
* MyAMS
* « My Application Management Skin »
*
- * $Tag: 0.1.11 $ (rev. 1)
+ * $Tag$ (rev. 1)
* A bootstrap based application/administration skin
*
* Custom administration and application skin tools
@@ -90,6 +90,13 @@
/**
* JQuery filter on parents class
+ * This filter is often combined with ":not()" to select DOM objects which don't have
+ * parents of a given class.
+ * For example:
+ *
+ * $('.hint:not(:parents(.nohints))', element);
+ *
+ * will select all elements with ".hint" class which don't have a parent with '.nohints' class.
*/
$.expr[':'].parents = function(obj, index, meta /*, stack*/) {
return $(obj).parents(meta[3]).length > 0;
@@ -98,7 +105,7 @@
/**
* JQuery 'scrollbarWidth' function
- * Get width of vertical scrollbar
+ * Get width of default vertical scrollbar
*/
if ($.scrollbarWidth === undefined) {
$.scrollbarWidth = function() {
@@ -116,16 +123,16 @@
*/
$.fn.extend({
- /*
+ /**
* Check if current object is empty or not
*/
exists: function() {
return $(this).length > 0;
},
- /*
+ /**
* Get object if it supports given CSS class,
- * otherwise looks for parents
+ * otherwise look for parents
*/
objectOrParentWithClass: function(klass) {
if (this.hasClass(klass)) {
@@ -135,7 +142,7 @@
}
},
- /*
+ /**
* Build an array of attributes of the given selection
*/
listattr: function(attr) {
@@ -146,7 +153,7 @@
return result;
},
- /*
+ /**
* CSS style function
* Code from Aram Kocharyan on stackoverflow.com
*/
@@ -176,7 +183,7 @@
}
},
- /*
+ /**
* Remove CSS classes starting with a given prefix
*/
removeClassPrefix: function (prefix) {
@@ -189,7 +196,7 @@
return this;
},
- /*
+ /**
* Context menu handler
*/
contextMenu: function(settings) {
@@ -576,9 +583,9 @@
};
/**
- * Get script or CSS file using browser cache
- * Script or CSS URLs can include variable names, given between braces, as in
- * {MyAMS.baseURL}
+ * Get target URL matching given source
+ *
+ * Given URL can include variable names (with their namespace), given between braces, as in {MyAMS.baseURL}
*/
MyAMS.getSource = function(url) {
return url.replace(/{[^{}]*}/g, function(match) {
@@ -586,6 +593,13 @@
});
};
+ /**
+ * Script loader function
+ *
+ * @param url: script URL
+ * @param callback: a callback to be called after script loading
+ * @param options: a set of options to be added to AJAX call
+ */
MyAMS.getScript = function(url, callback, options) {
if (typeof(callback) === 'object') {
options = callback;
@@ -606,6 +620,12 @@
return $.ajax(settings);
};
+ /**
+ * CSS file loader function
+ *
+ * @param url: CSS file URL
+ * @param id: a unique ID given to CSS file
+ */
MyAMS.getCSS = function(url, id) {
var head = $('HEAD');
var css = $('link[data-ams-id="' + id + '"]', head);
@@ -628,6 +648,9 @@
*/
MyAMS.event = {
+ /**
+ * Stop current event propagation
+ */
stop: function(event) {
if (!event) {
event = window.event;
@@ -650,6 +673,9 @@
*/
MyAMS.browser = {
+ /**
+ * Get IE version
+ */
getInternetExplorerVersion: function() {
var rv = -1;
if (navigator.appName === "Microsoft Internet Explorer") {
@@ -662,6 +688,9 @@
return rv;
},
+ /**
+ * Display alert for old IE version
+ */
checkVersion: function() {
var msg = "You're not using Windows Internet Explorer.";
var ver = this.getInternetExplorerVersion();
@@ -677,6 +706,9 @@
}
},
+ /**
+ * Check if IE is in version 8 or lower
+ */
isIE8orlower: function() {
var msg = "0";
var ver = this.getInternetExplorerVersion();
@@ -691,6 +723,14 @@
},
+ /**
+ * Copy selection to clipboard
+ *
+ * If 'text' argument is provided, given text is copied to clipboard.
+ * Otherwise, text ou event's source is copied.
+ * Several methods are tested to do clipboard copy (based on browser features); il copy can't be done,
+ * a prompt is displayed to allow user to make a manual copy.
+ */
copyToClipboard: function(text) {
function doCopy(text) {
@@ -722,7 +762,7 @@
? ams.i18n.CLIPBOARD_TEXT_COPY_OK
: ams.i18n.CLIPBOARD_CHARACTER_COPY_OK,
icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
- timeout: 1000
+ timeout: 3000
});
} else if (globals.prompt) {
globals.prompt(MyAMS.i18n.CLIPBOARD_COPY, text);
@@ -812,12 +852,12 @@
/**
* Check for given feature and download script if necessary
*
- * @checker: pointer to a javascript object which will be downloaded in undefined
- * @source: URL of a javascript file containing requested feature
- * @callback: pointer to a function which will be called after the script is downloaded. The first
+ * @param checker: pointer to a javascript object which will be downloaded in undefined
+ * @param source: URL of a javascript file containing requested feature
+ * @param callback: pointer to a function which will be called after the script is downloaded. The first
* argument of this callback is a boolean value indicating if the script was just downloaded (true)
* or if the requested object was already loaded (false)
- * @options: callback options
+ * @param options: callback options
*/
check: function(checker, source, callback, options) {
@@ -1142,8 +1182,7 @@
message = result.message;
if (typeof(message) === 'string') {
if ((status === 'info') || (status === 'success')) {
- ams.skin.smallBox(status,
- {
+ ams.skin.smallBox(status, {
title: message,
icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
timeout: 3000
@@ -1160,16 +1199,16 @@
}
}
if (result.smallbox) {
- ams.skin.smallBox(result.smallbox_status || status,
- {title: result.smallbox,
- icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
- timeout: 3000});
+ ams.skin.smallBox(result.smallbox_status || status, {
+ title: result.smallbox,
+ icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
+ timeout: 3000
+ });
}
if (result.messagebox) {
message = result.messagebox;
if (typeof(message) === 'string') {
- ams.skin.messageBox('info',
- {
+ ams.skin.messageBox('info', {
title: ams.i18n.ERROR_OCCURED,
content: message,
timeout: 10000
@@ -1179,12 +1218,13 @@
if (messageStatus === 'error' && form && target) {
ams.executeFunctionByName(form.data('ams-form-submit-error') || 'MyAMS.form.finalizeSubmitOnError', form, target);
}
- ams.skin.messageBox(messageStatus,
- {title: message.title || ams.i18n.ERROR_OCCURED,
- content: message.content,
- icon: message.icon,
- number: message.number,
- timeout: message.timeout === null ? undefined : (message.timeout || 10000)});
+ ams.skin.messageBox(messageStatus, {
+ title: message.title || ams.i18n.ERROR_OCCURED,
+ content: message.content,
+ icon: message.icon,
+ number: message.number,
+ timeout: message.timeout === null ? undefined : (message.timeout || 10000)
+ });
}
}
if (result.event) {
@@ -2122,14 +2162,16 @@
widget = $('[name="' + widgetData.name + ':list"]', form);
}
if (widget.exists()) {
+ // Update widget state
widget.parents('label:first')
.removeClassPrefix('state-')
.addClass('state-error')
.after('<span for="name" class="state-error">' + widgetData.message + '</span>');
- }
- // complete form alert message
- if (widgetData.label) {
- message.push(widgetData.label + ' : ' + widgetData.message);
+ } else {
+ // complete form alert message
+ if (widgetData.label) {
+ message.push(widgetData.label + ' : ' + widgetData.message);
+ }
}
// mark parent tab (if any) with error status
var tabIndex = widget.parents('.tab-pane').index() + 1;
@@ -2595,6 +2637,23 @@
/** Datetimepicker dialog cleaner callback */
datetimepickerDialogHiddenCallback: function() {
$('.datepicker, .timepicker, .datetimepicker', this).datetimepicker('destroy');
+ },
+
+ /** Set SEO status */
+ setSEOStatus: function() {
+ var input = $(this);
+ var progress = input.siblings('.progress').children('.progress-bar');
+ var length = Math.min(input.val().length, 100);
+ var status = 'success';
+ if (length < 20 || length > 80) {
+ status = 'danger';
+ } else if (length < 40 || length > 66) {
+ status = 'warning';
+ }
+ progress.removeClassPrefix('progress-bar')
+ .addClass('progress-bar')
+ .addClass('progress-bar-' + status)
+ .css('width', length + '%');
}
};
@@ -5813,6 +5872,11 @@
}
});
+ // Always blur readonly inputs
+ $(document).on('focus', 'input[readonly="readonly"]', function() {
+ $(this).blur();
+ });
+
// Prevent bootstrap dialog from blocking TinyMCE focus
$(document).on('focusin', function(e) {
if ($(e.target).closest('.mce-window').length) {
@@ -5821,13 +5885,29 @@
});
// Disable clicks on disabled tabs
- $("a[data-toggle=tab]", ".nav-tabs").on("click", function(e) {
+ $(document).on("click", '.nav-tabs a[data-toggle=tab]', function(e) {
if ($(this).parent('li').hasClass("disabled")) {
e.preventDefault();
return false;
}
});
+ // Automatically set orientation of dropdown menus
+ $(document).on('show.bs.dropdown', '.btn-group', function() {
+ var menu = $(this);
+ var ul = menu.children('.dropdown-menu');
+ var menuRect = menu.get(0).getBoundingClientRect();
+ var position = menuRect.top;
+ var buttonHeight = menuRect.height;
+ var menuHeight = ul.outerHeight();
+ if (position > menuHeight && $(window).height() - position < buttonHeight + menuHeight) {
+ menu.addClass("dropup");
+ }
+ }).on('hidden.bs.dropdown', '.btn-group', function() {
+ // always reset after close
+ $(this).removeClass('dropup');
+ });
+
// Enable tabs dynamic loading
$(document).on('show.bs.tab', function(e) {
var link = $(e.target);