# HG changeset patch # User Thierry Florac # Date 1512729887 -3600 # Node ID 86b71518e457850054e7e1e3c489d4131ea362aa # Parent 9f1dded7e725f0c82ccb8485ff2315635b0a914b Change dropdown menu orientation dynamically based on available free space (MyAMS.js) diff -r 9f1dded7e725 -r 86b71518e457 src/pyams_skin/resources/js/myams.js --- 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('' + widgetData.message + ''); - } - // 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); diff -r 9f1dded7e725 -r 86b71518e457 src/pyams_skin/resources/js/myams.min.js --- a/src/pyams_skin/resources/js/myams.min.js Fri Dec 08 11:40:56 2017 +0100 +++ b/src/pyams_skin/resources/js/myams.min.js Fri Dec 08 11:44:47 2017 +0100 @@ -1,1 +1,1 @@ -!function(e,a){"use strict";var t=a.console;String.prototype.startsWith=function(e){var a=this.length,t=e.length;return!(a0},void 0===e.scrollbarWidth&&(e.scrollbarWidth=function(){var a=e('
').appendTo("body"),t=a.children(),n=t.innerWidth()-t.height(99).innerWidth();return a.remove(),n}),e.fn.extend({exists:function(){return e(this).length>0},objectOrParentWithClass:function(e){return this.hasClass(e)?this:this.parents("."+e)},listattr:function(a){var t=[];return this.each(function(){t.push(e(this).attr(a))}),t},style:function(e,a,t){if(void 0!==this.get(0)){var n=this.get(0).style;return void 0!==e?void 0!==a?(t=void 0!==t?t:"",n.setProperty(e,a,t),this):n.getPropertyValue(e):n}},removeClassPrefix:function(a){return this.each(function(t,n){var s=n.className.split(" ").map(function(e){return e.startsWith(a)?"":e});n.className=e.trim(s.join(" "))}),this},contextMenu:function(a){function t(t,n,s){var i=e(window)[n](),r=e(a.menuSelector)[n](),o=t;return t+r>i&&r',openedSign:''},a),n=e(this);n.find("LI").each(function(){var a=e(this);if(a.find("UL").size()>0){a.find("A:first").append(""+t.closedSign+"");var n=a.find("A:first");"#"===n.attr("href")&&n.click(function(){return!1})}}),n.find("LI.active").each(function(){var a=e(this).parents("UL"),n=a.parent("LI");a.slideDown(t.speed),n.find("b:first").html(t.openedSign),n.addClass("open")}),n.find("LI A").on("click",function(){var a=e(this);if(!a.hasClass("active")){var s=a.attr("href").replace(/^#/,""),i=a.parent().find("UL");if(t.accordion){var r=a.parent().parents("UL"),o=n.find("UL:visible");o.each(function(a){var n=!0;if(r.each(function(e){if(r[e]===o[a])return n=!1,!1}),n&&i!==o[a]){var c=e(o[a]);!s&&c.hasClass("active")||c.slideUp(t.speed,function(){e(this).parent("LI").removeClass("open").find("B:first").delay(t.speed).html(t.closedSign)})}})}var c=a.parent().find("UL:first");s||!c.is(":visible")||c.hasClass("active")?c.slideDown(t.speed,function(){a.parent("LI").addClass("open").find("B:first").delay(t.speed).html(t.openedSign)}):c.slideUp(t.speed,function(){a.parent("LI").removeClass("open").find("B:first").delay(t.speed).html(t.closedSign)})}})}}),e.UTF8={encode:function(e){e=e.replace(/\r\n/g,"\n");for(var a="",t=0;t127&&n<2048?(a+=String.fromCharCode(n>>6|192),a+=String.fromCharCode(63&n|128)):(a+=String.fromCharCode(n>>12|224),a+=String.fromCharCode(n>>6&63|128),a+=String.fromCharCode(63&n|128))}return a},decode:function(e){for(var a="",t=0,n=0,s=0,i=0;t191&&n<224?(s=e.charCodeAt(t+1),a+=String.fromCharCode((31&n)<<6|63&s),t+=2):(s=e.charCodeAt(t+1),i=e.charCodeAt(t+2),a+=String.fromCharCode((15&n)<<12|(63&s)<<6|63&i),t+=3);return a}},void 0===a.MyAMS&&(a.MyAMS={devmode:!0,devext:"",lang:"en",throttleDelay:350,menuSpeed:235,navbarHeight:49,ajaxNav:!0,enableWidgets:!0,enableMobile:!1,enableFastclick:!1,warnOnFormChange:!1,ismobile:/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase())});var n=a.MyAMS,s=n;n.baseURL=function(){var a=e('script[src*="/myams.js"], script[src*="/myams.min.js"]').attr("src");return s.devmode=a.indexOf(".min.js")<0,s.devext=s.devmode?"":".min",a.substring(0,a.lastIndexOf("/")+1)}(),n.log=function(){t&&t.debug&&t.debug(this,arguments)},n.getQueryVar=function(e,a){if(e.indexOf("?")<0)return!1;e.endsWith("&")||(e+="&");var t=new RegExp(".*?[&\\?]"+a+"=(.*?)&.*"),n=e.replace(t,"$1");return n!==e&&n},n.rgb2hex=function(a){return"#"+e.map(a.match(/\b(\d+)\b/g),function(e){return("0"+parseInt(e).toString(16)).slice(-2)}).join("")},n.generateId=function(){function e(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return e()+e()+e()+e()},n.generateUUID=function(){var e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var t=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"===a?t:3&t|8).toString(16)})},n.getObject=function(e,a){if(e){if("string"!=typeof e)return e;var t=e.split(".");a=void 0===a||null===a?window:a;for(var n=0;n").attr({rel:"stylesheet",type:"text/css",href:i,"data-ams-id":t}).appendTo(n)}},n.event={stop:function(e){e||(e=window.event),e&&(e.stopPropagation?(e.stopPropagation(),e.preventDefault()):(e.cancelBubble=!0,e.returnValue=!1))}},n.browser={getInternetExplorerVersion:function(){var e=-1;if("Microsoft Internet Explorer"===navigator.appName){var a=navigator.userAgent;null!==new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})").exec(a)&&(e=parseFloat(RegExp.$1))}return e},checkVersion:function(){var e="You're not using Windows Internet Explorer.",t=this.getInternetExplorerVersion();t>-1&&(e=t>=8?"You're using a recent copy of Windows Internet Explorer.":"You should upgrade your copy of Windows Internet Explorer."),a.alert&&a.alert(e)},isIE8orlower:function(){var e="0",a=this.getInternetExplorerVersion();return a>-1&&(e=a>=9?0:1),e},copyToClipboard:function(i){function r(i){var r=!1;if(window.clipboardData&&window.clipboardData.setData)r=clipboardData.setData("Text",i);else if(document.queryCommandSupported&&document.queryCommandSupported("copy")){var o=e("