loleaflet/dist/toolbar/w2ui-1.5.rc1.js | 2512 ++++++++++++++++++++++-----------
 loleaflet/dist/w2ui-1.5.rc1.css        |  194 ++
 2 files changed, 1917 insertions(+), 789 deletions(-)

New commits:
commit 1c4fdf7df1afe5514a8ea1b94aa68044f20fba66
Author: Henry Castro <hcas...@collabora.com>
Date:   Mon Jun 18 21:06:37 2018 -0400

    loleaflet: update w2ui-1.5.rc1
    
    https://github.com/vitmalina/w2ui.git
    1de0c6dc7a756561fdb734ae14ba6972805f7b8c build,
    
    Change-Id: If2e2142fe17ea61ccf106008a1fea5d380a3cc81
    Reviewed-on: https://gerrit.libreoffice.org/56074
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>

diff --git a/loleaflet/dist/toolbar/w2ui-1.5.rc1.js 
b/loleaflet/dist/toolbar/w2ui-1.5.rc1.js
index 89f54de62..e38fd4fd7 100644
--- a/loleaflet/dist/toolbar/w2ui-1.5.rc1.js
+++ b/loleaflet/dist/toolbar/w2ui-1.5.rc1.js
@@ -1,4 +1,4 @@
-/* w2ui 1.5.rc1 (nightly) (c) http://w2ui.com, vitmal...@gmail.com */
+/* w2ui 1.5.x (nightly) (c) http://w2ui.com, vitmal...@gmail.com */
 var w2ui  = w2ui  || {};
 var w2obj = w2obj || {}; // expose object to be able to overwrite default 
functions
 
@@ -28,12 +28,18 @@ var w2obj = w2obj || {}; // expose object to be able to 
overwrite default functi
 *   - add w2utils.lang wrap for all captions in all buttons.
 *   - $().w2date(), $().w2dateTime()
 *
+* == 1.5 change
+*   - parseColor(str) returns rgb
+*   - rgb2hsv, hsv2rgb
+*   - color.onSelect
+*   - refactored w2tag object, it has more potential with $().data('w2tag')
+*
 ************************************************/
 
 var w2utils = (function ($) {
     var tmp = {}; // for some temp variables
     var obj = {
-        version  : '1.5.RC1',
+        version  : '1.5.x',
         settings : {
             "locale"            : "en-us",
             "dateFormat"        : "m/d/yyyy",
@@ -61,6 +67,7 @@ var w2utils = (function ($) {
         isHex           : isHex,
         isAlphaNumeric  : isAlphaNumeric,
         isEmail         : isEmail,
+        isIpAddress     : isIpAddress,
         isDate          : isDate,
         isTime          : isTime,
         isDateTime      : isDateTime,
@@ -83,6 +90,7 @@ var w2utils = (function ($) {
         lock            : lock,
         unlock          : unlock,
         message         : message,
+        naturalCompare  : naturalCompare,
         lang            : lang,
         locale          : locale,
         getSize         : getSize,
@@ -92,17 +100,22 @@ var w2utils = (function ($) {
         checkUniqueId   : checkUniqueId,
         parseRoute      : parseRoute,
         cssPrefix       : cssPrefix,
+        parseColor      : parseColor,
+        hsv2rgb         : hsv2rgb,
+        rgb2hsv         : rgb2hsv,
         getCursorPosition : getCursorPosition,
         setCursorPosition : setCursorPosition,
         testLocalStorage  : testLocalStorage,
         hasLocalStorage   : testLocalStorage(),
         // some internal variables
-        isIOS : ((navigator.userAgent.toLowerCase().indexOf('iphone') != -1 ||
-                 navigator.userAgent.toLowerCase().indexOf('ipod') != -1 ||
-                 navigator.userAgent.toLowerCase().indexOf('ipad') != -1)
+        isIOS : ((navigator.userAgent.toLowerCase().indexOf('iphone') !== -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('ipod') !== -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('ipad') !== -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('mobile') !== -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('android') !== -1)
                  ? true : false),
-        isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') != -1 ||
-                 navigator.userAgent.toLowerCase().indexOf('trident') != -1 )
+        isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') !== -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('trident') !== -1 )
                  ? true : false)
     };
     return obj;
@@ -118,7 +131,7 @@ var w2utils = (function ($) {
     }
 
     function isFloat (val) {
-        if (typeof val == 'string') val = val.replace(/\s+/g, 
'').replace(w2utils.settings.groupSymbol, 
'').replace(w2utils.settings.decimalSymbol, '.');
+        if (typeof val === 'string') val = val.replace(/\s+/g, 
'').replace(w2utils.settings.groupSymbol, 
'').replace(w2utils.settings.decimalSymbol, '.');
         return (typeof val === 'number' || (typeof val === 'string' && val !== 
'')) && !isNaN(Number(val));
     }
 
@@ -135,7 +148,7 @@ var w2utils = (function ($) {
     }
 
     function isHex (val) {
-        var re = /^[a-fA-F0-9]+$/;
+        var re = /^(0x)?[0-9a-fA-F]+$/;
         return re.test(val);
     }
 
@@ -145,10 +158,18 @@ var w2utils = (function ($) {
     }
 
     function isEmail (val) {
-        var email = /^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
+        var email = 
/^[a-zA-Z0-9._%\-+]+@[а-яА-Яa-zA-Z0-9.-]+\.[а-яА-Яa-zA-Z]+$/;
         return email.test(val);
     }
 
+    function isIpAddress (val) {
+        var re = new RegExp('^' +
+                            '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' +
+                            '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' +
+                            '$');
+        return re.test(val);
+    }
+
     function isDate (val, format, retDate) {
         if (!val) return false;
 
@@ -166,6 +187,14 @@ var w2utils = (function ($) {
             year  = val.getUTCFullYear();
             month = val.getUTCMonth() + 1;
             day   = val.getUTCDate();
+               } else if (String(new Date(val)) != 'Invalid Date') {
+            val = new Date(val);
+            if (retDate !== true) return true;
+            return val;
+            val = new Date(val);
+            year  = val.getUTCFullYear();
+            month = val.getUTCMonth() + 1;
+            day   = val.getUTCDate();
         } else {
             val = String(val);
             // convert month formats
@@ -206,7 +235,7 @@ var w2utils = (function ($) {
         dt    = new Date(year, month - 1, day);
         // do checks
         if (month == null) return false;
-        if (String(dt) == 'Invalid Date') return false;
+        if (String(dt) === 'Invalid Date') return false;
         if ((dt.getMonth() + 1 !== month) || (dt.getDate() !== day) || 
(dt.getFullYear() !== year)) return false;
         if (retDate === true) return dt; else return true;
     }
@@ -255,10 +284,16 @@ var w2utils = (function ($) {
         if (typeof val.getUTCFullYear === 'function') { // date object
             if (retDate !== true) return true;
             return val;
-        } else if (parseInt(val) == val && parseInt(val) > 0) {
+        } else if (parseInt(val) === val && parseInt(val) >= 0) {
             val = new Date(parseInt(val));
             if (retDate !== true) return true;
             return val;
+        } else if (parseInt(val) === val && parseInt(val) < 0) {
+            return false;
+               } else if (String(new Date(val)) != 'Invalid Date') {
+            val = new Date(val);
+            if (retDate !== true) return true;
+            return val;
         } else {
             var tmp = String(val).indexOf(' ');
             var values  = [val.substr(0, tmp), val.substr(tmp).trim()];
@@ -289,7 +324,7 @@ var w2utils = (function ($) {
         } else {
             d1 = new Date(dateStr);
         }
-        if (String(d1) == 'Invalid Date') return '';
+        if (String(d1) === 'Invalid Date') return '';
 
         var d2  = new Date();
         var sec = (d2.getTime() - d1.getTime()) / 1000;
@@ -346,10 +381,10 @@ var w2utils = (function ($) {
     }
 
     function date (dateStr) {
-        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' 
&& !dateStr.getMonth)) return '';
+        if (dateStr === '' || dateStr == null || (typeof dateStr === 'object' 
&& !dateStr.getMonth)) return '';
         var d1 = new Date(dateStr);
         if (w2utils.isInt(dateStr)) d1 = new Date(Number(dateStr)); // for 
unix timestamps
-        if (String(d1) == 'Invalid Date') return '';
+        if (String(d1) === 'Invalid Date') return '';
 
         var months = w2utils.settings.shortmonths;
         var d2   = new Date(); // today
@@ -379,7 +414,7 @@ var w2utils = (function ($) {
     }
 
     function formatNumber (val, fraction, useGrouping) {
-        if (val == null || val === '' || typeof val == 'object') return '';
+        if (val == null || val === '' || typeof val === 'object') return '';
         var options = {
             minimumFractionDigits : fraction,
             maximumFractionDigits : fraction,
@@ -394,11 +429,11 @@ var w2utils = (function ($) {
 
     function formatDate (dateStr, format) { // IMPORTANT dateStr HAS TO BE 
valid JavaScript Date String
         if (!format) format = this.settings.dateFormat;
-        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' 
&& !dateStr.getMonth)) return '';
+        if (dateStr === '' || dateStr == null || (typeof dateStr === 'object' 
&& !dateStr.getMonth)) return '';
 
         var dt = new Date(dateStr);
         if (w2utils.isInt(dateStr)) dt = new Date(Number(dateStr)); // for 
unix timestamps
-        if (String(dt) == 'Invalid Date') return '';
+        if (String(dt) === 'Invalid Date') return '';
 
         var year  = dt.getFullYear();
         var month = dt.getMonth();
@@ -423,7 +458,7 @@ var w2utils = (function ($) {
         var months = w2utils.settings.shortmonths;
         var fullMonths = w2utils.settings.fullmonths;
         if (!format) format = this.settings.timeFormat;
-        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' 
&& !dateStr.getMonth)) return '';
+        if (dateStr === '' || dateStr == null || (typeof dateStr === 'object' 
&& !dateStr.getMonth)) return '';
 
         var dt = new Date(dateStr);
         if (w2utils.isInt(dateStr)) dt  = new Date(Number(dateStr)); // for 
unix timestamps
@@ -433,7 +468,7 @@ var w2utils = (function ($) {
             dt.setHours(tmp.hours);
             dt.setMinutes(tmp.minutes);
         }
-        if (String(dt) == 'Invalid Date') return '';
+        if (String(dt) === 'Invalid Date') return '';
 
         var type = 'am';
         var hour = dt.getHours();
@@ -445,6 +480,7 @@ var w2utils = (function ($) {
         if (format.indexOf('am') !== -1 || format.indexOf('pm') !== -1) {
             if (hour >= 12) type = 'pm';
             if (hour > 12)  hour = hour - 12;
+            if (hour === 0) hour = 12;
         }
         return format.toLowerCase()
             .replace('am', type)
@@ -463,17 +499,17 @@ var w2utils = (function ($) {
 
     function formatDateTime(dateStr, format) {
         var fmt;
-        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' 
&& !dateStr.getMonth)) return '';
+        if (dateStr === '' || dateStr == null || (typeof dateStr === 'object' 
&& !dateStr.getMonth)) return '';
         if (typeof format !== 'string') {
             fmt = [this.settings.dateFormat, this.settings.timeFormat];
         } else {
             fmt = format.split('|');
             fmt[0] = fmt[0].trim();
-            fmt[1] = fmt[1].trim();
+            fmt[1] = (fmt.length > 1 ? fmt[1].trim() : 
this.settings.timeFormat);
         }
         // older formats support
-        if (fmt[1] == 'h12') fmt[1] = 'h:m pm';
-        if (fmt[1] == 'h24') fmt[1] = 'h24:m';
+        if (fmt[1] === 'h12') fmt[1] = 'h:m pm';
+        if (fmt[1] === 'h24') fmt[1] = 'h24:m';
         return this.formatDate(dateStr, fmt[0]) + ' ' + 
this.formatTime(dateStr, fmt[1]);
     }
 
@@ -1227,7 +1263,7 @@ var w2utils = (function ($) {
         if (!options.msg) mess.css({ 'background-color': 'transparent', 
'border': '0px' });
         if (options.spinner === true) options.msg = '<div class="w2ui-spinner" 
'+ (!options.msg ? 'style="width: 35px; height: 35px"' : '') +'></div>' + 
options.msg;
         if (options.opacity != null) $lock.css('opacity', options.opacity);
-        if (typeof $lock.fadeIn == 'function') {
+        if (typeof $lock.fadeIn === 'function') {
             $lock.fadeIn(200);
             mess.html(options.msg).fadeIn(200);
         } else {
@@ -1308,14 +1344,14 @@ var w2utils = (function ($) {
                 'transition': '0.15s',
                 'transform': 'translateY(-' + options.height + 'px)'
             })).addClass('w2ui-closing');
-            if (msgCount == 1) {
+            if (msgCount === 1) {
                 if (this.unlock) {
                     if (where.param) this.unlock(where.param, 150); else 
this.unlock(150);
                 }
             } else {
                 $(where.box).find('#w2ui-message'+ 
(msgCount-2)).css('z-index', 1500);
             }
-            closeTimer = setTimeout(function () { closeCB($msg, options) }, 
150);
+            closeTimer = setTimeout(function () { closeCB($msg, options); }, 
150);
 
         } else {
 
@@ -1346,9 +1382,9 @@ var w2utils = (function ($) {
                 .data('prev_focus', $(':focus'));
             var display = $(where.box).find('#w2ui-message'+ 
msgCount).css('display');
             $(where.box).find('#w2ui-message'+ 
msgCount).css(w2utils.cssPrefix({
-                'transform': (display == 'none' ? 'translateY(-' + 
options.height + 'px)' : 'translateY(0px)')
+                'transform': (display === 'none' ? 'translateY(-' + 
options.height + 'px)' : 'translateY(0px)')
             }));
-            if (display == 'none') {
+            if (display === 'none') {
                 $(where.box).find('#w2ui-message'+ 
msgCount).show().html(options.html);
                 options.box = $(where.box).find('#w2ui-message'+ msgCount);
                 // before event
@@ -1361,7 +1397,7 @@ var w2utils = (function ($) {
                 // timer needs to animation
                 setTimeout(function () {
                     $(where.box).find('#w2ui-message'+ 
msgCount).css(w2utils.cssPrefix({
-                        'transform': (display == 'none' ? 'translateY(0px)' : 
'translateY(-' + options.height + 'px)')
+                        'transform': (display === 'none' ? 'translateY(0px)' : 
'translateY(-' + options.height + 'px)')
                     }));
                 }, 1);
                 // timer for lock
@@ -1448,11 +1484,11 @@ var w2utils = (function ($) {
         if (translation == null) return phrase; else return translation;
     }
 
-    function locale (locale) {
+    function locale (locale, callBack) {
         if (!locale) locale = 'en-us';
 
         // if the locale is an object, not a string, than we assume it's a
-        if(typeof locale !== "string" ) {
+        if (typeof locale !== "string" ) {
             w2utils.settings = $.extend(true, w2utils.settings, locale);
             return;
         }
@@ -1467,9 +1503,9 @@ var w2utils = (function ($) {
             url      : locale,
             type     : "GET",
             dataType : "JSON",
-            async    : false,
             success  : function (data, status, xhr) {
                 w2utils.settings = $.extend(true, w2utils.settings, data);
+                if (typeof callBack === 'function') callBack();
             },
             error    : function (xhr, status, msg) {
                 console.log('ERROR: Cannot load locale '+ locale);
@@ -1569,7 +1605,7 @@ var w2utils = (function ($) {
         var doc = input.ownerDocument || input.document;
         var win = doc.defaultView || doc.parentWindow;
         var sel;
-        if (input.tagName && input.tagName.toUpperCase() == 'INPUT' && 
input.selectionStart) {
+        if (input.tagName && input.tagName.toUpperCase() === 'INPUT' && 
input.selectionStart) {
             // standards browser
             caretOffset = input.selectionStart;
         } else {
@@ -1582,7 +1618,7 @@ var w2utils = (function ($) {
                     preCaretRange.setEnd(range.endContainer, range.endOffset);
                     caretOffset = preCaretRange.toString().length;
                 }
-            } else if ( (sel = doc.selection) && sel.type != "Control") {
+            } else if ( (sel = doc.selection) && sel.type !== "Control") {
                 var textRange = sel.createRange();
                 var preCaretTextRange = doc.body.createTextRange();
                 preCaretTextRange.moveToElementText(input);
@@ -1641,6 +1677,149 @@ var w2utils = (function ($) {
         }
     }
 
+    function parseColor(str) {
+        if (typeof str !== 'string') return null; else str = 
str.trim().toUpperCase();
+        if (str[0] === '#') str = str.substr(1);
+        var color = {};
+        if (str.length === 3) {
+            color = {
+                r: parseInt(str[0] + str[0], 16),
+                g: parseInt(str[1] + str[1], 16),
+                b: parseInt(str[2] + str[2], 16),
+                a: 1
+            };
+        } else if (str.length === 6) {
+            color = {
+                r: parseInt(str.substr(0, 2), 16),
+                g: parseInt(str.substr(2, 2), 16),
+                b: parseInt(str.substr(4, 2), 16),
+                a: 1
+            };
+        } else if (str.length > 4 && str.substr(0, 4) === 'RGB(') {
+            var tmp = str.replace('RGB', '').replace(/\(/g, '').replace(/\)/g, 
'').split(',');
+            color = {
+                r: parseInt(tmp[0], 10),
+                g: parseInt(tmp[1], 10),
+                b: parseInt(tmp[2], 10),
+                a: 1
+            };
+        } else if (str.length > 5 && str.substr(0, 5) === 'RGBA(') {
+            var tmp = str.replace('RGBA', '').replace(/\(/g, 
'').replace(/\)/g, '').split(',');
+            color = {
+                r: parseInt(tmp[0], 10),
+                g: parseInt(tmp[1], 10),
+                b: parseInt(tmp[2], 10),
+                a: parseFloat(tmp[3])
+            };
+        } else {
+            // word color
+            return null;
+        }
+        return color;
+    }
+
+    // h=0..360, s=0..100, v=0..100
+    function hsv2rgb(h, s, v, a) {
+        var r, g, b, i, f, p, q, t;
+        if (arguments.length === 1) {
+            s = h.s; v = h.v; a = h.a; h = h.h;
+        }
+        h = h / 360;
+        s = s / 100;
+        v = v / 100;
+        i = Math.floor(h * 6);
+        f = h * 6 - i;
+        p = v * (1 - s);
+        q = v * (1 - f * s);
+        t = v * (1 - (1 - f) * s);
+        switch (i % 6) {
+            case 0: r = v, g = t, b = p; break;
+            case 1: r = q, g = v, b = p; break;
+            case 2: r = p, g = v, b = t; break;
+            case 3: r = p, g = q, b = v; break;
+            case 4: r = t, g = p, b = v; break;
+            case 5: r = v, g = p, b = q; break;
+        }
+        return {
+            r: Math.round(r * 255),
+            g: Math.round(g * 255),
+            b: Math.round(b * 255),
+            a: (a != null ? a : 1)
+        };
+    }
+
+    // r=0..255, g=0..255, b=0..255
+    function rgb2hsv(r, g, b, a) {
+        if (arguments.length === 1) {
+            g = r.g; b = r.b; a = r.a; r = r.r;
+        }
+        var max = Math.max(r, g, b), min = Math.min(r, g, b),
+            d = max - min,
+            h,
+            s = (max === 0 ? 0 : d / max),
+            v = max / 255;
+        switch (max) {
+            case min: h = 0; break;
+            case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;
+            case g: h = (b - r) + d * 2; h /= 6 * d; break;
+            case b: h = (r - g) + d * 4; h /= 6 * d; break;
+        }
+        return {
+            h: Math.round(h * 360),
+            s: Math.round(s * 100),
+            v: Math.round(v * 100),
+            a: (a != null ? a : 1)
+        }
+    }
+
+    /*! from litejs.com / MIT Licence
+        https://github.com/litejs/natural-compare-lite/blob/master/index.js */
+    /*
+     * @version    1.4.0
+     * @date       2015-10-26
+     * @stability  3 - Stable
+     * @author     Lauri Rooden 
(https://github.com/litejs/natural-compare-lite)
+     * @license    MIT License
+     */
+    function naturalCompare(a, b) {
+        var i, codeA
+        , codeB = 1
+        , posA = 0
+        , posB = 0
+        , alphabet = String.alphabet;
+
+        function getCode(str, pos, code) {
+            if (code) {
+                for (i = pos; code = getCode(str, i), code < 76 && code > 65;) 
++i;
+                return +str.slice(pos - 1, i);
+            }
+            code = alphabet && alphabet.indexOf(str.charAt(pos));
+            return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), 
code < 45 || code > 127) ? code
+                : code < 46 ? 65               // -
+                : code < 48 ? code - 1
+                : code < 58 ? code + 18        // 0-9
+                : code < 65 ? code - 11
+                : code < 91 ? code + 11        // A-Z
+                : code < 97 ? code - 37
+                : code < 123 ? code + 5        // a-z
+                : code - 63;
+        }
+
+
+        if ((a+="") != (b+="")) for (;codeB;) {
+            codeA = getCode(a, posA++);
+            codeB = getCode(b, posB++);
+
+            if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
+                codeA = getCode(a, posA, posA);
+                codeB = getCode(b, posB, posA = i);
+                posB = i;
+            }
+
+            if (codeA != codeB) return (codeA < codeB) ? -1 : 1;
+        }
+        return 0;
+    }
 })(jQuery);
 
 /***********************************************************
@@ -1766,15 +1945,15 @@ w2utils.event = {
         var $ = jQuery;
         var scope;
         // allow 'eventName.scope' syntax
-        if (typeof edata == 'string' && edata.indexOf('.') != -1) {
+        if (typeof edata === 'string' && edata.indexOf('.') !== -1) {
             var tmp = edata.split('.');
             edata = tmp[0];
             scope = tmp[1];
         }
         // allow 'eventName:after' syntax
-        if (typeof edata == 'string' && edata.indexOf(':') != -1) {
+        if (typeof edata === 'string' && edata.indexOf(':') !== -1) {
             var tmp = edata.split(':');
-            if (['complete', 'done'].indexOf(edata[1]) != -1) edata[1] = 
'after';
+            if (['complete', 'done'].indexOf(edata[1]) !== -1) edata[1] = 
'after';
             edata = {
                 type    : tmp[0],
                 execute : tmp[1]
@@ -1786,7 +1965,6 @@ w2utils.event = {
         if (!edata.type) { console.log('ERROR: You must specify event type 
when calling .on() method of '+ this.name); return; }
         if (!handler) { console.log('ERROR: You must specify event handler 
function when calling .on() method of '+ this.name); return; }
         if (!$.isArray(this.handlers)) this.handlers = [];
-        console.log('add', edata);
         this.handlers.push({ edata: edata, handler: handler });
     },
 
@@ -1794,19 +1972,19 @@ w2utils.event = {
         var $ = jQuery;
         var scope;
         // allow 'eventName.scope' syntax
-        if (typeof edata == 'string' && edata.indexOf('.') != -1) {
+        if (typeof edata === 'string' && edata.indexOf('.') !== -1) {
             var tmp = edata.split('.');
             edata = tmp[0];
             scope = tmp[1];
         }
         // allow 'eventName:after' syntax
-        if (typeof edata == 'string' && edata.indexOf(':') != -1) {
+        if (typeof edata === 'string' && edata.indexOf(':') !== -1) {
             var tmp = edata.split(':');
-            if (['complete', 'done'].indexOf(edata[1]) != -1) edata[1] = 
'after';
+            if (['complete', 'done'].indexOf(edata[1]) !== -1) edata[1] = 
'after';
             edata = {
                 type    : tmp[0],
                 execute : tmp[1]
-            }
+            };
         }
         if (!$.isPlainObject(edata)) edata = { type: edata };
         edata = $.extend({}, { type: null, execute: 'before', target: null, 
onComplete: null }, edata);
@@ -1820,7 +1998,7 @@ w2utils.event = {
             if ((t.edata.type === edata.type || edata.type === '*' || 
(t.edata.scope != null && edata.type == '')) &&
                 (t.edata.target === edata.target || edata.target == null) &&
                 (t.edata.execute === edata.execute || edata.execute == null) &&
-                (t.handler === handler || handler == null || (scope != null && 
t.edata.scope == scope)))
+                ((t.handler === handler && handler != null) || (scope != null 
&& t.edata.scope == scope)))
             {
                 // match
             } else {
@@ -1898,7 +2076,7 @@ w2utils.event = {
         if (edata.phase === 'after') {
             if (typeof edata.onComplete === 'function') 
edata.onComplete.call(this, edata);
             for (var i = 0; i < edata.doneHandlers.length; i++) {
-                if (typeof edata.doneHandlers[i] == 'function') {
+                if (typeof edata.doneHandlers[i] === 'function') {
                     edata.doneHandlers[i].call(this, edata);
                 }
             }
@@ -1951,7 +2129,7 @@ w2utils.event = {
         }
 
         function clearMarkedText(index, el) {
-            while (el.innerHTML.indexOf('<span class="w2ui-marker">') != -1) {
+            while (el.innerHTML.indexOf('<span class="w2ui-marker">') !== -1) {
                 el.innerHTML = el.innerHTML.replace(/\<span 
class=\"w2ui\-marker\"\>((.|\n|\r)*)\<\/span\>/ig, '$1'); // unmark
             }
         }
@@ -1961,7 +2139,7 @@ w2utils.event = {
 
     $.fn.w2tag = function (text, options) {
         // only one argument
-        if (arguments.length == 1 && typeof text == 'object') {
+        if (arguments.length === 1 && typeof text === 'object') {
             options = text;
             if (options.html != null) text = options.html;
         }
@@ -1981,7 +2159,8 @@ w2utils.event = {
             onHide          : null,     // callBack when hidden
             hideOnKeyPress  : true,     // hide tag if key pressed
             hideOnBlur      : false,    // hide tag on blur
-            hideOnClick     : false     // hide tag on document click
+            hideOnClick     : false,    // hide tag on document click
+            hideOnChange    : true
         }, options);
         if (options.name != null && options.id == null) options.id = 
options.name;
 
@@ -1991,162 +2170,196 @@ w2utils.event = {
         // remove all tags
         if ($(this).length === 0) {
             $('.w2ui-tag').each(function (index, el) {
-                var opt = $(el).data('options');
-                if (opt == null) opt = {};
-                $($(el).data('taged-el'))
-                    .removeClass(opt.inputClass)
-                    .removeData('w2tag')
-                    .removeData('checkIfMoved');
-                clearInterval($(el).data('timer'));
-                $(el).remove();
+                var tag = $(el).data('w2tag');
+                if (tag) tag.hide();
             });
             return;
         }
         return $(this).each(function (index, el) {
-            // show or hide tag
+            // main object
+            var tag;
             var origID = (options.id ? options.id : el.id);
-            var tagID  = w2utils.escapeId(origID);
-            var $tags  = $('#w2ui-tag-'+tagID);
+            if (origID == '') { // search for an id
+                origID = $(el).find('input').attr('id');
+            }
+            if (!origID) {
+                origID = 'noid';
+            }
+            var tmpID  = w2utils.escapeId(origID);
+            if ($(this).data('w2tag') != null) {
+                tag = $(this).data('w2tag');
+                $.extend(tag.options, options);
+            } else {
+                tag = {
+                    id        : origID,
+                    attachedTo: el,          // element attached to
+                    box       : $('#w2ui-tag-' + tmpID), // tag itself
+                    options   : $.extend({}, options),
+                    // methods
+                    init      : init,      // attach events
+                    hide      : hide,      // hide tag
+                    getPos    : getPos,    // gets position of tag
+                    isMoved   : isMoved,   // if called, will adjust position
+                    // internal
+                    tmp       : {}         // for temp variables
+                };
+            }
+            // show or hide tag
             if (text === '' || text == null) {
-                // remmove element
-                $tags.css('opacity', 0);
-                clearInterval($tags.data('timer'));
-                $tags.remove();
-                return;
-            } else if ($tags.length !== 0) {
+                tag.hide();
+            } else if (tag.box.length !== 0) {
                 // if already present
-                options = $.extend($tags.data('options'), options);
-                $tags.data('options', options);
-                $tags.find('.w2ui-tag-body')
-                    .attr('style', options.style)
-                    .addClass(options.className)
-                    .html(options.html);
-                checkIfMoved(true);
+                tag.box.find('.w2ui-tag-body')
+                    .css(tag.options.css)
+                    .attr('style', tag.options.style)
+                    .addClass(tag.options.className)
+                    .html(tag.options.html);
             } else {
-                var originalCSS = '';
-                if ($(el).length > 0) originalCSS = $(el)[0].style.cssText;
+                tag.tmp.originalCSS = '';
+                if ($(tag.attachedTo).length > 0) tag.tmp.originalCSS = 
$(tag.attachedTo)[0].style.cssText;
                 // insert
                 $('body').append(
-                    '<div onclick="event.stopPropagation()" 
style="display:none;" id="w2ui-tag-'+ origID +'" '+
-                    '       class="w2ui-tag '+ ($(el).parents('.w2ui-popup, 
.w2ui-overlay-popup, .w2ui-message').length > 0 ? 'w2ui-tag-popup' : '') + '">'+
+                    '<div onclick="event.stopPropagation()" style="display: 
none;" id="w2ui-tag-'+ tag.id +'" '+
+                    '       class="w2ui-tag '+ 
($(tag.attachedTo).parents('.w2ui-popup, .w2ui-overlay-popup, 
.w2ui-message').length > 0 ? 'w2ui-tag-popup' : '') + '">'+
                     '   <div style="margin: -2px 0px 0px -2px; white-space: 
nowrap;">'+
-                    '      <div class="w2ui-tag-body '+ options.className +'" 
style="'+ (options.style || '') +'">'+ text +'</div>'+
+                    '      <div class="w2ui-tag-body '+ tag.options.className 
+'" style="'+ (tag.options.style || '') +'">'+ text +'</div>'+
                     '   </div>' +
                     '</div>');
-                $tags = $('#w2ui-tag-'+tagID);
-                $(el).data('w2tag', $tags.get(0)).data('checkIfMoved', 
checkIfMoved);
+                tag.box = $('#w2ui-tag-' + tmpID);
+                $(tag.attachedTo).data('w2tag', tag); // make available to 
element tag attached to
+                setTimeout(init, 1);
             }
+            return;
 
-            // need time out to allow tag to be rendered
-            setTimeout(function () {
-                $tags.css('display', 'block');
-                if (!$(el).offset()) return;
-                var pos = checkIfMoved(true);
-                if (pos == null) return;
-                $tags.css({
+            function init() {
+                tag.box.css('display', 'block');
+                if (!tag || !tag.box || !$(tag.attachedTo).offset()) return;
+                var pos = tag.getPos();
+                tag.box.css({
                         opacity : '1',
                         left    : pos.left + 'px',
                         top     : pos.top + 'px'
                     })
-                    .data('options', options)
-                    .data('taged-el', el)
-                    .data('position', pos.left + 'x' + pos.top)
-                    .data('timer', setTimeout(checkIfMoved, 100))
+                    .data('w2tag', tag)
                     .find('.w2ui-tag-body').addClass(pos['posClass']);
+                tag.tmp.pos = pos.left + 'x' + pos.top;
 
-                $(el).css(options.css)
+                $(tag.attachedTo)
                     .off('.w2tag')
-                    .addClass(options.inputClass);
+                    .css(tag.options.css)
+                    .addClass(tag.options.inputClass);
 
-                if (options.hideOnKeyPress) {
-                    $(el).on('keypress.w2tag', hideTag);
+                if (tag.options.hideOnKeyPress) {
+                    $(tag.attachedTo).on('keypress.w2tag', tag.hide);
+                }
+                if (options.hideOnChange) {
+                    if (el.nodeName === 'INPUT') {
+                        $(el).on('change.w2tag', tag.hide);
+                    } else {
+                        $(el).find('input').on('change.w2tag', tag.hide);
+                    }
                 }
-                if (options.hideOnBlur) {
-                    $(el).on('blur.w2tag', hideTag);
+                if (tag.options.hideOnBlur) {
+                    $(tag.attachedTo).on('blur.w2tag', tag.hide);
                 }
-                if (options.hideOnClick) {
-                    $(document).on('click.w2tag', hideTag)
+                if (tag.options.hideOnClick) {
+                    $(document).on('click.w2tag', tag.hide);
                 }
-                if (typeof options.onShow === 'function') options.onShow();
-            }, 1);
+                if (typeof tag.options.onShow === 'function') {
+                    tag.options.onShow();
+                }
+                isMoved();
+            }
 
             // bind event to hide it
-            function hideTag() {
-                $tags = $('#w2ui-tag-'+tagID);
-                if ($tags.length <= 0) return;
-                clearInterval($tags.data('timer'));
-                $tags.remove();
-                $(document).off('.w2tag');
-                $(el).off('.w2tag', hideTag)
-                    .removeClass(options.inputClass)
-                    .removeData('w2tag')
-                    .removeData('checkIfMoved');
-                if ($(el).length > 0) $(el)[0].style.cssText = originalCSS;
-                if (typeof options.onHide === 'function') options.onHide();
-            }
-
-            function checkIfMoved(checkOnly, instant) {
+            function hide() {
+                if (tag.box.length <= 0) return;
+                if (tag.tmp.timer) clearTimeout(tag.tmp.timer);
+                tag.box.remove();
+                if (tag.options.hideOnClick) {
+                    $(document).off('.w2tag');
+                }
+                $(tag.attachedTo).off('.w2tag')
+                    .removeClass(tag.options.inputClass)
+                    .removeData('w2tag');
+                // restore original CSS
+                if ($(tag.attachedTo).length > 0) {
+                    $(tag.attachedTo)[0].style.cssText = tag.tmp.originalCSS;
+                }
+                if (typeof tag.options.onHide === 'function') {
+                    tag.options.onHide();
+                }
+            }
+
+            function isMoved(instant) {
                 // monitor if destroyed
-                var offset = $(el).offset();
-                if ($(el).length === 0 || (offset.left === 0 && offset.top === 
0) || $tags.find('.w2ui-tag-body').length === 0) {
-                    clearInterval($tags.data('timer'));
-                    hideTag();
+                var offset = $(tag.attachedTo).offset();
+                if ($(tag.attachedTo).length === 0 || (offset.left === 0 && 
offset.top === 0) || tag.box.find('.w2ui-tag-body').length === 0) {
+                    tag.hide();
                     return;
                 }
-                if (!instant) setTimeout(checkIfMoved, 100);
-                // monitor if moved
+                var pos = getPos();
+                if (tag.tmp.pos !== pos.left + 'x' + pos.top) {
+                    tag.box
+                        .css(w2utils.cssPrefix({ 'transition': (instant ? '0s' 
: '.2s') }))
+                        .css({
+                            left: pos.left + 'px',
+                            top : pos.top + 'px'
+                        });
+                    tag.tmp.pos = pos.left + 'x' + pos.top;
+                }
+                if (tag.tmp.timer) clearTimeout(tag.tmp.timer);
+                tag.tmp.timer = setTimeout(isMoved, 100);
+            }
+
+            function getPos() {
+                var offset   = $(tag.attachedTo).offset();
                 var posClass = 'w2ui-tag-right';
-                var posLeft  = parseInt(offset.left + el.offsetWidth + 
(options.left ? options.left : 0));
-                var posTop   = parseInt(offset.top + (options.top ? 
options.top : 0));
-                var tagBody  = $tags.find('.w2ui-tag-body');
+                var posLeft  = parseInt(offset.left + 
tag.attachedTo.offsetWidth + (tag.options.left ? tag.options.left : 0));
+                var posTop   = parseInt(offset.top + (tag.options.top ? 
tag.options.top : 0));
+                var tagBody  = tag.box.find('.w2ui-tag-body');
                 var width    = tagBody[0].offsetWidth;
                 var height   = tagBody[0].offsetHeight;
-                if (typeof options.position == 'string' && 
options.position.indexOf('|') != -1) {
-                    options.position = options.position.split('|');
+                if (typeof tag.options.position === 'string' && 
tag.options.position.indexOf('|') !== -1) {
+                    tag.options.position = tag.options.position.split('|');
                 }
-                if (options.position == 'top') {
+                if (tag.options.position === 'top') {
                     posClass  = 'w2ui-tag-top';
-                    posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - 14;
-                    posTop    = parseInt(offset.top + (options.top ? 
options.top : 0)) - height - 10;
-                }
-                else if (options.position == 'bottom') {
+                    posLeft   = parseInt(offset.left + (tag.options.left ? 
tag.options.left : 0)) - 14;
+                    posTop    = parseInt(offset.top + (tag.options.top ? 
tag.options.top : 0)) - height - 10;
+                } else if (tag.options.position === 'bottom') {
                     posClass  = 'w2ui-tag-bottom';
-                    posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - 14;
-                    posTop    = parseInt(offset.top + el.offsetHeight + 
(options.top ? options.top : 0)) + 10;
-                }
-                else if (options.position == 'left') {
+                    posLeft   = parseInt(offset.left + (tag.options.left ? 
tag.options.left : 0)) - 14;
+                    posTop    = parseInt(offset.top + 
tag.attachedTo.offsetHeight + (tag.options.top ? tag.options.top : 0)) + 10;
+                } else if (tag.options.position === 'left') {
                     posClass  = 'w2ui-tag-left';
-                    posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - width - 20;
-                    posTop    = parseInt(offset.top + (options.top ? 
options.top : 0));
-                }
-                else if (Array.isArray(options.position)) {
+                    posLeft   = parseInt(offset.left + (tag.options.left ? 
tag.options.left : 0)) - width - 20;
+                    posTop    = parseInt(offset.top + (tag.options.top ? 
tag.options.top : 0));
+                } else if (Array.isArray(tag.options.position)) {
                     // try to fit the tag on screen in the order defined in 
the array
                     var maxWidth  = window.innerWidth;
-                    var maxHeight = window.innerHeight
-                    for (var i=0; i<options.position.length; i++) {
-                        var pos = options.position[i];
-                        if (pos == 'right') {
+                    var maxHeight = window.innerHeight;
+                    for (var i = 0; i < tag.options.position.length; i++) {
+                        var pos = tag.options.position[i];
+                        if (pos === 'right') {
                             posClass = 'w2ui-tag-right';
-                            posLeft  = parseInt(offset.left + el.offsetWidth + 
(options.left ? options.left : 0));
-                            posTop   = parseInt(offset.top + (options.top ? 
options.top : 0));
+                            posLeft  = parseInt(offset.left + 
tag.attachedTo.offsetWidth + (tag.options.left ? tag.options.left : 0));
+                            posTop   = parseInt(offset.top + (tag.options.top 
? tag.options.top : 0));
                             if (posLeft+width <= maxWidth) break;
-                        }
-                        else if (pos == 'left') {
+                        } else if (pos === 'left') {
                             posClass  = 'w2ui-tag-left';
-                            posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - width - 20;
-                            posTop    = parseInt(offset.top + (options.top ? 
options.top : 0));
+                            posLeft   = parseInt(offset.left + 
(tag.options.left ? tag.options.left : 0)) - width - 20;
+                            posTop    = parseInt(offset.top + (tag.options.top 
? tag.options.top : 0));
                             if (posLeft >= 0) break;
-                        }
-                        else if (pos == 'top') {
+                        } else if (pos === 'top') {
                             posClass  = 'w2ui-tag-top';
-                            posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - 14;
-                            posTop    = parseInt(offset.top + (options.top ? 
options.top : 0)) - height - 10;
+                            posLeft   = parseInt(offset.left + 
(tag.options.left ? tag.options.left : 0)) - 14;
+                            posTop    = parseInt(offset.top + (tag.options.top 
? tag.options.top : 0)) - height - 10;
                             if(posLeft+width <= maxWidth && posTop >= 0) break;
-                        }
-                        else if (pos == 'bottom') {
+                        } else if (pos === 'bottom') {
                             posClass  = 'w2ui-tag-bottom';
-                            posLeft   = parseInt(offset.left + (options.left ? 
options.left : 0)) - 14;
-                            posTop    = parseInt(offset.top + el.offsetHeight 
+ (options.top ? options.top : 0)) + 10;
+                            posLeft   = parseInt(offset.left + 
(tag.options.left ? tag.options.left : 0)) - 14;
+                            posTop    = parseInt(offset.top + 
tag.attachedTo.offsetHeight + (tag.options.top ? tag.options.top : 0)) + 10;
                             if (posLeft+width <= maxWidth && posTop+height <= 
maxHeight) break;
                         }
                     }
@@ -2156,12 +2369,6 @@ w2utils.event = {
                             .data('posClass', posClass);
                     }
                 }
-                if ($tags.data('position') !== posLeft + 'x' + posTop && 
checkOnly !== true) {
-                    $tags.css(w2utils.cssPrefix({ 'transition': (instant ? 
'0s' : '.2s') })).css({
-                        left: posLeft + 'px',
-                        top : posTop + 'px'
-                    }).data('position', posLeft + 'x' + posTop);
-                }
                 return { left: posLeft, top: posTop, posClass: posClass };
             }
         });
@@ -2197,14 +2404,14 @@ w2utils.event = {
             openAbove   : false,             // show above control
             tmp         : {}
         };
-        if (arguments.length == 1) {
-            if (typeof html == 'object') {
+        if (arguments.length === 1) {
+            if (typeof html === 'object') {
                 options = html;
             } else {
                 options = { html: html };
             }
         }
-        if (arguments.length == 2) options.html = html;
+        if (arguments.length === 2) options.html = html;
         if (!$.isPlainObject(options)) options = {};
         options = $.extend({}, defaults, options);
         if (options.name) name = '-' + options.name;
@@ -2222,10 +2429,10 @@ w2utils.event = {
         // hide previous if any
         if ($('#w2ui-overlay'+ name).length > 0) {
             tmp_hide = $('#w2ui-overlay'+ name)[0].hide;
-            $(document).off('.w2overlayHide');
+            $(document).off('.w2overlay'+ name);
             if (typeof tmp_hide === 'function') tmp_hide();
         }
-        if (obj.length > 0 && (obj[0].tagName == null || 
obj[0].tagName.toUpperCase() == 'BODY')) options.contextMenu = true;
+        if (obj.length > 0 && (obj[0].tagName == null || 
obj[0].tagName.toUpperCase() === 'BODY')) options.contextMenu = true;
         if (options.contextMenu && options.originalEvent) {
             options.pageX = options.originalEvent.pageX;
             options.pageY = options.originalEvent.pageY;
@@ -2255,12 +2462,13 @@ w2utils.event = {
             .data('position', offset.left + 'x' + offset.top)
             .fadeIn('fast')
             .on('click', function (event) {
+                $('#w2ui-overlay'+ name).data('keepOpen', true);
                 // if there is label for input, it will produce 2 click events
-                if (event.target.tagName.toUpperCase() == 'LABEL') 
event.stopPropagation();
+                if (event.target.tagName.toUpperCase() === 'LABEL') 
event.stopPropagation();
             })
             .on('mousedown', function (event) {
-                $('#w2ui-overlay'+ name).data('keepOpen', true);
-                if (['INPUT', 'TEXTAREA', 
'SELECT'].indexOf(event.target.tagName.toUpperCase()) == -1 && 
!options.selectable) {
+                var tmp = event.target.tagName.toUpperCase();
+                if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(tmp) === -1 && 
!options.selectable) {
                     event.preventDefault();
                 }
             });
@@ -2269,9 +2477,9 @@ w2utils.event = {
 
         // need time to display
         setTimeout(function () {
-            resize();
-            $(document).off('.w2overlayHide').on('click.w2overlayHide', hide);
+            $(document).off('.w2overlay'+ name).on('click.w2overlay'+ name, 
hide);
             if (typeof options.onShow === 'function') options.onShow();
+            resize();
         }, 10);
 
         monitor();
@@ -2295,6 +2503,8 @@ w2utils.event = {
         function hide(event) {
             if (event && event.button !== 0) return; // only for left click 
button
             var div1 = $('#w2ui-overlay'+ name);
+            // Allow clicking inside other overlays which belong to the 
elements inside this overlay
+            if (event && 
$($(event.target).closest('.w2ui-overlay').data('element')).closest('.w2ui-overlay')[0]
 === div1[0]) return;
             if (div1.data('keepOpen') === true) {
                 div1.removeData('keepOpen');
                 return;
@@ -2303,11 +2513,11 @@ w2utils.event = {
             if (typeof options.onHide === 'function') result = 
options.onHide();
             if (result === false) return;
             div1.remove();
-            $(document).off('click', hide);
+            $(document).off('.w2overlay'+ name);
             clearInterval(div1.data('timer'));
         }
 
-        function resize () {
+        function resize() {
             var div1 = $('#w2ui-overlay'+ name);
             var div2 = div1.find(' > div');
             var menu = $('#w2ui-overlay'+ name +' div.menu');
@@ -2334,10 +2544,10 @@ w2utils.event = {
                     }, 1);
                     setTimeout(function () {
                         var $div = div2.find('div.menu');
-                        if ($div.css('overflow-y') != 'auto') 
$div.css('overflow-y', 'auto');
+                        if ($div.css('overflow-y') !== 'auto') 
$div.css('overflow-y', 'auto');
                     }, 10);
                 }
-                if (options.tmp.contentWidth && options.align != 'both') {
+                if (options.tmp.contentWidth && options.align !== 'both') {
                     w = parseInt(options.tmp.contentWidth);
                     div2.width(w);
                     setTimeout(function () {
@@ -2370,7 +2580,7 @@ w2utils.event = {
                 }
                 if (w === 30 && !boxWidth) boxWidth = 30; else boxWidth = 
(options.width ? options.width : 'auto');
                 var tmp = (w - 17) / 2;
-                if (boxWidth != 'auto') tmp = (boxWidth - 17) / 2;
+                if (boxWidth !== 'auto') tmp = (boxWidth - 17) / 2;
                 if (tmp < 25) {
                     boxLeft = 25 - tmp;
                     tipLeft = Math.floor(tmp);
@@ -2453,7 +2663,7 @@ w2utils.event = {
                     );
                 }
                 // check scroll bar (needed to avoid horizontal scrollbar)
-                if (overflowY && options.align != 'both') div2.width(w + 
w2utils.scrollBarSize() + 2);
+                if (overflowY && options.align !== 'both') div2.width(w + 
w2utils.scrollBarSize() + 2);
             }
             menu.css('overflow-y', 'auto');
         }
@@ -2477,7 +2687,7 @@ w2utils.event = {
             }
         */
         // if items is a function
-        if (options && typeof options.items == 'function') {
+        if (options && typeof options.items === 'function') {
             options.items = options.items();
         }
         var defaults = {
@@ -2529,44 +2739,52 @@ w2utils.event = {
             // since only one overlay can exist at a time
             $.fn.w2menuClick = function (event, index) {
                 var keepOpen = false;
-                if (['radio', 'check'].indexOf(options.type) != -1) {
+                if (['radio', 'check'].indexOf(options.type) !== -1) {
                     if (event.shiftKey || event.metaKey || event.ctrlKey) 
keepOpen = true;
                 }
                 if (typeof options.onSelect === 'function') {
-                    // need time so that menu first hides
-                    setTimeout(function () {
-                        options.onSelect({
-                            index: index,
-                            item: options.items[index],
-                            keepOpen: keepOpen,
-                            originalEvent: event
-                        });
-                    }, 10);
+                    options.onSelect({
+                        index   : index,
+                        item    : options.items[index],
+                        keepOpen: keepOpen,
+                        originalEvent: event
+                    });
                 }
                 // do not uncomment (or enum search type is not working in 
grid)
                 // setTimeout(function () { $(document).click(); }, 50);
                 // -- hide
                 var div = $('#w2ui-overlay'+ name);
                 div.removeData('keepOpen');
-                if (typeof div[0].hide === 'function' && !keepOpen) {
+                if (div.length > 0 && typeof div[0].hide === 'function' && 
!keepOpen) {
                     div[0].hide();
                 }
             };
             $.fn.w2menuDown = function (event, index) {
                 var $el  = $(event.target).parents('tr');
                 var tmp  = $el.find('.w2ui-icon');
-                if ((options.type == 'check') || (options.type == 'radio')) {
+                if (options.type === 'check' || options.type === 'radio') {
                    var item = options.items[index];
                    item.checked = !item.checked;
                    if (item.checked) {
-                       if (options.type == 'radio') {
+                        if (options.type === 'radio') {
                            tmp.parents('table').find('.w2ui-icon')
                                .removeClass('w2ui-icon-check')
                                .addClass('w2ui-icon-empty');
-                       }
-                       
tmp.removeClass('w2ui-icon-empty').addClass('w2ui-icon-check');
-                   } else if (options.type == 'check') {
-                       
tmp.removeClass('w2ui-icon-check').addClass('w2ui-icon-empty');
+                        }
+                        // groups of checkboxes
+                        if (options.type === 'check' && item.group != null) {
+                            options.items.forEach(function (sub, ind) {
+                                if (sub.group === item.group && sub.checked) {
+                                    tmp.parents('table').find('tr[index='+ ind 
+'] .w2ui-icon')
+                                       .removeClass('w2ui-icon-check')
+                                       .addClass('w2ui-icon-empty');
+                                    sub.checked = false;
+                                }
+                            });
+                        }
+                        
tmp.removeClass('w2ui-icon-empty').addClass('w2ui-icon-check');
+                   } else if (options.type === 'check' && item.group == null) {
+                        
tmp.removeClass('w2ui-icon-check').addClass('w2ui-icon-empty');
                    }
                 }
                 // highlight record
@@ -2596,7 +2814,7 @@ w2utils.event = {
                         if (event.keyCode === 9) { event.stopPropagation(); 
event.preventDefault(); }
                     });
                 if (options.search) {
-                    if (['text', 'password'].indexOf($(obj)[0].type) != -1 || 
$(obj)[0].tagName.toUpperCase() == 'TEXTAREA') return;
+                    if (['text', 'password'].indexOf($(obj)[0].type) !== -1 || 
$(obj)[0].tagName.toUpperCase() === 'TEXTAREA') return;
                     $('#w2ui-overlay'+ name +' #menu-search').focus();
                 }
                 mresize();
@@ -2614,7 +2832,7 @@ w2utils.event = {
                 var scrTop = $('#w2ui-overlay'+ name +' div.menu').scrollTop();
                 cur.addClass('w2ui-selected');
                 if (options.tmp) options.tmp.contentHeight = 
$('#w2ui-overlay'+ name +' table').height() + (options.search ? 50 : 10);
-                if (options.tmp) options.tmp.contentWidth  = 
$('#w2ui-overlay'+ name +' table').width();
+                if (options.tmp) options.tmp.contentWidth  = 
$('#w2ui-overlay'+ name +' table').width() + 1;
                 if ($('#w2ui-overlay'+ name).length > 0) $('#w2ui-overlay'+ 
name)[0].resize();
                 // scroll into view
                 if (cur.length > 0) {
@@ -2722,7 +2940,7 @@ w2utils.event = {
                     if (img)  imgd = '<td class="menu-icon"><div 
class="w2ui-tb-image w2ui-icon '+ img +'"></div></td>';
                     if (icon) imgd = '<td class="menu-icon" 
align="center"><span class="w2ui-icon '+ icon +'"></span></td>';
                     // render only if non-empty
-                    if (txt != null && txt !== '' && !(/^-+$/.test(txt))) {
+                    if (mitem.type !== 'break' && txt != null && txt !== '' && 
!(/^-+$/.test(txt))) {
                         var bg = (count % 2 === 0 ? 'w2ui-item-even' : 
'w2ui-item-odd');
                         if (options.altRows !== true) bg = '';
                         var colspan = 1;
@@ -2761,62 +2979,85 @@ w2utils.event = {
     };
 
     $.fn.w2color = function (options, callBack) {
-        var obj   = this;
-        var el    = $(this)[0];
+        var obj = this;
+        var $el = $(this);
+        var el  = $el[0];
+        // no need to init
+        if ($el.data('skipInit')) {
+            $el.removeData('skipInit');
+            return;
+        }
+        // needed for keyboard navigation
         var index = [-1, -1];
         if ($.fn.w2colorPalette == null) {
             $.fn.w2colorPalette = [
-                ['000000', '555555', '888888', 'BBBBBB', 'DDDDDD', 'EEEEEE', 
'F7F7F7', 'FFFFFF'],
-                ['FF011B', 'FF9838', 'FFFD59', '01FD55', '00FFFE', '006CE7', 
'9B24F4', 'FF21F5'],
-                ['FFEAEA', 'FCEFE1', 'FCF5E1', 'EBF7E7', 'E9F3F5', 'ECF4FC', 
'EAE6F4', 'F5E7ED'],
-                ['F4CCCC', 'FCE5CD', 'FFF2CC', 'D9EAD3', 'D0E0E3', 'CFE2F3', 
'D9D1E9', 'EAD1DC'],
-                ['EA9899', 'F9CB9C', 'FEE599', 'B6D7A8', 'A2C4C9', '9FC5E8', 
'B4A7D6', 'D5A6BD'],
-                ['E06666', 'F6B26B', 'FED966', '93C47D', '76A5AF', '6FA8DC', 
'8E7CC3', 'C27BA0'],
-                ['CC0814', 'E69138', 'F1C232', '6AA84F', '45818E', '3D85C6', 
'674EA7', 'A54D79'],
-                ['99050C', 'B45F17', 'BF901F', '37761D', '124F5C', '0A5394', 
'351C75', '741B47'],
-                // ['660205', '783F0B', '7F6011', '274E12', '0C343D', 
'063762', '20124D', '4C1030'],
-                ['F2F2F2', 'F2F2F2', 'F2F2F2', 'F2F2F2', 'F2F2F2'] // custom 
colors (up to 4)
+                ['000000', '333333', '555555', '777777', '888888', '999999', 
'AAAAAA', 'CCCCCC', 'DDDDDD', 'EEEEEE', 'F7F7F7', 'FFFFFF'],
+                ['FF011B', 'FF9838', 'FFC300',  'FFFD59', '86FF14', '14FF7A', 
'2EFFFC', '2693FF', '006CE7', '9B24F4', 'FF21F5', 'FF0099'],
+                ['FFEAEA', 'FCEFE1', 'FCF4DC',  'FFFECF', 'EBFFD9', 'D9FFE9', 
'E0FFFF', 'E8F4FF', 'ECF4FC', 'EAE6F4', 'FFF5FE', 'FCF0F7'],
+                ['F4CCCC', 'FCE5CD', 'FFF1C2',  'FFFDA1', 'D5FCB1', 'B5F7D0', 
'BFFFFF', 'D6ECFF', 'CFE2F3', 'D9D1E9', 'FFE3FD', 'FFD9F0'],
+                ['EA9899', 'F9CB9C', 'FFE48C',  'F7F56F', 'B9F77E', '84F0B1', 
'83F7F7', 'B5DAFF', '9FC5E8', 'B4A7D6', 'FAB9F6', 'FFADDE'],
+                ['E06666', 'F6B26B', 'DEB737',  'E0DE51', '8FDB48', '52D189', 
'4EDEDB', '76ACE3', '6FA8DC', '8E7CC3', 'E07EDA', 'F26DBD'],
+                ['CC0814', 'E69138', 'AB8816',  'B5B20E', '6BAB30', '27A85F', 
'1BA8A6', '3C81C7', '3D85C6', '674EA7', 'A14F9D', 'BF4990'],
+                ['99050C', 'B45F17', '80650E',  '737103', '395E14', '10783D', 
'13615E', '094785', '0A5394', '351C75', '780172', '782C5A']
             ];
         }
         var pal = $.fn.w2colorPalette;
-        if (typeof options == 'string') options = {
+        if (typeof options === 'string') options = {
             color: options,
             transparent: true
         };
+        if (options.onSelect == null && callBack != null) options.onSelect = 
callBack;
         // add remove transarent color
-        if (options.transparent && pal[0][1] == '555555') {
+        if (options.transparent && pal[0][1] == '333333') {
             pal[0].splice(1, 1);
             pal[0].push('');
         }
-        if (!options.transparent && pal[0][1] != '555555') {
-            pal[0].splice(1, 0, '555555');
+        if (!options.transparent && pal[0][1] != '333333') {
+            pal[0].splice(1, 0, '333333');
             pal[0].pop();
         }
         if (options.color) options.color = String(options.color).toUpperCase();
+        if (typeof options.color === 'string' && options.color.substr(0,1) === 
'#') options.color = options.color.substr(1);
+        if (options.fireChange == null) options.fireChange = true;
 
         if ($('#w2ui-overlay').length === 0) {
-            $(el).w2overlay(getColorHTML(options), {
-                onHide: function () {
-                    if (typeof callBack == 'function') 
callBack($(el).data('_color'));
-                    $(el).removeData('_color');
-                }
-            });
+            $(el).w2overlay(getColorHTML(options), options);
         } else { // only refresh contents
             $('#w2ui-overlay 
.w2ui-color').parent().html(getColorHTML(options));
+            $('#w2ui-overlay').show();
         }
         // bind events
         $('#w2ui-overlay .color')
             .off('.w2color')
             .on('mousedown.w2color', function (event) {
-                var color = $(event.originalEvent.target).attr('name');
+                var color = $(event.originalEvent.target).attr('name'); // 
should not have #
                 index = $(event.originalEvent.target).attr('index').split(':');
-                $(el).data('_color', color);
+                if (el.tagName.toUpperCase() === 'INPUT') {
+                    if (options.fireChange) $(el).change();
+                    $(el).next().find('>div').css('background-color', color);
+                } else {
+                    $(el).data('_color', color);
+                }
+                if (typeof options.onSelect === 'function') 
options.onSelect(color);
             })
             .on('mouseup.w2color', function () {
                 setTimeout(function () {
                     if ($("#w2ui-overlay").length > 0) 
$('#w2ui-overlay').removeData('keepOpen')[0].hide();
                 }, 10);
             });
+        $('#w2ui-overlay .color-original')
+            .off('.w2color')
+            .on('click.w2color', function (event) {
+                // restore original color
+                var tmp = 
w2utils.parseColor($(event.target).css('background-color'));
+                if (tmp != null) {
+                    rgb = tmp;
+                    hsv = w2utils.rgb2hsv(rgb);
+                    setColor(hsv);
+                    updateSlides();
+                    refreshPalette();
+                }
+            });
         $('#w2ui-overlay input')
             .off('.w2color')
             .on('mousedown.w2color', function (event) {
@@ -2824,22 +3065,177 @@ w2utils.event = {
                 setTimeout(function () { $('#w2ui-overlay').data('keepOpen', 
true); }, 10);
                 event.stopPropagation();
             })
-            .on('keyup.w2color', function (event) {
-                if (this.value !== '' && this.value[0] !== '#') this.value = 
'#' + this.value;
-            })
-            .on('change.w2color', function (event) {
-                var tmp = this.value;
-                if (tmp.substr(0, 1) == '#') tmp = tmp.substr(1);
-                if (tmp.length != 6) {
-                    $(this).w2tag('Invalid color.');
-                    return;
+            .on('change.w2color', function () {
+                var $el = $(this);
+                var val = parseFloat($el.val());
+                var max = parseFloat($el.attr('max'));
+                if (isNaN(val)) val = 0;
+                if (max > 1) val = parseInt(val);
+                if (max > 0 && val > max) {
+                    $el.val(max);
+                    val = max;
+                }
+                if (val < 0) {
+                    $el.val(0);
+                    val = 0;
+                }
+                var name  = $el.attr('name');
+                var color = {};
+                if (['r', 'g', 'b', 'a'].indexOf(name) !== -1) {
+                    rgb[name] = val;
+                    hsv = w2utils.rgb2hsv(rgb);
+                } else if (['h', 's', 'v'].indexOf(name) !== -1) {
+                    color[name] = val;
+                }
+                setColor(color);
+                updateSlides();
+                refreshPalette();
+            });
+        // advanced color events
+        var initial;
+        var hsv, rgb = w2utils.parseColor(options.color);
+        if (rgb == null) {
+            rgb = { r: 140, g: 150, b: 160, a: 1 };
+            hsv = w2utils.rgb2hsv(rgb);
+        }
+        hsv = w2utils.rgb2hsv(rgb);
+
+        var setColor = function (color, silent) {
+            if (color.h != null) hsv.h = color.h;
+            if (color.s != null) hsv.s = color.s;
+            if (color.v != null) hsv.v = color.v;
+            if (color.a != null) { rgb.a = color.a; hsv.a = color.a; }
+            rgb = w2utils.hsv2rgb(hsv);
+            var newColor = 'rgba('+ rgb.r +','+ rgb.g +','+ rgb.b +','+ rgb.a 
+')';
+            var cl = [
+                Number(rgb.r).toString(16).toUpperCase(),
+                Number(rgb.g).toString(16).toUpperCase(),
+                Number(rgb.b).toString(16).toUpperCase()
+            ];
+            cl.forEach(function (item, ind) { if (item.length === 1) cl[ind] = 
'0' + item; });
+            if (rgb.a === 1) {
+                newColor = cl[0] + cl[1] + cl[2];
+            }
+            $('#w2ui-overlay .color-preview').css('background-color', 
newColor);
+            $('#w2ui-overlay input').each(function (index, el) {
+                if (el.name) {
+                    if (rgb[el.name] != null) el.value = rgb[el.name];
+                    if (hsv[el.name] != null) el.value = hsv[el.name];
+                    if (el.name === 'a') el.value = rgb.a;
                 }
-                $.fn.w2colorPalette[pal.length - 1].unshift(tmp.toUpperCase());
-                $(el).w2color(options, callBack);
-                setTimeout(function() { $('#w2ui-overlay input')[0].focus(); 
}, 100);
-            })
-            .w2field('hex');
+            });
+            if (!silent) {
+                if (el.tagName.toUpperCase() === 'INPUT') {
+                    $(el).val(newColor).data('skipInit', true);
+                    if (options.fireChange) $(el).change();
+                    $(el).next().find('>div').css('background-color', 
newColor);
+                } else {
+                    $(el).data('_color', newColor);
+                }
+                if (typeof options.onSelect === 'function') 
options.onSelect(newColor);
+            } else {
+                $('#w2ui-overlay .color-original').css('background-color', 
newColor);
+            }
+        }
+        var updateSlides = function () {
+            var $el1 = $('#w2ui-overlay .palette .value1');
+            var $el2 = $('#w2ui-overlay .rainbow .value2');
+            var $el3 = $('#w2ui-overlay .alpha .value2');
+            var offset1 = parseInt($el1.width()) / 2;
+            var offset2 = parseInt($el2.width()) / 2;
+            $el1.css({ 'left': hsv.s * 150 / 100 - offset1, 'top': (100 - 
hsv.v) * 125 / 100 - offset1});
+            $el2.css('left', hsv.h/(360/150) - offset2);
+            $el3.css('left', rgb.a*150 - offset2);
+        }
+        var refreshPalette = function() {
+            var cl  = w2utils.hsv2rgb(hsv.h, 100, 100);
+            var rgb = cl.r + ',' + cl.g + ',' + cl.b;
+            $('#w2ui-overlay .palette').css('background-image',
+                'linear-gradient(90deg, rgba('+ rgb +',0) 0%, rgba(' + rgb + 
',1) 100%)');
+        }
+        var mouseDown = function (event) {
+            var $el = $(this).find('.value1, .value2');
+            var offset = parseInt($el.width()) / 2;
+            if ($el.hasClass('move-x')) $el.css({ left: (event.offsetX - 
offset) + 'px' });
+            if ($el.hasClass('move-y')) $el.css({ top: (event.offsetY - 
offset) + 'px' });
+            initial = {
+                $el    : $el,
+                x      : event.pageX,
+                y      : event.pageY,
+                width  : $el.parent().width(),
+                height : $el.parent().height(),
+                left   : parseInt($el.css('left')),
+                top    : parseInt($el.css('top'))
+            };
+            mouseMove(event);
+            $('body').off('.w2color')
+                .on(mMove, mouseMove)
+                .on(mUp, mouseUp);
+        };
+        var mouseUp = function(event) {
+            $('body').off('.w2color');
+        };
+        var mouseMove = function(event) {
+            var $el    = initial.$el;
+            var divX   = event.pageX - initial.x;
+            var divY   = event.pageY - initial.y;
+            var newX   = initial.left + divX;
+            var newY   = initial.top + divY;
+            var offset = parseInt($el.width()) / 2;
+            if (newX < -offset) newX = -offset;
+            if (newY < -offset) newY = -offset;
+            if (newX > initial.width - offset)  newX = initial.width - offset;
+            if (newY > initial.height - offset) newY = initial.height - offset
+            if ($el.hasClass('move-x')) $el.css({ left : newX + 'px' });
+            if ($el.hasClass('move-y')) $el.css({ top : newY + 'px' });
+
+            // move
+            var name = $el.parent().attr('name');
+            var x = parseInt($el.css('left')) + offset;
+            var y = parseInt($el.css('top')) + offset;
+            if (name === 'palette') {
+                setColor({
+                    s: Math.round(x / initial.width * 100),
+                    v: Math.round(100 - (y / initial.height * 100))
+                });
+            }
+            if (name === 'rainbow') {
+                var h = Math.round(360 / 150 * x);
+                setColor({ h: h });
+                refreshPalette();
+            }
+            if (name === 'alpha') {
+                setColor({ a: parseFloat(Number(x / 150).toFixed(2)) });
+            }
+        }
+        if ($.fn._colorAdvanced === true || options.advanced === true) {
+            $('#w2ui-overlay .w2ui-color-tabs :nth-child(2)').click();
+            $('#w2ui-overlay').removeData('keepOpen');
+        }
+        setColor({}, true);
+        refreshPalette();
+        updateSlides();
+
+        // Events of iOS
+        var mDown = 'mousedown.w2color';
+        var mUp   = 'mouseup.w2color';
+        var mMove = 'mousemove.w2color';
+        if (w2utils.isIOS) {
+            mDown = 'touchstart.w2color';
+            mUp   = 'touchend.w2color';
+            mMove = 'touchmove.w2color  ';
+        }
+        $('#w2ui-overlay .palette')
+            .off('.w2color')
+            .on('mousedown.w2color', mouseDown);
+        $('#w2ui-overlay .rainbow')
+            .off('.w2color')
+            .on('mousedown.w2color', mouseDown);
+        $('#w2ui-overlay .alpha')
+            .off('.w2color')
+            .on('mousedown.w2color', mouseDown);
 
+        // keyboard navigation
         el.nav = function (direction) {
             switch (direction) {
                 case 'up':
@@ -2866,14 +3262,16 @@ w2utils.event = {
         };
 
         function getColorHTML(options) {
-            var color = options.color;
-            var html  = '<div class="w2ui-color" 
onmousedown="event.stopPropagation(); event.preventDefault()">'+ // prevent 
default is needed otherwiser selection gets unselected
+            var color = options.color, bor;
+            var html  = '<div class="w2ui-color" 
onmousedown="jQuery(this).parents(\'.w2ui-overlay\').data(\'keepOpen\', 
true)">'+
+                        '<div class="w2ui-color-palette">'+
                         '<table cellspacing="5"><tbody>';
-            for (var i = 0; i < pal.length - 1; i++) {
+            for (var i = 0; i < pal.length; i++) {
                 html += '<tr>';
                 for (var j = 0; j < pal[i].length; j++) {
+                    if (pal[i][j] === 'FFFFFF') bor = ';border: 1px solid 
#efefef'; else bor = '';
                     html += '<td>'+
-                            '    <div class="color '+ (pal[i][j] === '' ? 
'no-color' : '') +'" style="background-color: #'+ pal[i][j] +';" ' +
+                            '    <div class="color '+ (pal[i][j] === '' ? 
'no-color' : '') +'" style="background-color: #'+ pal[i][j] + bor +';" ' +
                             '       name="'+ pal[i][j] +'" index="'+ i + ':' + 
j +'">'+ (options.color == pal[i][j] ? '&#149;' : '&#160;') +
                             '    </div>'+
                             '</td>';
@@ -2882,23 +3280,73 @@ w2utils.event = {
                 html += '</tr>';
                 if (i < 2) html += '<tr><td style="height: 8px" 
colspan="8"></td></tr>';
             }
-            var tmp = pal[pal.length - 1];
-            html += '<tr><td style="height: 8px" colspan="8"></td></tr>'+
-                    '<tr>'+
-                    '   <td colspan="4" style="text-align: left"><input 
placeholder="#FFF000" style="margin-left: 1px; width: 74px" 
maxlength="7"/></td>'+
-                    '   <td><div class="color" style="background-color: #'+ 
tmp[0] +';" name="'+ tmp[0] +'" index="8:0">'+ (options.color == tmp[0] ? 
'&#149;' : '&#160;') +'</div></td>'+
-                    '   <td><div class="color" style="background-color: #'+ 
tmp[1] +';" name="'+ tmp[1] +'" index="8:0">'+ (options.color == tmp[1] ? 
'&#149;' : '&#160;') +'</div></td>'+
-                    '   <td><div class="color" style="background-color: #'+ 
tmp[2] +';" name="'+ tmp[2] +'" index="8:0">'+ (options.color == tmp[2] ? 
'&#149;' : '&#160;') +'</div></td>'+
-                    '   <td><div class="color" style="background-color: #'+ 
tmp[3] +';" name="'+ tmp[3] +'" index="8:0">'+ (options.color == tmp[3] ? 
'&#149;' : '&#160;') +'</div></td>'+
-                    '</tr>'+
-                    '<tr><td style="height: 4px" colspan="8"></td></tr>';
-            html += '</tbody></table></div>';
+            html += '</tbody></table>'+
+                    '</div>';
+            if (true) {
+                html += '<div class="w2ui-color-advanced" style="display: 
none">'+
+                        '   <div class="color-info">'+
+                        '       <div class="color-preview-bg"><div 
class="color-preview"></div><div class="color-original"></div></div>'+
+                        '       <div class="color-part">'+
+                        '           <span>H</span> <input name="h" 
maxlength="3" max="360" tabindex="101">'+
+                        '           <span>R</span> <input name="r" 
maxlength="3" max="255" tabindex="104">'+
+                        '       </div>'+
+                        '       <div class="color-part">'+
+                        '           <span>S</span> <input name="s" 
maxlength="3" max="100" tabindex="102">'+
+                        '           <span>G</span> <input name="g" 
maxlength="3" max="255" tabindex="105">'+
+                        '       </div>'+
+                        '       <div class="color-part">'+
+                        '           <span>V</span> <input name="v" 
maxlength="3" max="100" tabindex="103">'+
+                        '           <span>B</span> <input name="b" 
maxlength="3" max="255" tabindex="106">'+
+                        '       </div>'+
+                        '       <div class="color-part" style="margin: 30px 
0px 0px 2px">'+
+                        '           <span style="width: 40px">Opacity</span> '+
+                        '           <input name="a" maxlength="5" max="1" 
style="width: 32px !important" tabindex="107">'+
+                        '       </div>'+
+                        '   </div>'+
+                        '   <div class="palette" name="palette">'+
+                        '       <div class="palette-bg"></div>'+
+                        '       <div class="value1 move-x move-y"></div>'+
+                        '   </div>'+
+                        '   <div class="rainbow" name="rainbow">'+
+                        '       <div class="value2 move-x"></div>'+
+                        '   </div>'+
+                        '   <div class="alpha" name="alpha">'+
+                        '       <div class="alpha-bg"></div>'+
+                        '       <div class="value2 move-x"></div>'+
+                        '   </div>'+
+                        '</div>';
+            }
+            html += '<div class="w2ui-color-tabs">'+
+                    '   <div class="w2ui-color-tab selected" 
onclick="jQuery(this).addClass(\'selected\').next().removeClass(\'selected\').parents(\'.w2ui-overlay\').find(\'.w2ui-color-advanced\').hide().parent().find(\'.w2ui-color-palette\').show();
 jQuery.fn._colorAdvanced = false; jQuery(\'#w2ui-overlay\')[0].resize()"><span 
class="w2ui-icon w2ui-icon-colors"></span></div>'+
+                    '   <div class="w2ui-color-tab" 
onclick="jQuery(this).addClass(\'selected\').prev().removeClass(\'selected\').parents(\'.w2ui-overlay\').find(\'.w2ui-color-advanced\').show().parent().find(\'.w2ui-color-palette\').hide();
 jQuery.fn._colorAdvanced = true; jQuery(\'#w2ui-overlay\')[0].resize()"><span 
class="w2ui-icon w2ui-icon-settings"></span></div>'+
+                    '</div>'+
+                    '</div>'+
+                    '<div style="clear: both; height: 0"></div>';
             return html;
         }
     };
 
 })(jQuery);
 
+/***********************************************************
+*  Compatibility with CommonJS and AMD modules
+*
+*********************************************************/
+
+(function(global, w2ui) {
+    if (typeof define=='function' && define.amd) {
+        return define(function(){ return w2ui; });
+    }
+    if (typeof exports!='undefined') {
+        if (typeof module!='undefined' && module.exports)
+            return exports = module.exports = w2ui;
+        global = exports;
+    }
+    for (var m in w2ui) {
+        global[m] = w2ui[m];
+    }
+})(this, { w2ui: w2ui, w2obj: w2obj, w2utils: w2utils });
+
 /************************************************************************
 *   Library: Web 2.0 UI for jQuery (using prototypical inheritance)
 *   - Following objects defined
@@ -2951,8 +3399,8 @@ w2utils.event = {
 *   - added focus(), blur(), onFocus, onBlur
 *   - search.simple - if false, will not show up in simple search
 *   - search.operator - default operator to use with search field
-*   - search.operators - array of operators for the serach
-*   - search.hidden - could not be clearned by the user
+*   - search.operators - array of operators for the search
+*   - search.hidden - could not be cleared by the user
 *   - search.value - only for hidden searches
 *   - if .search(val) - search all fields
 *   - refactor reorderRow (not finished)
@@ -2991,6 +3439,7 @@ w2utils.event = {
         w2ui: {
             colspan: { field: 5, ...}
             editable: true/false
+            hideCheckBox: true/false,
             changes: {
                 field: chagned_value,
                 ....
@@ -3007,6 +3456,7 @@ w2utils.event = {
     }
 *   - added this.show.toolbarInput
 *   - disableCVS
+*   - added useFieldDot: use field name containing dots as separator to look 
into objects
 *   - grid.message
 *   - added noReset option to localSort()
 *   - onColumnSelect
@@ -3027,6 +3477,9 @@ w2utils.event = {
 *   - columnTooltip
 *   - expendable grids are still working
 *   - added search.type = 'color'
+*   - added getFirst
+*   - added stateSaveColumnProperties
+*   - added stateSaveColumnFallbackValues
 *
 ************************************************************************/
 
@@ -3049,7 +3502,7 @@ w2utils.event = {
         this.postData     = {};
         this.httpHeaders  = {};
         this.toolbar      = {};       // if not empty object; then it is 
toolbar object
-        this.stateId      = null;     // Custom state name for satateSave, 
stateRestore and stateReset
+        this.stateId      = null;     // Custom state name for stateSave, 
stateRestore and stateReset
 
         this.show = {
             header          : false,
@@ -3096,10 +3549,12 @@ w2utils.event = {
         this.multiSort       = true;
         this.reorderColumns  = false;
         this.reorderRows     = false;
+        this.searchContextRows = 0;
         this.markSearch      = true;
         this.columnTooltip   = 'normal'; // can be normal, top, bottom, left, 
right
         this.disableCVS      = false;    // disable Column Virtual Scroll
         this.textSearch      = 'begins'; // default search type for text
+        this.useFieldDot     = true;     // use field name containing dots as 
separator to look into object
 
         this.total   = 0;     // server total
         this.limit   = 100;
@@ -3108,6 +3563,7 @@ w2utils.event = {
         this.ranges  = [];
         this.menu    = [];
         this.method  = null;  // if defined, then overwrited ajax method
+        this.dataType = null;   // if defined, then overwrited 
w2utils.settings.dataType
         this.recid   = null;
         this.parser  = null;
 
@@ -3139,6 +3595,58 @@ w2utils.event = {
             isSafari    : 
(/^((?!chrome|android).)*safari/i).test(navigator.userAgent)
         };
 
+        // these column properties will be saved in stateSave()
+        this.stateSaveColumnProperties = {
+            caption         : false,
+            field           : true,
+            size            : true,
+            min             : false,
+            max             : false,
+            gridMinWidth    : false,
+            sizeCorrected   : false,
+            sizeCalculated  : true,
+            sizeOriginal    : true,
+            sizeType        : true,
+            hidden          : true,
+            sortable        : false,
+            searchable      : false,
+            resizable       : false,
+            hideable        : false,
+            attr            : false,
+            style           : false,
+            render          : false,
+            title           : false,
+            editable        : false,
+            frozen          : true,
+            info            : false,
+        };
+
+        // these are the stateSave() fallback values if the property to save 
is not a property of the column object
+        this.stateSaveColumnFallbackValues = {
+            caption         : '',     // column caption
+            field           : '',     // field name to map column to a record
+            size            : null,   // size of column in px or %
+            min             : 20,     // minimum width of column in px
+            max             : null,   // maximum width of column in px
+            gridMinWidth    : null,   // minimum width of the grid when column 
is visible
+            sizeCorrected   : null,   // read only, corrected size (see 
explanation below)
+            sizeCalculated  : null,   // read only, size in px (see 
explanation below)
+            sizeOriginal    : null,
+            sizeType        : null,
+            hidden          : false,  // indicates if column is hidden
+            sortable        : false,  // indicates if column is sortable
+            searchable      : false,  // indicates if column is searchable, 
bool/string: int,float,date,...
+            resizable       : true,   // indicates if column is resizable
+            hideable        : true,   // indicates if column can be hidden
+            attr            : '',     // string that will be inside the <td 
... attr> tag
+            style           : '',     // additional style for the td tag
+            render          : null,   // string or render function
+            title           : null,   // string or function for the title 
property for the column cells
+            editable        : {},     // editable object if column fields are 
editable
+            frozen          : false,  // indicates if the column is fixed to 
the left
+            info            : null    // info bubble, can be bool/object
+        };
+
         $.extend(true, this, w2obj.grid, options);
     };
 
@@ -3162,7 +3670,6 @@ w2utils.event = {
             // extend items
             var object = new w2grid(method);
             $.extend(object, { postData: {}, httpHeaders: {}, records: [], 
columns: [], searches: [], toolbar: {}, sortData: [], searchData: [], handlers: 
[] });
-            if (object.onExpand != null) object.show.expandColumn = true;
             $.extend(true, object.toolbar, toolbar);
             // reassign variables
             var p;
@@ -3250,7 +3757,7 @@ w2utils.event = {
             "hex"     : ['is', 'between'],
             "color"   : ['is', 'begins', 'contains', 'ends'],
             "enum"    : ['in', 'not in']
-            // -- all posible
+            // -- all possible
             // "text"    : ['is', 'begins', 'contains', 'ends'],
             // "number"  : ['is', 'between', 'less:less than', 'more:more 
than', 'null:is null', 'not null:is not null'],
             // "list"    : ['is', 'null:is null', 'not null:is not null'],
@@ -3296,6 +3803,7 @@ w2utils.event = {
         onColumnClick      : null,
         onColumnDblClick   : null,
         onColumnResize     : null,
+        onColumnAutoResize : null,
         onSort             : null,
         onSearch           : null,
         onSearchOpen       : null,
@@ -3377,7 +3885,7 @@ w2utils.event = {
         },
 
         set: function (recid, record, noRefresh) { // does not delete 
existing, but overrides on top of it
-            if (typeof recid == 'object') {
+            if ((typeof recid == 'object') && (recid !== null)) {
                 noRefresh = record;
                 record    = recid;
                 recid     = null;
@@ -3406,34 +3914,42 @@ w2utils.event = {
             // search records
             if ($.isArray(recid)) {
                 var recs = [];
-                for (var i = 0; i < this.records.length; i++) {
-                    if ($.inArray(this.records[i].recid, recid) != -1) {
-                        if (returnIndex === true) {
-                            recs.push(i);
-                        } else {
-                            recs.push(this.records[i]);
-                        }
-                    }
-                }
-                for (var i = 0; i < this.summary.length; i++) {
-                    if ($.inArray(this.summary[i].recid, recid) != -1) {
-                        if (returnIndex === true) {
-                            recs.push(i);
-                        } else {
-                            recs.push(this.summary[i]);
-                        }
-                    }
+                for (var i = 0; i < recid.length; i++) {
+                    var v = this.get(recid[i], returnIndex);
+                    if (v !== null)
+                        recs.push(v);
                 }
                 return recs;
             } else {
+                // get() must be fast, implements a cache to bypass loop over 
all records
+                // most of the time.
+                var idCache = this.last.idCache;
+                if (!idCache) {
+                    this.last.idCache = idCache = {};
+                }
+                var i = idCache[recid];
+                if (typeof(i) === "number") {
+                    if (i >= 0 && i < this.records.length && 
this.records[i].recid == recid) {
+                        if (returnIndex === true) return i; else return 
this.records[i];
+                    }
+                    // summary indexes are stored as negative numbers, try 
them now.
+                    i = ~i;
+                    if (i >= 0 && i < this.summary.length && 
this.summary[i].recid == recid) {
+                        if (returnIndex === true) return i; else return 
this.summary[i];
+                    }
+                    // wrong index returned, clear cache
+                    this.last.idCache = idCache = {};
+                }
                 for (var i = 0; i < this.records.length; i++) {
                     if (this.records[i].recid == recid) {
+                        idCache[recid] = i;
                         if (returnIndex === true) return i; else return 
this.records[i];
                     }
                 }
                 // search summary
                 for (var i = 0; i < this.summary.length; i++) {
                     if (this.summary[i].recid == recid) {
+                        idCache[recid] = ~i;
                         if (returnIndex === true) return i; else return 
this.summary[i];
                     }
                 }
@@ -3441,6 +3957,20 @@ w2utils.event = {
             }
         },
 
+        getFirst: function () {
+            if (this.records.length == 0) return null;
+            var recid = this.records[0].recid;
+            var tmp   = this.last.searchIds;
+            if (this.searchData.length > 0) {
+                if (Array.isArray(tmp) && tmp.length > 0) {
+                    recid = this.records[tmp[0]].recid;
+                } else {
+                    recid = null;
+                }
+            }
+            return recid;
+        },
+
         remove: function () {
             var removed = 0;
             for (var a = 0; a < arguments.length; a++) {
@@ -3547,6 +4077,7 @@ w2utils.event = {
             }
             this.refreshBody();
             this.resizeRecords();
+            this.scroll(); // scroll needed because of column virtual scroll
             return shown;
         },
 
@@ -3563,6 +4094,7 @@ w2utils.event = {
             }
             this.refreshBody();
             this.resizeRecords();
+            this.scroll(); // scroll needed because of column virtual scroll
             return hidden;
         },
 
@@ -3711,8 +4243,9 @@ w2utils.event = {
             function preparePaths() {
                 for (var i = 0; i < obj.records.length; i++) {
                     var rec = obj.records[i];
-                    if (rec.w2ui && rec.w2ui.parent_recid != null)
+                    if (rec.w2ui && rec.w2ui.parent_recid != null) {
                         rec.w2ui._path = getRecordPath(rec);
+                    }
                 }
             }
 
@@ -3720,8 +4253,9 @@ w2utils.event = {
             function cleanupPaths() {
                 for (var i = 0; i < obj.records.length; i++) {
                     var rec = obj.records[i];
-                    if (rec.w2ui && rec.w2ui.parent_recid != null)
+                    if (rec.w2ui && rec.w2ui.parent_recid != null) {
                         rec.w2ui._path = null;
+                    }
                 }
             }
 
@@ -3761,30 +4295,29 @@ w2utils.event = {
                 if (a === b) return 0; // optimize, same object
                 for (var i = 0; i < obj.sortData.length; i++) {
                     var fld = obj.sortData[i].field;
-                    if (obj.sortData[i].field_) fld = obj.sortData[i].field_;
-                    var aa = a[fld];
-                    var bb = b[fld];
+                    var sortFld = (obj.sortData[i].field_) ? 
obj.sortData[i].field_ : fld;
+                    var aa = a[sortFld];
+                    var bb = b[sortFld];
                     if (String(fld).indexOf('.') != -1) {
-                        aa = obj.parseField(a, fld);
-                        bb = obj.parseField(b, fld);
+                        aa = obj.parseField(a, sortFld);
+                        bb = obj.parseField(b, sortFld);
                     }
                     var col = obj.getColumn(fld);
                     if (col && col.editable != null) { // for drop editable 
fields and drop downs
                         if ($.isPlainObject(aa) && aa.text) aa = aa.text;
                         if ($.isPlainObject(bb) && bb.text) bb = bb.text;
                     }
-                    var ret = compareCells(aa, bb, i, 
obj.sortData[i].direction);
+                    var ret = compareCells(aa, bb, i, 
obj.sortData[i].direction, col.sortMode || 'default');
                     if (ret !== 0) return ret;
                 }
                 // break tie for similar records,
                 // required to have consistent ordering for tree paths
                 var ret = compareCells(a.recid, b.recid, -1, 'asc');
-                if (ret !== 0) return ret;
-                return 0;
+                return ret;
             }
 
             // compare two values, aa and bb, producing consistent ordering
-            function compareCells(aa, bb, i, direction) {
+            function compareCells(aa, bb, i, direction, sortMode) {
                 // if both objects are strictly equal, we're done
                 if (aa === bb)
                     return 0;
@@ -3793,7 +4326,7 @@ w2utils.event = {
                     return 1;
                 if ((aa != null && aa !== "") && (bb == null || bb === ""))
                     return -1;
-                var dir = (direction == 'asc') ? 1 : -1;
+                var dir = (direction.toLowerCase() === 'asc') ? 1 : -1;
                 // for different kind of objects, sort by object type
                 if (typeof aa != typeof bb)
                     return (typeof aa > typeof bb) ? dir : -dir;
@@ -3813,11 +4346,22 @@ w2utils.event = {
                     aa = String(aa);
                 if (bb && typeof bb == 'object' && bb.toString != 
defaultToString)
                     bb = String(bb);
-                // do case-insensitive string comparaison
+                // do case-insensitive string comparison
                 if (typeof aa == 'string')
                     aa = $.trim(aa.toLowerCase());
                 if (typeof bb == 'string')
                     bb = $.trim(bb.toLowerCase());
+
+                switch (sortMode) {
+                    case 'natural':
+                        sortMode = w2utils.naturalCompare;
+                        break;
+                }
+
+                if (typeof sortMode == 'function') {
+                    return sortMode(aa,bb) * dir;
+                }
+
                 // compare both objects
                 if (aa > bb)
                     return dir;
@@ -3851,7 +4395,43 @@ w2utils.event = {
                     if (match) {
                         if (rec && rec.w2ui)
                             addParent(rec.w2ui.parent_recid);
-                        this.last.searchIds.push(i);
+
+                        if (this.searchContextRows && this.searchContextRows > 
0)
+                        {
+                            var beforeItemCount = this.searchContextRows;
+                            var afterItemCount = this.searchContextRows;
+
+                            if (i < beforeItemCount)
+                                beforeItemCount = i;
+
+                            if (i + afterItemCount > this.records.length)
+                                afterItemCount = this.records.length - i;
+
+                            if (beforeItemCount > 0)
+                            {
+                                for (var j = i - beforeItemCount; j < i; j++)
+                                {
+                                    if (this.last.searchIds.indexOf(j) < 0)
+                                        this.last.searchIds.push(j);
+                                }
+                            }
+
+                            if (this.last.searchIds.indexOf(i) < 0)
+                                this.last.searchIds.push(i);
+
+                            if (afterItemCount > 0)
+                            {
+                                for (var j = (i + 1) ; j <= (i + 
afterItemCount) ; j++)
+                                {
+                                    if (this.last.searchIds.indexOf(j) < 0)
+                                        this.last.searchIds.push(j);
+                                }
+                            }
+                        }
+                        else
+                        {
+                            this.last.searchIds.push(i);
+                        }
                     }
                 }
                 this.total = this.last.searchIds.length;
@@ -3911,7 +4491,8 @@ w2utils.event = {
                             if (parseFloat(obj.parseField(rec, search.field)) 
>= parseFloat(val2) && parseFloat(obj.parseField(rec, search.field)) <= 
parseFloat(val3)) fl++;
                         }
                         else if (search.type == 'date') {
-                            var val1 = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
+                            var tmp = (obj.parseField(rec, search.field + '_') 
instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, 
search.field));
+                            var val1 = w2utils.isDate(tmp, 
w2utils.settings.dateFormat, true);
                             var val2 = w2utils.isDate(val2, 
w2utils.settings.dateFormat, true);
                             var val3 = w2utils.isDate(val3, 
w2utils.settings.dateFormat, true);
                             if (val3 != null) val3 = new Date(val3.getTime() + 
86400000); // 1 day
@@ -3939,7 +4520,7 @@ w2utils.event = {
                         }
                         else if (search.type == 'date') {
                             var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
+                            var val1 = w2utils.formatDate(w2utils.isDate(tmp, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
                             var val2 = w2utils.formatDate(w2utils.isDate(val2, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
                             if (val1 <= val2) fl++;
                         }
@@ -3962,7 +4543,7 @@ w2utils.event = {
                         }
                         else if (search.type == 'date') {
                             var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
+                            var val1 = w2utils.formatDate(w2utils.isDate(tmp, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
                             var val2 = w2utils.formatDate(w2utils.isDate(val2, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
                             if (val1 >= val2) fl++;
                         }
@@ -3982,14 +4563,12 @@ w2utils.event = {
                     case 'in':
                         var tmp = sdata.value;
                         if (sdata.svalue) tmp = sdata.svalue;
-                        if (tmp.indexOf(w2utils.isFloat(val1) ? 
parseFloat(val1) : val1) !== -1) fl++;
-                        if (tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) !== -1) fl++;
+                        if ((tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) !== -1) || tmp.indexOf(val1) !== -1) fl++;
                         break;
                     case 'not in':
                         var tmp = sdata.value;
                         if (sdata.svalue) tmp = sdata.svalue;
-                        if (tmp.indexOf(w2utils.isFloat(val1) ? 
parseFloat(val1) : val1) == -1) fl++;
-                        if (tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) == -1) fl++;
+                        if (!((tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) !== -1) || tmp.indexOf(val1) !== -1)) fl++;
                         break;
                     case 'begins':
                     case 'begins with': // need for back compatib.
@@ -4289,8 +4868,10 @@ w2utils.event = {
                     originalRange : [{ recid: sel[0].recid, column: 
sel[0].column }, { recid: sel[sel.length-1].recid, column: 
sel[sel.length-1].column }],
                     newRange      : [{ recid: sel[0].recid, column: 
sel[0].column }, { recid: sel[sel.length-1].recid, column: 
sel[sel.length-1].column }]
                 };
-                $(document).off('mousemove', mouseMove).on('mousemove', 
mouseMove);
-                $(document).off('mouseup', mouseStop).on('mouseup', mouseStop);
+                $(document)
+                    .off('.w2ui-' + obj.name)
+                    .on('mousemove.w2ui-' + obj.name, mouseMove)
+                    .on('mouseup.w2ui-' + obj.name, mouseStop);
                 // do not blur grid
                 event.preventDefault();
             }
@@ -4332,8 +4913,7 @@ w2utils.event = {
                 // default behavior
                 obj.removeRange('grid-selection-expand');
                 delete obj.last.move;
-                $(document).off('mousemove', mouseMove);
-                $(document).off('mouseup', mouseStop);
+                $(document).off('.w2ui-' + obj.name);
                 // event after
                 obj.trigger($.extend(edata, { phase: 'after' }));
             }
@@ -4595,6 +5175,7 @@ w2utils.event = {

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to