text: /* w2ui-fields.js 1.5 (nightly), part of w2ui (c) http://w2ui.com, vitmalina@gmail.com */
  var w2ui  = w2ui  || {};
  var w2obj = w2obj || {}; // expose object to be able to overwrite default functions
  
  /************************************************
  *  Library: Web 2.0 UI for jQuery
  *  - Following objects are defines
  *        - w2ui             - object that will contain all widgets
  *        - w2obj            - object with widget prototypes
  *        - w2utils          - basic utilities
  *        - $().w2render     - common render
  *        - $().w2destroy    - common destroy
  *        - $().w2marker     - marker plugin
  *        - $().w2tag        - tag plugin
  *        - $().w2overlay    - overlay plugin
  *        - $().w2menu       - menu plugin
  *        - w2utils.event    - generic event object
  *  - Dependencies: jQuery
  *
  * == NICE TO HAVE ==
  *   - overlay should be displayed where more space (on top or on bottom)
  *   - write and article how to replace certain framework functions
  *   - add maxHeight for the w2menu
  *   - add time zone
  *   - TEST On IOS
  *   - $().w2marker() -- only unmarks first instance
  *   - subitems for w2menus()
  *   - add w2utils.lang wrap for all captions in all buttons.
  *   - $().w2date(), $().w2dateTime()
  *
  * == 1.5 change
  *   - parseColor(str) returns rgb
  *   - rgb2hsv, hsv2rgb
  *   - color.onSelect
  *
  ************************************************/
  
  var w2utils = (function ($) {
      var tmp = {}; // for some temp variables
      var obj = {
          version  : '1.5.RC1',
          settings : {
              "locale"            : "en-us",
              "dateFormat"        : "m/d/yyyy",
              "timeFormat"        : "hh:mi pm",
              "datetimeFormat"    : "m/d/yyyy|hh:mi pm",
              "currencyPrefix"    : "$",
              "currencySuffix"    : "",
              "currencyPrecision" : 2,
              "groupSymbol"       : ",",
              "decimalSymbol"     : ".",
              "shortmonths"       : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
              "fullmonths"        : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
              "shortdays"         : ["M", "T", "W", "T", "F", "S", "S"],
              "fulldays"          : ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
              "weekStarts"        : "M",        // can be "M" for Monday or "S" for Sunday
              "dataType"          : 'HTTPJSON', // can be HTTP, HTTPJSON, RESTFULL, RESTFULLJSON, JSON (case sensitive)
              "phrases"           : {},         // empty object for english phrases
              "dateStartYear"     : 1950,       // start year for date-picker
              "dateEndYear"       : 2020        // end year for date picker
          },
          isBin           : isBin,
          isInt           : isInt,
          isFloat         : isFloat,
          isMoney         : isMoney,
          isHex           : isHex,
          isAlphaNumeric  : isAlphaNumeric,
          isEmail         : isEmail,
          isDate          : isDate,
          isTime          : isTime,
          isDateTime      : isDateTime,
          age             : age,
          interval        : interval,
          date            : date,
          formatSize      : formatSize,
          formatNumber    : formatNumber,
          formatDate      : formatDate,
          formatTime      : formatTime,
          formatDateTime  : formatDateTime,
          stripTags       : stripTags,
          encodeTags      : encodeTags,
          decodeTags      : decodeTags,
          escapeId        : escapeId,
          base64encode    : base64encode,
          base64decode    : base64decode,
          md5             : md5,
          transition      : transition,
          lock            : lock,
          unlock          : unlock,
          message         : message,
          lang            : lang,
          locale          : locale,
          getSize         : getSize,
          getStrWidth     : getStrWidth,
          scrollBarSize   : scrollBarSize,
          checkName       : checkName,
          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)
                   ? true : false),
          isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') != -1 ||
                   navigator.userAgent.toLowerCase().indexOf('trident') != -1 )
                   ? true : false)
      };
      return obj;
  
      function isBin (val) {
          var re = /^[0-1]+$/;
          return re.test(val);
      }
  
      function isInt (val) {
          var re = /^[-+]?[0-9]+$/;
          return re.test(val);
      }
  
      function isFloat (val) {
          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));
      }
  
      function isMoney (val) {
          var se = w2utils.settings;
          var re = new RegExp('^'+ (se.currencyPrefix ? '\\' + se.currencyPrefix + '?' : '') +
                              '[-+]?'+ (se.currencyPrefix ? '\\' + se.currencyPrefix + '?' : '') +
                              '[0-9]*[\\'+ se.decimalSymbol +']?[0-9]+'+ (se.currencySuffix ? '\\' + se.currencySuffix + '?' : '') +'$', 'i');
          if (typeof val === 'string') {
              val = val.replace(new RegExp(se.groupSymbol, 'g'), '');
          }
          if (typeof val === 'object' || val === '') return false;
          return re.test(val);
      }
  
      function isHex (val) {
          var re = /^[a-fA-F0-9]+$/;
          return re.test(val);
      }
  
      function isAlphaNumeric (val) {
          var re = /^[a-zA-Z0-9_-]+$/;
          return re.test(val);
      }
  
      function isEmail (val) {
          var email = /^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
          return email.test(val);
      }
  
      function isDate (val, format, retDate) {
          if (!val) return false;
  
          var dt   = 'Invalid Date';
          var month, day, year;
  
          if (format == null) format = w2utils.settings.dateFormat;
  
          if (typeof val.getUTCFullYear === 'function') { // date object
              year  = val.getUTCFullYear();
              month = val.getUTCMonth() + 1;
              day   = val.getUTCDate();
          } else if (parseInt(val) == val && parseInt(val) > 0) {
              val = new Date(parseInt(val));
              year  = val.getUTCFullYear();
              month = val.getUTCMonth() + 1;
              day   = val.getUTCDate();
          } else {
              val = String(val);
              // convert month formats
              if (new RegExp('mon', 'ig').test(format)) {
                  format = format.replace(/month/ig, 'm').replace(/mon/ig, 'm').replace(/dd/ig, 'd').replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
                  val    = val.replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
                  for (var m = 0, len = w2utils.settings.fullmonths.length; m < len; m++) {
                      var t = w2utils.settings.fullmonths[m];
                      val = val.replace(new RegExp(t, 'ig'), (parseInt(m) + 1)).replace(new RegExp(t.substr(0, 3), 'ig'), (parseInt(m) + 1));
                  }
              }
              // format date
              var tmp  = val.replace(/-/g, '/').replace(/\./g, '/').toLowerCase().split('/');
              var tmp2 = format.replace(/-/g, '/').replace(/\./g, '/').toLowerCase();
              if (tmp2 === 'mm/dd/yyyy') { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
              if (tmp2 === 'm/d/yyyy')   { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
              if (tmp2 === 'dd/mm/yyyy') { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
              if (tmp2 === 'd/m/yyyy')   { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
              if (tmp2 === 'yyyy/dd/mm') { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
              if (tmp2 === 'yyyy/d/m')   { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
              if (tmp2 === 'yyyy/mm/dd') { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
              if (tmp2 === 'yyyy/m/d')   { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
              if (tmp2 === 'mm/dd/yy')   { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
              if (tmp2 === 'm/d/yy')     { month = tmp[0]; day = tmp[1]; year = parseInt(tmp[2]) + 1900; }
              if (tmp2 === 'dd/mm/yy')   { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
              if (tmp2 === 'd/m/yy')     { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
              if (tmp2 === 'yy/dd/mm')   { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
              if (tmp2 === 'yy/d/m')     { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
              if (tmp2 === 'yy/mm/dd')   { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
              if (tmp2 === 'yy/m/d')     { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
          }
          if (!isInt(year)) return false;
          if (!isInt(month)) return false;
          if (!isInt(day)) return false;
          year  = +year;
          month = +month;
          day   = +day;
          dt    = new Date(year, month - 1, day);
          // do checks
          if (month == null) 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;
      }
  
      function isTime (val, retTime) {
          // Both formats 10:20pm and 22:20
          if (val == null) return false;
          var max, am, pm;
          // -- process american format
          val = String(val);
          val = val.toUpperCase();
          am = val.indexOf('AM') >= 0;
          pm = val.indexOf('PM') >= 0;
          var ampm = (pm || am);
          if (ampm) max = 12; else max = 24;
          val = val.replace('AM', '').replace('PM', '');
          val = $.trim(val);
          // ---
          var tmp = val.split(':');
          var h = parseInt(tmp[0] || 0), m = parseInt(tmp[1] || 0), s = parseInt(tmp[2] || 0);
          // accept edge case: 3PM is a good timestamp, but 3 (without AM or PM) is NOT:
          if ((!ampm || tmp.length !== 1) && tmp.length !== 2 && tmp.length !== 3) { return false; }
          if (tmp[0] === '' || h < 0 || h > max || !this.isInt(tmp[0]) || tmp[0].length > 2) { return false; }
          if (tmp.length > 1 && (tmp[1] === '' || m < 0 || m > 59 || !this.isInt(tmp[1]) || tmp[1].length !== 2)) { return false; }
          if (tmp.length > 2 && (tmp[2] === '' || s < 0 || s > 59 || !this.isInt(tmp[2]) || tmp[2].length !== 2)) { return false; }
          // check the edge cases: 12:01AM is ok, as is 12:01PM, but 24:01 is NOT ok while 24:00 is (midnight; equivalent to 00:00).
          // meanwhile, there is 00:00 which is ok, but 0AM nor 0PM are okay, while 0:01AM and 0:00AM are.
          if (!ampm && max === h && (m !== 0 || s !== 0)) { return false; }
          if (ampm && tmp.length === 1 && h === 0) { return false; }
  
          if (retTime === true) {
              if (pm && h !== 12) h += 12;   // 12:00pm - is noon
              if (am && h === 12) h += 12;   // 12:00am - is midnight
              return {
                  hours: h,
                  minutes: m,
                  seconds: s
              };
          }
          return true;
      }
  
      function isDateTime (val, format, retDate) {
          if (format == null) format = w2utils.settings.datetimeFormat;
          var formats = format.split('|');
          if (typeof val.getUTCFullYear === 'function') { // date object
              if (retDate !== true) return true;
              return val;
          } else if (parseInt(val) == val && parseInt(val) > 0) {
              val = new Date(parseInt(val));
              if (retDate !== true) return true;
              return val;
          } else {
              var tmp = String(val).indexOf(' ');
              var values  = [val.substr(0, tmp), val.substr(tmp).trim()];
              formats[0] = formats[0].trim();
              if (formats[1]) formats[1] = formats[1].trim();
              // check
              var tmp1 = w2utils.isDate(values[0], formats[0], true);
              var tmp2 = w2utils.isTime(values[1], true);
              if (tmp1 !== false && tmp2 !== false) {
                  if (retDate !== true) return true;
                  tmp1.setHours(tmp2.hours);
                  tmp1.setMinutes(tmp2.minutes);
                  tmp1.setSeconds(tmp2.seconds);
                  return tmp1;
              } else {
                  return false;
              }
          }
      }
  
      function age(dateStr) {
          var d1;
          if (dateStr === '' || dateStr == null) return '';
          if (typeof dateStr.getUTCFullYear === 'function') { // date object
              d1 = dateStr;
          } else if (parseInt(dateStr) == dateStr && parseInt(dateStr) > 0) {
              d1 = new Date(parseInt(dateStr));
          } else {
              d1 = new Date(dateStr);
          }
          if (String(d1) == 'Invalid Date') return '';
  
          var d2  = new Date();
          var sec = (d2.getTime() - d1.getTime()) / 1000;
          var amount = '';
          var type   = '';
          if (sec < 0) {
              amount = 0;
              type   = 'sec';
          } else if (sec < 60) {
              amount = Math.floor(sec);
              type   = 'sec';
              if (sec < 0) { amount = 0; type = 'sec'; }
          } else if (sec < 60*60) {
              amount = Math.floor(sec/60);
              type   = 'min';
          } else if (sec < 24*60*60) {
              amount = Math.floor(sec/60/60);
              type   = 'hour';
          } else if (sec < 30*24*60*60) {
              amount = Math.floor(sec/24/60/60);
              type   = 'day';
          } else if (sec < 365*24*60*60) {
              amount = Math.floor(sec/30/24/60/60*10)/10;
              type   = 'month';
          } else if (sec < 365*4*24*60*60) {
              amount = Math.floor(sec/365/24/60/60*10)/10;
              type   = 'year';
          } else if (sec >= 365*4*24*60*60) {
              // factor in leap year shift (only older then 4 years)
              amount = Math.floor(sec/365.25/24/60/60*10)/10;
              type   = 'year';
          }
          return amount + ' ' + type + (amount > 1 ? 's' : '');
      }
  
      function interval (value) {
          var ret = '';
          if (value < 1000) {
              ret = "< 1 sec";
          } else if (value < 60000) {
              ret = Math.floor(value / 1000) + " secs";
          } else if (value < 3600000) {
              ret = Math.floor(value / 60000) + " mins";
          } else if (value < 86400000) {
              ret = Math.floor(value / 3600000 * 10) / 10 + " hours";
          } else if (value < 2628000000) {
              ret = Math.floor(value / 86400000 * 10) / 10 + " days";
          } else if (value < 3.1536e+10) {
              ret = Math.floor(value / 2628000000 * 10) / 10 + " months";
          } else {
              ret = Math.floor(value / 3.1536e+9) / 10 + " years";
          }
          return ret;
      }
  
      function date (dateStr) {
          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 '';
  
          var months = w2utils.settings.shortmonths;
          var d2   = new Date(); // today
          var d3   = new Date();
          d3.setTime(d3.getTime() - 86400000); // yesterday
  
          var dd1  = months[d1.getMonth()] + ' ' + d1.getDate() + ', ' + d1.getFullYear();
          var dd2  = months[d2.getMonth()] + ' ' + d2.getDate() + ', ' + d2.getFullYear();
          var dd3  = months[d3.getMonth()] + ' ' + d3.getDate() + ', ' + d3.getFullYear();
  
          var time = (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
          var time2= (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ':' + (d1.getSeconds() < 10 ? '0' : '') + d1.getSeconds() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
          var dsp = dd1;
          if (dd1 === dd2) dsp = time;
          if (dd1 === dd3) dsp = w2utils.lang('Yesterday');
  
          return '<span title="'+ dd1 +' ' + time2 +'">'+ dsp +'</span>';
      }
  
      function formatSize (sizeStr) {
          if (!w2utils.isFloat(sizeStr) || sizeStr === '') return '';
          sizeStr = parseFloat(sizeStr);
          if (sizeStr === 0) return 0;
          var sizes = ['Bt', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB'];
          var i = parseInt( Math.floor( Math.log(sizeStr) / Math.log(1024) ) );
          return (Math.floor(sizeStr / Math.pow(1024, i) * 10) / 10).toFixed(i === 0 ? 0 : 1) + ' ' + (sizes[i] || '??');
      }
  
      function formatNumber (val, fraction, useGrouping) {
          if (val == null || val === '' || typeof val == 'object') return '';
          var options = {
              minimumFractionDigits : fraction,
              maximumFractionDigits : fraction,
              useGrouping : useGrouping
          };
          if (fraction == null || fraction < 0) {
              options.minimumFractionDigits = 0;
              options.maximumFractionDigits = 20;
          }
          return parseFloat(val).toLocaleString(w2utils.settings.locale, options);
      }
  
      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 '';
  
          var dt = new Date(dateStr);
          if (w2utils.isInt(dateStr)) dt = new Date(Number(dateStr)); // for unix timestamps
          if (String(dt) == 'Invalid Date') return '';
  
          var year  = dt.getFullYear();
          var month = dt.getMonth();
          var date  = dt.getDate();
          return format.toLowerCase()
              .replace('month', w2utils.settings.fullmonths[month])
              .replace('mon', w2utils.settings.shortmonths[month])
              .replace(/yyyy/g, year)
              .replace(/yyy/g, year)
              .replace(/yy/g, year > 2000 ? 100 + parseInt(String(year).substr(2)) : String(year).substr(2))
              .replace(/(^|[^a-z$])y/g, '$1' + year)            // only y's that are not preceded by a letter
              .replace(/mm/g, (month + 1 < 10 ? '0' : '') + (month + 1))
              .replace(/dd/g, (date < 10 ? '0' : '') + date)
              .replace(/th/g, (date == 1 ? 'st' : 'th'))
              .replace(/th/g, (date == 2 ? 'nd' : 'th'))
              .replace(/th/g, (date == 3 ? 'rd' : 'th'))
              .replace(/(^|[^a-z$])m/g, '$1' + (month + 1))     // only y's that are not preceded by a letter
              .replace(/(^|[^a-z$])d/g, '$1' + date);           // only y's that are not preceded by a letter
      }
  
      function formatTime (dateStr, format) { // IMPORTANT dateStr HAS TO BE valid JavaScript Date String
          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 '';
  
          var dt = new Date(dateStr);
          if (w2utils.isInt(dateStr)) dt  = new Date(Number(dateStr)); // for unix timestamps
          if (w2utils.isTime(dateStr)) {
              var tmp = w2utils.isTime(dateStr, true);
              dt = new Date();
              dt.setHours(tmp.hours);
              dt.setMinutes(tmp.minutes);
          }
          if (String(dt) == 'Invalid Date') return '';
  
          var type = 'am';
          var hour = dt.getHours();
          var h24  = dt.getHours();
          var min  = dt.getMinutes();
          var sec  = dt.getSeconds();
          if (min < 10) min = '0' + min;
          if (sec < 10) sec = '0' + sec;
          if (format.indexOf('am') !== -1 || format.indexOf('pm') !== -1) {
              if (hour >= 12) type = 'pm';
              if (hour > 12)  hour = hour - 12;
          }
          return format.toLowerCase()
              .replace('am', type)
              .replace('pm', type)
              .replace('hhh', (hour < 10 ? '0' + hour : hour))
              .replace('hh24', (h24 < 10 ? '0' + h24 : h24))
              .replace('h24', h24)
              .replace('hh', hour)
              .replace('mm', min)
              .replace('mi', min)
              .replace('ss', sec)
              .replace(/(^|[^a-z$])h/g, '$1' + hour)    // only y's that are not preceded by a letter
              .replace(/(^|[^a-z$])m/g, '$1' + min)     // only y's that are not preceded by a letter
              .replace(/(^|[^a-z$])s/g, '$1' + sec);    // only y's that are not preceded by a letter
      }
  
      function formatDateTime(dateStr, format) {
          var fmt;
          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();
          }
          // older formats support
          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]);
      }
  
      function stripTags (html) {
          if (html == null) return html;
          switch (typeof html) {
              case 'number':
                  break;
              case 'string':
                  html = String(html).replace(/(<([^>]+)>)/ig, "");
                  break;
              case 'object':
                  // does not modify original object, but creates a copy
                  if (Array.isArray(html)) {
                      html = $.extend(true, [], html);
                      for (var i = 0; i < html.length; i++) html[i] = this.stripTags(html[i]);
                  }  else {
                      html = $.extend(true, {}, html);
                      for (var i in html) html[i] = this.stripTags(html[i]);
                  }
                  break;
          }
          return html;
      }
  
      function encodeTags (html) {
          if (html == null) return html;
          switch (typeof html) {
              case 'number':
                  break;
              case 'string':
                  html = String(html).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
                  break;
              case 'object':
                  // does not modify original object, but creates a copy
                  if (Array.isArray(html)) {
                      html = $.extend(true, [], html);
                      for (var i = 0; i < html.length; i++) html[i] = this.encodeTags(html[i]);
                  }  else {
                      html = $.extend(true, {}, html);
                      for (var i in html) html[i] = this.encodeTags(html[i]);
                  }
                  break;
          }
          return html;
      }
  
      function decodeTags (html) {
          if (html == null) return html;
          switch (typeof html) {
              case 'number':
                  break;
              case 'string':
                  html = String(html).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
                  break;
              case 'object':
                  // does not modify original object, but creates a copy
                  if (Array.isArray(html)) {
                      html = $.extend(true, [], html);
                      for (var i = 0; i < html.length; i++) html[i] = this.decodeTags(html[i]);
                  }  else {
                      html = $.extend(true, {}, html);
                      for (var i in html) html[i] = this.decodeTags(html[i]);
                  }
                  break;
          }
          return html;
      }
  
      function escapeId (id) {
          if (id === '' || id == null) return '';
          return String(id).replace(/([;&,\.\+\*\~'`:"\!\^#$%@\[\]\(\)=<>\|\/? {}\\])/g, '\\$1');
      }
  
      function base64encode (input) {
          var output = "";
          var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
          var i = 0;
          var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
          input = utf8_encode(input);
  
          while (i < input.length) {
              chr1 = input.charCodeAt(i++);
              chr2 = input.charCodeAt(i++);
              chr3 = input.charCodeAt(i++);
              enc1 = chr1 >> 2;
              enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
              enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
              enc4 = chr3 & 63;
              if (isNaN(chr2)) {
                  enc3 = enc4 = 64;
              } else if (isNaN(chr3)) {
                  enc4 = 64;
              }
              output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
          }
  
          function utf8_encode (string) {
              string = String(string).replace(/\r\n/g,"\n");
              var utftext = "";
  
              for (var n = 0; n < string.length; n++) {
                  var c = string.charCodeAt(n);
                  if (c < 128) {
                      utftext += String.fromCharCode(c);
                  }
                  else if((c > 127) && (c < 2048)) {
                      utftext += String.fromCharCode((c >> 6) | 192);
                      utftext += String.fromCharCode((c & 63) | 128);
                  }
                  else {
                      utftext += String.fromCharCode((c >> 12) | 224);
                      utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                      utftext += String.fromCharCode((c & 63) | 128);
                  }
              }
              return utftext;
          }
  
          return output;
      }
  
      function base64decode (input) {
          var output = "";
          var chr1, chr2, chr3;
          var enc1, enc2, enc3, enc4;
          var i = 0;
          var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
          input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  
          while (i < input.length) {
              enc1 = keyStr.indexOf(input.charAt(i++));
              enc2 = keyStr.indexOf(input.charAt(i++));
              enc3 = keyStr.indexOf(input.charAt(i++));
              enc4 = keyStr.indexOf(input.charAt(i++));
              chr1 = (enc1 << 2) | (enc2 >> 4);
              chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
              chr3 = ((enc3 & 3) << 6) | enc4;
              output = output + String.fromCharCode(chr1);
              if (enc3 !== 64) {
                  output = output + String.fromCharCode(chr2);
              }
              if (enc4 !== 64) {
                  output = output + String.fromCharCode(chr3);
              }
          }
          output = utf8_decode(output);
  
          function utf8_decode (utftext) {
              var string = "";
              var i = 0;
              var c = 0, c2, c3;
  
              while ( i < utftext.length ) {
                  c = utftext.charCodeAt(i);
                  if (c < 128) {
                      string += String.fromCharCode(c);
                      i++;
                  }
                  else if((c > 191) && (c < 224)) {
                      c2 = utftext.charCodeAt(i+1);
                      string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                      i += 2;
                  }
                  else {
                      c2 = utftext.charCodeAt(i+1);
                      c3 = utftext.charCodeAt(i+2);
                      string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                      i += 3;
                  }
              }
  
              return string;
          }
  
          return output;
      }
  
      function md5(input) {
          /*
           * Based on http://pajhome.org.uk/crypt/md5
           */
  
          var hexcase = 0;
          var b64pad = "";
  
          function __pj_crypt_hex_md5(s) {
              return __pj_crypt_rstr2hex(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)));
          }
          function __pj_crypt_b64_md5(s) {
              return __pj_crypt_rstr2b64(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)));
          }
          function __pj_crypt_any_md5(s, e) {
              return __pj_crypt_rstr2any(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)), e);
          }
          function __pj_crypt_hex_hmac_md5(k, d)
          {
              return __pj_crypt_rstr2hex(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)));
          }
          function __pj_crypt_b64_hmac_md5(k, d)
          {
              return __pj_crypt_rstr2b64(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)));
          }
          function __pj_crypt_any_hmac_md5(k, d, e)
          {
              return __pj_crypt_rstr2any(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)), e);
          }
  
          /*
           * Calculate the MD5 of a raw string
           */
          function __pj_crypt_rstr_md5(s)
          {
              return __pj_crypt_binl2rstr(__pj_crypt_binl_md5(__pj_crypt_rstr2binl(s), s.length * 8));
          }
  
          /*
           * Calculate the HMAC-MD5, of a key and some data (raw strings)
           */
          function __pj_crypt_rstr_hmac_md5(key, data)
          {
              var bkey = __pj_crypt_rstr2binl(key);
              if (bkey.length > 16)
                  bkey = __pj_crypt_binl_md5(bkey, key.length * 8);
  
              var ipad = Array(16), opad = Array(16);
              for (var i = 0; i < 16; i++)
              {
                  ipad[i] = bkey[i] ^ 0x36363636;
                  opad[i] = bkey[i] ^ 0x5C5C5C5C;
              }
  
              var hash = __pj_crypt_binl_md5(ipad.concat(__pj_crypt_rstr2binl(data)), 512 + data.length * 8);
              return __pj_crypt_binl2rstr(__pj_crypt_binl_md5(opad.concat(hash), 512 + 128));
          }
  
          /*
           * Convert a raw string to a hex string
           */
          function __pj_crypt_rstr2hex(input)
          {
              try {
                  hexcase
              } catch (e) {
                  hexcase = 0;
              }
              var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
              var output = "";
              var x;
              for (var i = 0; i < input.length; i++)
              {
                  x = input.charCodeAt(i);
                  output += hex_tab.charAt((x >>> 4) & 0x0F)
                          + hex_tab.charAt(x & 0x0F);
              }
              return output;
          }
  
          /*
           * Convert a raw string to a base-64 string
           */
          function __pj_crypt_rstr2b64(input)
          {
              try {
                  b64pad
              } catch (e) {
                  b64pad = '';
              }
              var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
              var output = "";
              var len = input.length;
              for (var i = 0; i < len; i += 3)
              {
                  var triplet = (input.charCodeAt(i) << 16)
                          | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0)
                          | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
                  for (var j = 0; j < 4; j++)
                  {
                      if (i * 8 + j * 6 > input.length * 8)
                          output += b64pad;
                      else
                          output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
                  }
              }
              return output;
          }
  
          /*
           * Convert a raw string to an arbitrary string encoding
           */
          function __pj_crypt_rstr2any(input, encoding)
          {
              var divisor = encoding.length;
              var i, j, q, x, quotient;
  
              /* Convert to an array of 16-bit big-endian values, forming the dividend */
              var dividend = Array(Math.ceil(input.length / 2));
              for (i = 0; i < dividend.length; i++)
              {
                  dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
              }
  
              /*
               * Repeatedly perform a long division. The binary array forms the dividend,
               * the length of the encoding is the divisor. Once computed, the quotient
               * forms the dividend for the next step. All remainders are stored for later
               * use.
               */
              var full_length = Math.ceil(input.length * 8 /
                      (Math.log(encoding.length) / Math.log(2)));
              var remainders = Array(full_length);
              for (j = 0; j < full_length; j++)
              {
                  quotient = Array();
                  x = 0;
                  for (i = 0; i < dividend.length; i++)
                  {
                      x = (x << 16) + dividend[i];
                      q = Math.floor(x / divisor);
                      x -= q * divisor;
                      if (quotient.length > 0 || q > 0)
                          quotient[quotient.length] = q;
                  }
                  remainders[j] = x;
                  dividend = quotient;
              }
  
              /* Convert the remainders to the output string */
              var output = "";
              for (i = remainders.length - 1; i >= 0; i--)
                  output += encoding.charAt(remainders[i]);
  
              return output;
          }
  
          /*
           * Encode a string as utf-8.
           * For efficiency, this assumes the input is valid utf-16.
           */
          function __pj_crypt_str2rstr_utf8(input)
          {
              var output = "";
              var i = -1;
              var x, y;
  
              while (++i < input.length)
              {
                  /* Decode utf-16 surrogate pairs */
                  x = input.charCodeAt(i);
                  y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
                  if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
                  {
                      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
                      i++;
                  }
  
                  /* Encode output as utf-8 */
                  if (x <= 0x7F)
                      output += String.fromCharCode(x);
                  else if (x <= 0x7FF)
                      output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
                              0x80 | (x & 0x3F));
                  else if (x <= 0xFFFF)
                      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                              0x80 | ((x >>> 6) & 0x3F),
                              0x80 | (x & 0x3F));
                  else if (x <= 0x1FFFFF)
                      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                              0x80 | ((x >>> 12) & 0x3F),
                              0x80 | ((x >>> 6) & 0x3F),
                              0x80 | (x & 0x3F));
              }
              return output;
          }
  
          /*
           * Encode a string as utf-16
           */
          function __pj_crypt_str2rstr_utf16le(input)
          {
              var output = "";
              for (var i = 0; i < input.length; i++)
                  output += String.fromCharCode(input.charCodeAt(i) & 0xFF,
                          (input.charCodeAt(i) >>> 8) & 0xFF);
              return output;
          }
  
          function __pj_crypt_str2rstr_utf16be(input)
          {
              var output = "";
              for (var i = 0; i < input.length; i++)
                  output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                          input.charCodeAt(i) & 0xFF);
              return output;
          }
  
          /*
           * Convert a raw string to an array of little-endian words
           * Characters >255 have their high-byte silently ignored.
           */
          function __pj_crypt_rstr2binl(input)
          {
              var output = Array(input.length >> 2);
              for (var i = 0; i < output.length; i++)
                  output[i] = 0;
              for (var i = 0; i < input.length * 8; i += 8)
                  output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
              return output;
          }
  
          /*
           * Convert an array of little-endian words to a string
           */
          function __pj_crypt_binl2rstr(input)
          {
              var output = "";
              for (var i = 0; i < input.length * 32; i += 8)
                  output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
              return output;
          }
  
          /*
           * Calculate the MD5 of an array of little-endian words, and a bit length.
           */
          function __pj_crypt_binl_md5(x, len)
          {
              /* append padding */
              x[len >> 5] |= 0x80 << ((len) % 32);
              x[(((len + 64) >>> 9) << 4) + 14] = len;
  
              var a = 1732584193;
              var b = -271733879;
              var c = -1732584194;
              var d = 271733878;
  
              for (var i = 0; i < x.length; i += 16)
              {
                  var olda = a;
                  var oldb = b;
                  var oldc = c;
                  var oldd = d;
  
                  a = __pj_crypt_md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
                  d = __pj_crypt_md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
                  c = __pj_crypt_md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
                  b = __pj_crypt_md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
                  a = __pj_crypt_md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
                  d = __pj_crypt_md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
                  c = __pj_crypt_md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
                  b = __pj_crypt_md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
                  a = __pj_crypt_md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
                  d = __pj_crypt_md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
                  c = __pj_crypt_md5_ff(c, d, a, b, x[i + 10], 17, -42063);
                  b = __pj_crypt_md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
                  a = __pj_crypt_md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
                  d = __pj_crypt_md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
                  c = __pj_crypt_md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
                  b = __pj_crypt_md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
  
                  a = __pj_crypt_md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
                  d = __pj_crypt_md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
                  c = __pj_crypt_md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
                  b = __pj_crypt_md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
                  a = __pj_crypt_md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
                  d = __pj_crypt_md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
                  c = __pj_crypt_md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
                  b = __pj_crypt_md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
                  a = __pj_crypt_md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
                  d = __pj_crypt_md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
                  c = __pj_crypt_md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
                  b = __pj_crypt_md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
                  a = __pj_crypt_md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
                  d = __pj_crypt_md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
                  c = __pj_crypt_md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
                  b = __pj_crypt_md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
  
                  a = __pj_crypt_md5_hh(a, b, c, d, x[i + 5], 4, -378558);
                  d = __pj_crypt_md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
                  c = __pj_crypt_md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
                  b = __pj_crypt_md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
                  a = __pj_crypt_md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
                  d = __pj_crypt_md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
                  c = __pj_crypt_md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
                  b = __pj_crypt_md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
                  a = __pj_crypt_md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
                  d = __pj_crypt_md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
                  c = __pj_crypt_md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
                  b = __pj_crypt_md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
                  a = __pj_crypt_md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
                  d = __pj_crypt_md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
                  c = __pj_crypt_md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
                  b = __pj_crypt_md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
  
                  a = __pj_crypt_md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
                  d = __pj_crypt_md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
                  c = __pj_crypt_md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
                  b = __pj_crypt_md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
                  a = __pj_crypt_md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
                  d = __pj_crypt_md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
                  c = __pj_crypt_md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
                  b = __pj_crypt_md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
                  a = __pj_crypt_md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
                  d = __pj_crypt_md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
                  c = __pj_crypt_md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
                  b = __pj_crypt_md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
                  a = __pj_crypt_md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
                  d = __pj_crypt_md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
                  c = __pj_crypt_md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
                  b = __pj_crypt_md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
  
                  a = __pj_crypt_safe_add(a, olda);
                  b = __pj_crypt_safe_add(b, oldb);
                  c = __pj_crypt_safe_add(c, oldc);
                  d = __pj_crypt_safe_add(d, oldd);
              }
              return Array(a, b, c, d);
          }
  
          /*
           * These functions implement the four basic operations the algorithm uses.
           */
          function __pj_crypt_md5_cmn(q, a, b, x, s, t)
          {
              return __pj_crypt_safe_add(__pj_crypt_bit_rol(__pj_crypt_safe_add(__pj_crypt_safe_add(a, q), __pj_crypt_safe_add(x, t)), s), b);
          }
          function __pj_crypt_md5_ff(a, b, c, d, x, s, t)
          {
              return __pj_crypt_md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
          }
          function __pj_crypt_md5_gg(a, b, c, d, x, s, t)
          {
              return __pj_crypt_md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
          }
          function __pj_crypt_md5_hh(a, b, c, d, x, s, t)
          {
              return __pj_crypt_md5_cmn(b ^ c ^ d, a, b, x, s, t);
          }
          function __pj_crypt_md5_ii(a, b, c, d, x, s, t)
          {
              return __pj_crypt_md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
          }
  
          /*
           * Add integers, wrapping at 2^32. This uses 16-bit operations internally
           * to work around bugs in some JS interpreters.
           */
          function __pj_crypt_safe_add(x, y)
          {
              var lsw = (x & 0xFFFF) + (y & 0xFFFF);
              var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
              return (msw << 16) | (lsw & 0xFFFF);
          }
  
          /*
           * Bitwise rotate a 32-bit number to the left.
           */
          function __pj_crypt_bit_rol(num, cnt)
          {
              return (num << cnt) | (num >>> (32 - cnt));
          }
  
          return __pj_crypt_hex_md5(input);
  
      }
  
      function transition (div_old, div_new, type, callBack) {
          var width  = $(div_old).width();
          var height = $(div_old).height();
          var time   = 0.5;
  
          if (!div_old || !div_new) {
              console.log('ERROR: Cannot do transition when one of the divs is null');
              return;
          }
  
          div_old.parentNode.style.cssText += 'perspective: 900px; overflow: hidden;';
          div_old.style.cssText += '; position: absolute; z-index: 1019; backface-visibility: hidden';
          div_new.style.cssText += '; position: absolute; z-index: 1020; backface-visibility: hidden';
  
          switch (type) {
              case 'slide-left':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; transform: translate3d('+ width + 'px, 0, 0)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(-'+ width +'px, 0, 0)';
                  }, 1);
                  break;
  
              case 'slide-right':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; transform: translate3d(-'+ width +'px, 0, 0)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0px, 0, 0)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d('+ width +'px, 0, 0)';
                  }, 1);
                  break;
  
              case 'slide-down':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; z-index: 1; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; z-index: 0; transform: translate3d(0, 0, 0)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, '+ height +'px, 0)';
                  }, 1);
                  break;
  
              case 'slide-up':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, '+ height +'px, 0)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
                  }, 1);
                  break;
  
              case 'flip-left':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: rotateY(0deg)';
                  div_new.style.cssText += 'overflow: hidden; transform: rotateY(-180deg)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: rotateY(0deg)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: rotateY(180deg)';
                  }, 1);
                  break;
  
              case 'flip-right':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: rotateY(0deg)';
                  div_new.style.cssText += 'overflow: hidden; transform: rotateY(180deg)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: rotateY(0deg)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: rotateY(-180deg)';
                  }, 1);
                  break;
  
              case 'flip-down':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: rotateX(0deg)';
                  div_new.style.cssText += 'overflow: hidden; transform: rotateX(180deg)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: rotateX(0deg)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: rotateX(-180deg)';
                  }, 1);
                  break;
  
              case 'flip-up':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: rotateX(0deg)';
                  div_new.style.cssText += 'overflow: hidden; transform: rotateX(-180deg)';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: rotateX(0deg)';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: rotateX(180deg)';
                  }, 1);
                  break;
  
              case 'pop-in':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(.8); opacity: 0;';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; transform: scale(1); opacity: 1;';
                      div_old.style.cssText += 'transition: '+ time +'s;';
                  }, 1);
                  break;
  
              case 'pop-out':
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(1); opacity: 1;';
                  div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); opacity: 0;';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; opacity: 1;';
                      div_old.style.cssText += 'transition: '+ time +'s; transform: scale(1.7); opacity: 0;';
                  }, 1);
                  break;
  
              default:
                  // init divs
                  div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
                  div_new.style.cssText += 'overflow: hidden; translate3d(0, 0, 0); opacity: 0;';
                  $(div_new).show();
                  // -- need a timing function because otherwise not working
                  window.setTimeout(function() {
                      div_new.style.cssText += 'transition: '+ time +'s; opacity: 1;';
                      div_old.style.cssText += 'transition: '+ time +'s';
                  }, 1);
                  break;
          }
  
          setTimeout(function () {
              if (type === 'slide-down') {
                  $(div_old).css('z-index', '1019');
                  $(div_new).css('z-index', '1020');
              }
              if (div_new) {
                  $(div_new).css({ 'opacity': '1' }).css(w2utils.cssPrefix({
                      'transition': '',
                      'transform' : ''
                  }));
              }
              if (div_old) {
                  $(div_old).css({ 'opacity': '1' }).css(w2utils.cssPrefix({
                      'transition': '',
                      'transform' : ''
                  }));
              }
              if (typeof callBack === 'function') callBack();
          }, time * 1000);
      }
  
      function lock (box, msg, spinner) {
          var options = {};
          if (typeof msg === 'object') {
              options = msg;
          } else {
              options.msg     = msg;
              options.spinner = spinner;
          }
          if (!options.msg && options.msg !== 0) options.msg = '';
          w2utils.unlock(box);
          $(box).prepend(
              '<div class="w2ui-lock"></div>'+
              '<div class="w2ui-lock-msg"></div>'
          );
          var $lock = $(box).find('.w2ui-lock');
          var mess = $(box).find('.w2ui-lock-msg');
          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') {
              $lock.fadeIn(200);
              mess.html(options.msg).fadeIn(200);
          } else {
              $lock.show();
              mess.html(options.msg).show(0);
          }
      }
  
      function unlock (box, speed) {
          if (isInt(speed)) {
              $(box).find('.w2ui-lock').fadeOut(speed);
              setTimeout(function () {
                  $(box).find('.w2ui-lock').remove();
                  $(box).find('.w2ui-lock-msg').remove();
              }, speed);
          } else {
              $(box).find('.w2ui-lock').remove();
              $(box).find('.w2ui-lock-msg').remove();
          }
      }
  
      /**
      *  Used in w2popup, w2grid, w2form, w2layout
      *  should be called with .call(...) method
      */
  
      function message(where, options) {
          var obj = this, closeTimer, edata;
          // var where.path    = 'w2popup';
          // var where.title   = '.w2ui-popup-title';
          // var where.body    = '.w2ui-box';
          $().w2tag(); // hide all tags
          if (!options) options = { width: 200, height: 100 };
          if (options.on == null) $.extend(options, w2utils.event);
          if (options.width == null) options.width = 200;
          if (options.height == null) options.height = 100;
          var pWidth  = parseInt($(where.box).width());
          var pHeight = parseInt($(where.box).height());
          var titleHeight = parseInt($(where.box).find(where.title).css('height') || 0);
          if (options.width > pWidth) options.width = pWidth - 10;
          if (options.height > pHeight - titleHeight) options.height = pHeight - 10 - titleHeight;
          options.originalWidth  = options.width;
          options.originalHeight = options.height;
          if (parseInt(options.width) < 0)   options.width  = pWidth + options.width;
          if (parseInt(options.width) < 10)  options.width  = 10;
          if (parseInt(options.height) < 0)  options.height  = pHeight + options.height - titleHeight;
          if (parseInt(options.height) < 10) options.height = 10;
          if (options.hideOnClick == null) options.hideOnClick = false;
          var poptions = $(where.box).data('options') || {};
          if (options.width == null || options.width > poptions.width - 10) {
              options.width = poptions.width - 10;
          }
          if (options.height == null || options.height > poptions.height - titleHeight - 5) {
              options.height = poptions.height - titleHeight - 5; // need margin from bottom only
          }
          // negative value means margin
          if (options.originalHeight < 0) options.height = pHeight + options.originalHeight - titleHeight;
          if (options.originalWidth < 0) options.width = pWidth + options.originalWidth * 2; // x 2 because there is left and right margin
          var head = $(where.box).find(where.title);
  
          // if some messages are closing, insta close them
          var $tmp = $(where.box).find('.w2ui-message.w2ui-closing');
          if ($(where.box).find('.w2ui-message.w2ui-closing').length > 0) {
              clearTimeout(closeTimer);
              closeCB($tmp, $tmp.data('options') || {});
          }
          var msgCount = $(where.box).find('.w2ui-message').length;
          // remove message
          if ($.trim(options.html) === '' && $.trim(options.body) === '' && $.trim(options.buttons) === '') {
              if (msgCount === 0) return; // no messages at all
              var $msg = $(where.box).find('#w2ui-message'+ (msgCount-1));
              var options = $msg.data('options') || {};
              // before event
              edata = options.trigger({ phase: 'before', type: 'close', target: 'self' });
              if (edata.isCancelled === true) return;
              // default behavior
              $msg.css(w2utils.cssPrefix({
                  'transition': '0.15s',
                  'transform': 'translateY(-' + options.height + 'px)'
              })).addClass('w2ui-closing');
              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);
  
          } else {
  
              if ($.trim(options.body) !== '' || $.trim(options.buttons) !== '') {
                  options.html = '<div class="w2ui-message-body">'+ (options.body || '') +'</div>'+
                      '<div class="w2ui-message-buttons">'+ (options.buttons || '') +'</div>';
              }
              // hide previous messages
              $(where.box).find('.w2ui-message').css('z-index', 1390);
              head.data('old-z-index', head.css('z-index'));
              head.css('z-index', 1501);
              // add message
              $(where.box).find(where.body)
                  .before('<div id="w2ui-message' + msgCount + '" onmousedown="event.stopPropagation();" '+
                          '   class="w2ui-message" style="display: none; z-index: 1500; ' +
                              (head.length === 0 ? 'top: 0px;' : 'top: ' + w2utils.getSize(head, 'height') + 'px;') +
                              (options.width  != null ? 'width: ' + options.width + 'px; left: ' + ((pWidth - options.width) / 2) + 'px;' : 'left: 10px; right: 10px;') +
                              (options.height != null ? 'height: ' + options.height + 'px;' : 'bottom: 6px;') +
                              w2utils.cssPrefix('transition', '.3s', true) + '"' +
                              (options.hideOnClick === true
                                  ? where.param
                                      ? 'onclick="'+ where.path +'.message(\''+ where.param +'\');"'
                                      : 'onclick="'+ where.path +'.message();"'
                                  : '') + '>' +
                          '</div>');
              $(where.box).find('#w2ui-message'+ msgCount)
                  .data('options', options)
                  .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)')
              }));
              if (display == 'none') {
                  $(where.box).find('#w2ui-message'+ msgCount).show().html(options.html);
                  options.box = $(where.box).find('#w2ui-message'+ msgCount);
                  // before event
                  edata = options.trigger({ phase: 'before', type: 'open', target: 'self' });
                  if (edata.isCancelled === true) {
                      head.css('z-index', head.data('old-z-index'));
                      $(where.box).find('#w2ui-message'+ msgCount).remove();
                      return;
                  }
                  // timer needs to animation
                  setTimeout(function () {
                      $(where.box).find('#w2ui-message'+ msgCount).css(w2utils.cssPrefix({
                          'transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)')
                      }));
                  }, 1);
                  // timer for lock
                  if (msgCount === 0 && this.lock) {
                      if (where.param) this.lock(where.param); else this.lock();
                  }
                  setTimeout(function() {
                      // has to be on top of lock
                      $(where.box).find('#w2ui-message'+ msgCount).css(w2utils.cssPrefix({ 'transition': '0s' }));
                      // event after
                      options.trigger($.extend(edata, { phase: 'after' }));
                  }, 350);
              }
          }
  
          function closeCB($msg, options) {
              if (edata == null) {
                  // before event
                  edata = options.trigger({ phase: 'before', type: 'open', target: 'self' });
                  if (edata.isCancelled === true) {
                      head.css('z-index', head.data('old-z-index'));
                      $(where.box).find('#w2ui-message'+ msgCount).remove();
                      return;
                  }
              }
              var $focus = $msg.data('prev_focus');
              $msg.remove();
              if ($focus && $focus.length > 0) {
                  $focus.focus();
              } else {
                  if (obj && obj.focus) obj.focus();
              }
              head.css('z-index', head.data('old-z-index'));
              // event after
              options.trigger($.extend(edata, { phase: 'after' }));
          }
      }
  
      function getSize (el, type) {
          var $el = $(el);
          var bwidth = {
              left    : parseInt($el.css('border-left-width')) || 0,
              right   : parseInt($el.css('border-right-width')) || 0,
              top     : parseInt($el.css('border-top-width')) || 0,
              bottom  : parseInt($el.css('border-bottom-width')) || 0
          };
          var mwidth = {
              left    : parseInt($el.css('margin-left')) || 0,
              right   : parseInt($el.css('margin-right')) || 0,
              top     : parseInt($el.css('margin-top')) || 0,
              bottom  : parseInt($el.css('margin-bottom')) || 0
          };
          var pwidth = {
              left    : parseInt($el.css('padding-left')) || 0,
              right   : parseInt($el.css('padding-right')) || 0,
              top     : parseInt($el.css('padding-top')) || 0,
              bottom  : parseInt($el.css('padding-bottom')) || 0
          };
          switch (type) {
              case 'top'      : return bwidth.top + mwidth.top + pwidth.top;
              case 'bottom'   : return bwidth.bottom + mwidth.bottom + pwidth.bottom;
              case 'left'     : return bwidth.left + mwidth.left + pwidth.left;
              case 'right'    : return bwidth.right + mwidth.right + pwidth.right;
              case 'width'    : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right + parseInt($el.width());
              case 'height'   : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom + parseInt($el.height());
              case '+width'   : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right;
              case '+height'  : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom;
          }
          return 0;
      }
  
      function getStrWidth (str, styles) {
          var w, html = '<div id="_tmp_width" style="position: absolute; top: -900px;'+ (styles || '') +'">'+
                          encodeTags(str) +
                        '</div>';
          $('body').append(html);
          w = $('#_tmp_width').width();
          $('#_tmp_width').remove();
          return w;
      }
  
      function lang (phrase) {
          var translation = this.settings.phrases[phrase];
          if (translation == null) return phrase; else return translation;
      }
  
      function locale (locale) {
          if (!locale) locale = 'en-us';
  
          // if the locale is an object, not a string, than we assume it's a
          if(typeof locale !== "string" ) {
              w2utils.settings = $.extend(true, w2utils.settings, locale);
              return;
          }
  
          if (locale.length === 5) locale = 'locale/'+ locale +'.json';
  
          // clear phrases from language before
          w2utils.settings.phrases = {};
  
          // load from the file
          $.ajax({
              url      : locale,
              type     : "GET",
              dataType : "JSON",
              async    : false,
              success  : function (data, status, xhr) {
                  w2utils.settings = $.extend(true, w2utils.settings, data);
              },
              error    : function (xhr, status, msg) {
                  console.log('ERROR: Cannot load locale '+ locale);
              }
          });
      }
  
      function scrollBarSize () {
          if (tmp.scrollBarSize) return tmp.scrollBarSize;
          var html =
              '<div id="_scrollbar_width" style="position: absolute; top: -300px; width: 100px; height: 100px; overflow-y: scroll;">'+
              '    <div style="height: 120px">1</div>'+
              '</div>';
          $('body').append(html);
          tmp.scrollBarSize = 100 - $('#_scrollbar_width > div').width();
          $('#_scrollbar_width').remove();
          if (String(navigator.userAgent).indexOf('MSIE') >= 0) tmp.scrollBarSize  = tmp.scrollBarSize / 2; // need this for IE9+
          return tmp.scrollBarSize;
      }
  
      function checkName (params, component) { // was w2checkNameParam
          if (!params || params.name == null) {
              console.log('ERROR: The parameter "name" is required but not supplied in $().'+ component +'().');
              return false;
          }
          if (w2ui[params.name] != null) {
              console.log('ERROR: The parameter "name" is not unique. There are other objects already created with the same name (obj: '+ params.name +').');
              return false;
          }
          if (!w2utils.isAlphaNumeric(params.name)) {
              console.log('ERROR: The parameter "name" has to be alpha-numeric (a-z, 0-9, dash and underscore). ');
              return false;
          }
          return true;
      }
  
      function checkUniqueId (id, items, itemsDecription, objName) { // was w2checkUniqueId
          if (!$.isArray(items)) items = [items];
          for (var i = 0; i < items.length; i++) {
              if (items[i].id === id) {
                  console.log('ERROR: The parameter "id='+ id +'" is not unique within the current '+ itemsDecription +'. (obj: '+ objName +')');
                  return false;
              }
          }
          return true;
      }
  
      function parseRoute(route) {
          var keys = [];
          var path = route
              .replace(/\/\(/g, '(?:/')
              .replace(/\+/g, '__plus__')
              .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) {
                  keys.push({ name: key, optional: !! optional });
                  slash = slash || '';
                  return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + (optional || '');
              })
              .replace(/([\/.])/g, '\\$1')
              .replace(/__plus__/g, '(.+)')
              .replace(/\*/g, '(.*)');
          return {
              path  : new RegExp('^' + path + '$', 'i'),
              keys  : keys
          };
      }
  
      function cssPrefix(field, value, returnString) {
          var css    = {};
          var newCSS = {};
          var ret    = '';
          if (!$.isPlainObject(field)) {
              css[field] = value;
          } else {
              css = field;
              if (value === true) returnString = true;
          }
          for (var c in css) {
              newCSS[c] = css[c];
              newCSS['-webkit-'+c] = css[c];
              newCSS['-moz-'+c]    = css[c].replace('-webkit-', '-moz-');
              newCSS['-ms-'+c]     = css[c].replace('-webkit-', '-ms-');
              newCSS['-o-'+c]      = css[c].replace('-webkit-', '-o-');
          }
          if (returnString === true) {
              for (var c in newCSS) {
                  ret += c + ': ' + newCSS[c] + '; ';
              }
          } else {
              ret = newCSS;
          }
          return ret;
      }
  
      function getCursorPosition(input) {
          if (input == null) return null;
          var caretOffset = 0;
          var doc = input.ownerDocument || input.document;
          var win = doc.defaultView || doc.parentWindow;
          var sel;
          if (input.tagName && input.tagName.toUpperCase() == 'INPUT' && input.selectionStart) {
              // standards browser
              caretOffset = input.selectionStart;
          } else {
              if (win.getSelection) {
                  sel = win.getSelection();
                  if (sel.rangeCount > 0) {
                      var range = sel.getRangeAt(0);
                      var preCaretRange = range.cloneRange();
                      preCaretRange.selectNodeContents(input);
                      preCaretRange.setEnd(range.endContainer, range.endOffset);
                      caretOffset = preCaretRange.toString().length;
                  }
              } else if ( (sel = doc.selection) && sel.type != "Control") {
                  var textRange = sel.createRange();
                  var preCaretTextRange = doc.body.createTextRange();
                  preCaretTextRange.moveToElementText(input);
                  preCaretTextRange.setEndPoint("EndToEnd", textRange);
                  caretOffset = preCaretTextRange.text.length;
              }
          }
          return caretOffset;
      }
  
      function setCursorPosition(input, pos, posEnd) {
          var range = document.createRange();
          var el, sel = window.getSelection();
          if (input == null) return;
          for (var i = 0; i < input.childNodes.length; i++) {
              var tmp = $(input.childNodes[i]).text();
              if (input.childNodes[i].tagName) {
                  tmp = $(input.childNodes[i]).html();
                  tmp = tmp.replace(/&lt;/g, '<')
                           .replace(/&gt;/g, '>')
                           .replace(/&amp;/g, '&')
                           .replace(/&quot;/g, '"')
                           .replace(/&nbsp;/g, ' ');
              }
              if (pos <= tmp.length) {
                  el = input.childNodes[i];
                  if (el.childNodes && el.childNodes.length > 0) el = el.childNodes[0];
                  if (el.childNodes && el.childNodes.length > 0) el = el.childNodes[0];
                  break;
              } else {
                  pos -= tmp.length;
              }
          }
          if (el == null) return;
          if (pos > el.length) pos = el.length;
          range.setStart(el, pos);
          if (posEnd) {
              range.setEnd(el, posEnd);
          } else {
              range.collapse(true);
          }
          sel.removeAllRanges();
          sel.addRange(range);
      }
  
      function testLocalStorage() {
          // test if localStorage is available, see issue #1282
          // original code: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
          var str = 'w2ui_test';
          try {
            localStorage.setItem(str, str);
            localStorage.removeItem(str);
            return true;
          } catch (e) {
            return false;
          }
      }
  
      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)
          }
      }
  
  })(jQuery);
  
  /***********************************************************
  *  Formatters object
  *  --- Primariy used in grid
  *
  *********************************************************/
  
  w2utils.formatters = {
  
      'number': function (value, params) {
          if (parseInt(params) > 20) params = 20;
          if (parseInt(params) < 0) params = 0;
          if (value == null || value === '') return '';
          return w2utils.formatNumber(parseFloat(value), params, true);
      },
  
      'float': function (value, params) {
          return w2utils.formatters['number'](value, params);
      },
  
      'int': function (value, params) {
          return w2utils.formatters['number'](value, 0);
      },
  
      'money': function (value, params) {
          if (value == null || value === '') return '';
          var data = w2utils.formatNumber(Number(value), w2utils.settings.currencyPrecision || 2);
          return (w2utils.settings.currencyPrefix || '') + data + (w2utils.settings.currencySuffix || '');
      },
  
      'currency': function (value, params) {
          return w2utils.formatters['money'](value, params);
      },
  
      'percent': function (value, params) {
          if (value == null || value === '') return '';
          return w2utils.formatNumber(value, params || 1) + '%';
      },
  
      'size': function (value, params) {
          if (value == null || value === '') return '';
          return w2utils.formatSize(parseInt(value));
      },
  
      'date': function (value, params) {
          if (params === '') params = w2utils.settings.dateFormat;
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, params, true);
          if (dt === false) dt = w2utils.isDate(value, params, true);
          return '<span title="'+ dt +'">' + w2utils.formatDate(dt, params) + '</span>';
      },
  
      'datetime': function (value, params) {
          if (params === '') params = w2utils.settings.datetimeFormat;
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, params, true);
          if (dt === false) dt = w2utils.isDate(value, params, true);
          return '<span title="'+ dt +'">' + w2utils.formatDateTime(dt, params) + '</span>';
      },
  
      'time': function (value, params) {
          if (params === '') params = w2utils.settings.timeFormat;
          if (params === 'h12') params = 'hh:mi pm';
          if (params === 'h24') params = 'h24:mi';
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, params, true);
          if (dt === false) dt = w2utils.isDate(value, params, true);
          return '<span title="'+ dt +'">' + w2utils.formatTime(value, params) + '</span>';
      },
  
      'timestamp': function (value, params) {
          if (params === '') params = w2utils.settings.datetimeFormat;
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, params, true);
          if (dt === false) dt = w2utils.isDate(value, params, true);
          return dt.toString ? dt.toString() : '';
      },
  
      'gmt': function (value, params) {
          if (params === '') params = w2utils.settings.datetimeFormat;
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, params, true);
          if (dt === false) dt = w2utils.isDate(value, params, true);
          return dt.toUTCString ? dt.toUTCString() : '';
      },
  
      'age': function (value, params) {
          if (value == null || value === 0 || value === '') return '';
          var dt = w2utils.isDateTime(value, null, true);
          if (dt === false) dt = w2utils.isDate(value, null, true);
          return '<span title="'+ dt +'">' + w2utils.age(value) + (params ? (' ' + params) : '') + '</span>';
      },
  
      'interval': function (value, params) {
          if (value == null || value === 0 || value === '') return '';
          return w2utils.interval(value) + (params ? (' ' + params) : '');
      },
  
      'toggle': function (value, params) {
          return (value ? 'Yes' : '');
      },
  
      'password': function (value, params) {
          var ret = "";
          for (var i=0; i < value.length; i++) {
              ret += "*";
          }
          return ret;
      }
  };
  
  /***********************************************************
  *  Generic Event Object
  *  --- This object is reused across all other
  *  --- widgets in w2ui.
  *
  *********************************************************/
  
  w2utils.event = {
  
      on: function (edata, handler) {
          var $ = jQuery;
          var scope;
          // allow 'eventName.scope' syntax
          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) {
              var tmp = edata.split(':');
              if (['complete', 'done'].indexOf(edata[1]) != -1) edata[1] = 'after';
              edata = {
                  type    : tmp[0],
                  execute : tmp[1]
              };
          }
          if (!$.isPlainObject(edata)) edata = { type: edata, scope: scope };
          edata = $.extend({ type: null, execute: 'before', target: null, onComplete: null }, edata);
          // errors
          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 = [];
          this.handlers.push({ edata: edata, handler: handler });
      },
  
      off: function (edata, handler) {
          var $ = jQuery;
          var scope;
          // allow 'eventName.scope' syntax
          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) {
              var tmp = edata.split(':');
              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);
          // errors
          if (!edata.type && !scope) { console.log('ERROR: You must specify event type when calling .off() method of '+ this.name); return; }
          if (!handler) { handler = null; }
          // remove handlers
          var newHandlers = [];
          for (var h = 0, len = this.handlers.length; h < len; h++) {
              var t = this.handlers[h];
              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)))
              {
                  // match
              } else {
                  newHandlers.push(t);
              }
          }
          this.handlers = newHandlers;
      },
  
      trigger: function (edata) {
          var $ = jQuery;
          var edata = $.extend({ type: null, phase: 'before', target: null, doneHandlers: [] }, edata, {
              isStopped       : false,
              isCancelled     : false,
              done            : function (handler) { this.doneHandlers.push(handler); },
              preventDefault  : function () { this.isCancelled = true; },
              stopPropagation : function () { this.isStopped   = true; }
          });
          if (edata.phase === 'before') edata.onComplete = null;
          var args, fun, tmp;
          if (edata.target == null) edata.target = null;
          if (!$.isArray(this.handlers)) this.handlers = [];
          // process events in REVERSE order
          for (var h = this.handlers.length-1; h >= 0; h--) {
              var item = this.handlers[h];
              if ((item.edata.type === edata.type || item.edata.type === '*') &&
                  (item.edata.target === edata.target || item.edata.target == null) &&
                  (item.edata.execute === edata.phase || item.edata.execute === '*' || item.edata.phase === '*'))
              {
                  edata = $.extend({}, item.edata, edata);
                  // check handler arguments
                  args = [];
                  tmp  = new RegExp(/\((.*?)\)/).exec(item.handler);
                  if (tmp) args = tmp[1].split(/\s*,\s*/);
                  if (args.length === 2) {
                      item.handler.call(this, edata.target, edata); // old way for back compatibility
                  } else {
                      item.handler.call(this, edata); // new way
                  }
                  if (edata.isStopped === true || edata.stop === true) return edata; // back compatibility edata.stop === true
              }
          }
          // main object events
          var funName = 'on' + edata.type.substr(0,1).toUpperCase() + edata.type.substr(1);
          if (edata.phase === 'before' && typeof this[funName] === 'function') {
              fun = this[funName];
              // check handler arguments
              args = [];
              tmp  = new RegExp(/\((.*?)\)/).exec(fun);
              if (tmp) args = tmp[1].split(/\s*,\s*/);
              if (args.length === 2) {
                  fun.call(this, edata.target, edata); // old way for back compatibility
              } else {
                  fun.call(this, edata); // new way
              }
              if (edata.isStopped === true || edata.stop === true) return edata; // back compatibility edata.stop === true
          }
          // item object events
          if (edata.object != null && edata.phase === 'before' &&
              typeof edata.object[funName] === 'function')
          {
              fun = edata.object[funName];
              // check handler arguments
              args = [];
              tmp  = new RegExp(/\((.*?)\)/).exec(fun);
              if (tmp) args = tmp[1].split(/\s*,\s*/);
              if (args.length === 2) {
                  fun.call(this, edata.target, edata); // old way for back compatibility
              } else {
                  fun.call(this, edata); // new way
              }
              if (edata.isStopped === true || edata.stop === true) return edata;
          }
          // execute onComplete
          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') {
                      edata.doneHandlers[i].call(this, edata);
                  }
              }
          }
          return edata;
      }
  };
  
  /***********************************************************
  *  Commonly used plugins
  *  --- used primarily in grid and form
  *
  *********************************************************/
  
  (function ($) {
  
      $.fn.w2render = function (name) {
          if ($(this).length > 0) {
              if (typeof name === 'string' && w2ui[name]) w2ui[name].render($(this)[0]);
              if (typeof name === 'object') name.render($(this)[0]);
          }
      };
  
      $.fn.w2destroy = function (name) {
          if (!name && this.length > 0) name = this.attr('name');
          if (typeof name === 'string' && w2ui[name]) w2ui[name].destroy();
          if (typeof name === 'object') name.destroy();
      };
  
      $.fn.w2marker = function () {
          var str = Array.prototype.slice.call(arguments, 0);
          if (Array.isArray(str[0])) str = str[0];
          if (str.length === 0 || !str[0]) { // remove marker
              return $(this).each(clearMarkedText);
          } else { // add marker
              return $(this).each(function (index, el) {
                  clearMarkedText(index, el);
                  for (var s = 0; s < str.length; s++) {
                      var tmp = str[s];
                      if (typeof tmp !== 'string') tmp = String(tmp);
                      // escape regex special chars
                      tmp = tmp.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&").replace(/&/g, '&amp;').replace(/</g, '&gt;').replace(/>/g, '&lt;');
                      var regex = new RegExp(tmp + '(?!([^<]+)?>)', "gi"); // only outside tags
                      el.innerHTML = el.innerHTML.replace(regex, replaceValue);
                  }
                  function replaceValue(matched) { // mark new
                      return '<span class="w2ui-marker">' + matched + '</span>';
                  }
              });
          }
  
          function clearMarkedText(index, el) {
              while (el.innerHTML.indexOf('<span class="w2ui-marker">') != -1) {
                  el.innerHTML = el.innerHTML.replace(/\<span class=\"w2ui\-marker\"\>((.|\n|\r)*)\<\/span\>/ig, '$1'); // unmark
              }
          }
      };
  
      // -- w2tag - there can be multiple on screen at a time
  
      $.fn.w2tag = function (text, options) {
          // only one argument
          if (arguments.length == 1 && typeof text == 'object') {
              options = text;
              if (options.html != null) text = options.html;
          }
          // default options
          options = $.extend({
              id              : null,     // id for the tag, otherwise input id is used
              html            : text,     // or html
              position        : 'right|top',  // can be left, right, top, bottom
              align           : 'none',   // can be none, left, right (only works for potision: top | bottom)
              left            : 0,        // delta for left coordinate
              top             : 0,        // delta for top coordinate
              style           : '',       // adition style for the tag
              css             : {},       // add css for input when tag is shown
              className       : '',       // add class bubble
              inputClass      : '',       // add class for input when tag is shown
              onShow          : null,     // callBack when shown
              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
          }, options);
          if (options.name != null && options.id == null) options.id = options.name;
  
          // for backward compatibility
          if (options['class'] !== '' && options.inputClass === '') options.inputClass = options['class'];
  
          // 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();
              });
              return;
          }
          return $(this).each(function (index, el) {
              // show or hide tag
              var origID = (options.id ? options.id : el.id);
              var tagID  = w2utils.escapeId(origID);
              var $tags  = $('#w2ui-tag-'+tagID);
              if (text === '' || text == null) {
                  // remmove element
                  $tags.css('opacity', 0);
                  clearInterval($tags.data('timer'));
                  $tags.remove();
                  return;
              } else if ($tags.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);
              } else {
                  var originalCSS = '';
                  if ($(el).length > 0) originalCSS = $(el)[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 style="margin: -2px 0px 0px -2px; white-space: nowrap;">'+
                      '      <div class="w2ui-tag-body '+ options.className +'" style="'+ (options.style || '') +'">'+ text +'</div>'+
                      '   </div>' +
                      '</div>');
                  $tags = $('#w2ui-tag-'+tagID);
                  $(el).data('w2tag', $tags.get(0)).data('checkIfMoved', checkIfMoved);
              }
  
              // 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({
                          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))
                      .find('.w2ui-tag-body').addClass(pos['posClass']);
  
                  $(el).css(options.css)
                      .off('.w2tag')
                      .addClass(options.inputClass);
  
                  if (options.hideOnKeyPress) {
                      $(el).on('keypress.w2tag', hideTag);
                  }
                  if (options.hideOnBlur) {
                      $(el).on('blur.w2tag', hideTag);
                  }
                  if (options.hideOnClick) {
                      $(document).on('click.w2tag', hideTag)
                  }
                  if (typeof options.onShow === 'function') options.onShow();
              }, 1);
  
              // 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) {
                  // 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();
                      return;
                  }
                  if (!instant) setTimeout(checkIfMoved, 100);
                  // monitor if moved
                  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 width    = tagBody[0].offsetWidth;
                  var height   = tagBody[0].offsetHeight;
                  if (typeof options.position == 'string' && options.position.indexOf('|') != -1) {
                      options.position = options.position.split('|');
                  }
                  if (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') {
                      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') {
                      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)) {
                      // 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') {
                              posClass = 'w2ui-tag-right';
                              posLeft  = parseInt(offset.left + el.offsetWidth + (options.left ? options.left : 0));
                              posTop   = parseInt(offset.top + (options.top ? options.top : 0));
                              if (posLeft+width <= maxWidth) break;
                          }
                          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));
                              if (posLeft >= 0) break;
                          }
                          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;
                              if(posLeft+width <= maxWidth && posTop >= 0) break;
                          }
                          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;
                              if (posLeft+width <= maxWidth && posTop+height <= maxHeight) break;
                          }
                      }
                      if (tagBody.data('posClass') !== posClass) {
                          tagBody.removeClass('w2ui-tag-right w2ui-tag-left w2ui-tag-top w2ui-tag-bottom')
                              .addClass(posClass)
                              .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 };
              }
          });
      };
  
      // w2overlay - appears under the element, there can be only one at a time
  
      $.fn.w2overlay = function (html, options) {
          var obj  = this;
          var name = '';
          var defaults = {
              name        : null,              // it not null, then allows multiple concurrent overlays
              html        : '',                // html text to display
              align       : 'none',            // can be none, left, right, both
              left        : 0,                 // offset left
              top         : 0,                 // offset top
              tipLeft     : 30,                // tip offset left
              noTip       : false,             // if true - no tip will be displayed
              selectable  : false,
              width       : 0,                 // fixed width
              height      : 0,                 // fixed height
              maxWidth    : null,              // max width if any
              maxHeight   : null,              // max height if any
              contextMenu : false,             // if true, it will be opened at mouse position
              pageX       : null,
              pageY       : null,
              originalEvent : null,
              style       : '',                // additional style for main div
              'class'     : '',                // additional class name for main div
              overlayStyle: '',
              onShow      : null,              // event on show
              onHide      : null,              // event on hide
              openAbove   : false,             // show above control
              tmp         : {}
          };
          if (arguments.length == 1) {
              if (typeof html == 'object') {
                  options = html;
              } else {
                  options = { html: html };
              }
          }
          if (arguments.length == 2) options.html = html;
          if (!$.isPlainObject(options)) options = {};
          options = $.extend({}, defaults, options);
          if (options.name) name = '-' + options.name;
          // hide
          var tmp_hide;
          if (this.length === 0 || options.html === '' || options.html == null) {
              if ($('#w2ui-overlay'+ name).length > 0) {
                  tmp_hide = $('#w2ui-overlay'+ name)[0].hide;
                  if (typeof tmp_hide === 'function') tmp_hide();
              } else {
                  $('#w2ui-overlay'+ name).remove();
              }
              return $(this);
          }
          // hide previous if any
          if ($('#w2ui-overlay'+ name).length > 0) {
              tmp_hide = $('#w2ui-overlay'+ name)[0].hide;
              $(document).off('.w2overlayHide');
              if (typeof tmp_hide === 'function') tmp_hide();
          }
          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;
          }
          if (options.contextMenu && (options.pageX == null || options.pageY == null)) {
              console.log('ERROR: to display menu at mouse location, pass options.pageX and options.pageY.');
          }
          // append
          $('body').append(
              '<div id="w2ui-overlay'+ name +'" style="display: none; left: 0px; top: 0px; '+ options.overlayStyle +'"'+
              '        class="w2ui-reset w2ui-overlay '+ ($(this).parents('.w2ui-popup, .w2ui-overlay-popup, .w2ui-message').length > 0 ? 'w2ui-overlay-popup' : '') +'">'+
              '    <style></style>'+
              '    <div style="min-width: 100%; '+ options.style +'" class="'+ options['class'] +'"></div>'+
              '</div>'
          );
          // init
          var div1 = $('#w2ui-overlay'+ name);
          var div2 = div1.find(' > div');
          div2.html(options.html);
          // pick bg color of first div
          var bc  = div2.css('background-color');
          if (bc != null && bc !== 'rgba(0, 0, 0, 0)' && bc !== 'transparent') div1.css({ 'background-color': bc, 'border-color': bc });
  
          var offset = $(obj).offset() || {};
          div1.data('element', obj.length > 0 ? obj[0] : null)
              .data('options', options)
              .data('position', offset.left + 'x' + offset.top)
              .fadeIn('fast')
              .on('click', function (event) {
                  // if there is label for input, it will produce 2 click events
                  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) {
                      event.preventDefault();
                  }
              });
          div1[0].hide   = hide;
          div1[0].resize = resize;
  
          // need time to display
          setTimeout(function () {
              resize();
              $(document).off('.w2overlayHide').on('click.w2overlayHide', hide);
              if (typeof options.onShow === 'function') options.onShow();
          }, 10);
  
          monitor();
          return $(this);
  
          // monitor position
          function monitor() {
              var tmp = $('#w2ui-overlay'+ name);
              if (tmp.data('element') !== obj[0]) return; // it if it different overlay
              if (tmp.length === 0) return;
              var offset = $(obj).offset() || {};
              var pos = offset.left + 'x' + offset.top;
              if (tmp.data('position') !== pos) {
                  hide();
              } else {
                  setTimeout(monitor, 250);
              }
          }
  
          // click anywhere else hides the drop down
          function hide(event) {
              if (event && event.button !== 0) return; // only for left click button
              var div1 = $('#w2ui-overlay'+ name);
              if (div1.data('keepOpen') === true) {
                  div1.removeData('keepOpen');
                  return;
              }
              var result;
              if (typeof options.onHide === 'function') result = options.onHide();
              if (result === false) return;
              div1.remove();
              $(document).off('click', hide);
              clearInterval(div1.data('timer'));
          }
  
          function resize () {
              var div1 = $('#w2ui-overlay'+ name);
              var div2 = div1.find(' > div');
              var menu = $('#w2ui-overlay'+ name +' div.menu');
              menu.css('overflow-y', 'hidden');
              // if goes over the screen, limit height and width
              if (div1.length > 0) {
                  div2.height('auto').width('auto');
                  // width/height
                  var overflowX = false;
                  var overflowY = false;
                  var h = div2.height();
                  var w = div2.width();
                  if (options.width && options.width < w) w = options.width;
                  if (w < 30) w = 30;
                  // if content of specific height
                  if (options.tmp.contentHeight) {
                      h = parseInt(options.tmp.contentHeight);
                      div2.height(h);
                      setTimeout(function () {
                          var $div = div2.find('div.menu');
                          if (h > $div.height()) {
                              div2.find('div.menu').css('overflow-y', 'hidden');
                          }
                      }, 1);
                      setTimeout(function () {
                          var $div = div2.find('div.menu');
                          if ($div.css('overflow-y') != 'auto') $div.css('overflow-y', 'auto');
                      }, 10);
                  }
                  if (options.tmp.contentWidth && options.align != 'both') {
                      w = parseInt(options.tmp.contentWidth);
                      div2.width(w);
                      setTimeout(function () {
                          if (w > div2.find('div.menu > table').width()) {
                              div2.find('div.menu > table').css('overflow-x', 'hidden');
                          }
                      }, 1);
                      setTimeout(function () {
                          div2.find('div.menu > table').css('overflow-x', 'auto');
                      }, 10);
                  }
                  // adjust position
                  var boxLeft  = options.left;
                  var boxWidth = options.width;
                  var tipLeft  = options.tipLeft;
                  // alignment
                  switch (options.align) {
                      case 'both':
                          boxLeft = 17;
                          if (options.width === 0) options.width = w2utils.getSize($(obj), 'width');
                          if (options.maxWidth && options.width > options.maxWidth) options.width = options.maxWidth;
                          break;
                      case 'left':
                          boxLeft = 17;
                          break;
                      case 'right':
                          boxLeft = w2utils.getSize($(obj), 'width') - w + 10;
                          tipLeft = w - 40;
                          break;
                  }
                  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 (tmp < 25) {
                      boxLeft = 25 - tmp;
                      tipLeft = Math.floor(tmp);
                  }
                  // Y coord
                  var X, Y, offsetTop;
                  if (options.contextMenu) { // context menu
                      X = options.pageX + 8;
                      Y = options.pageY - 0;
                      offsetTop = options.pageY;
                  } else {
                      var offset = obj.offset() || {};
                      X = ((offset.left > 25 ? offset.left : 25) + boxLeft);
                      Y = (offset.top + w2utils.getSize(obj, 'height') + options.top + 7);
                      offsetTop = offset.top;
                  }
                  div1.css({
                      left        :  X + 'px',
                      top         :  Y + 'px',
                      'min-width' : boxWidth,
                      'min-height': (options.height ? options.height : 'auto')
                  });
                  // $(window).height() - has a problem in FF20
                  var offset = div2.offset() || {};
                  var maxHeight = window.innerHeight + $(document).scrollTop() - offset.top - 7;
                  var maxWidth  = window.innerWidth + $(document).scrollLeft() - offset.left - 7;
                  if (options.contextMenu) { // context menu
                      maxHeight = window.innerHeight + $(document).scrollTop() - options.pageY - 15;
                      maxWidth  = window.innerWidth + $(document).scrollLeft() - options.pageX;
                  }
  
                  if ((maxHeight > -50 && maxHeight < 210) || options.openAbove === true) {
                      var tipOffset;
                      // show on top
                      if (options.contextMenu) { // context menu
                          maxHeight = options.pageY - 7;
                          tipOffset = 5;
                      } else {
                          maxHeight = offset.top - $(document).scrollTop() - 7;
                          tipOffset = 24;
                      }
                      if (options.maxHeight && maxHeight > options.maxHeight) maxHeight = options.maxHeight;
                      if (h > maxHeight) {
                          overflowY = true;
                          div2.height(maxHeight).width(w).css({ 'overflow-y': 'auto' });
                          h = maxHeight;
                      }
                      div1.addClass('bottom-arrow');
                      div1.css('top', (offsetTop - h - tipOffset + options.top) + 'px');
                      div1.find('>style').html(
                          '#w2ui-overlay'+ name +':before { margin-left: '+ parseInt(tipLeft) +'px; }'+
                          '#w2ui-overlay'+ name +':after { margin-left: '+ parseInt(tipLeft) +'px; }'
                      );
                  } else {
                      // show under
                      if (options.maxHeight && maxHeight > options.maxHeight) maxHeight = options.maxHeight;
                      if (h > maxHeight) {
                          overflowY = true;
                          div2.height(maxHeight).width(w).css({ 'overflow-y': 'auto' });
                      }
                      div1.addClass('top-arrow');
                      div1.find('>style').html(
                          '#w2ui-overlay'+ name +':before { margin-left: '+ parseInt(tipLeft) +'px; }'+
                          '#w2ui-overlay'+ name +':after { margin-left: '+ parseInt(tipLeft) +'px; }'
                      );
                  }
                  // check width
                  w = div2.width();
                  maxWidth = window.innerWidth + $(document).scrollLeft() - offset.left - 7;
                  if (options.maxWidth && maxWidth > options.maxWidth) maxWidth = options.maxWidth;
                  if (w > maxWidth && options.align !== 'both') {
                      options.align = 'right';
                      setTimeout(function () { resize(); }, 1);
                  }
                  // don't show tip
                  if (options.contextMenu || options.noTip) { // context menu
                      div1.find('>style').html(
                          '#w2ui-overlay'+ name +':before { display: none; }'+
                          '#w2ui-overlay'+ name +':after { display: none; }'
                      );
                  }
                  // check scroll bar (needed to avoid horizontal scrollbar)
                  if (overflowY && options.align != 'both') div2.width(w + w2utils.scrollBarSize() + 2);
              }
              menu.css('overflow-y', 'auto');
          }
      };
  
      $.fn.w2menu = function (menu, options) {
          /*
          ITEM STRUCTURE
              item : {
                  id       : null,
                  text     : '',
                  style    : '',
                  img      : '',
                  icon     : '',
                  count    : '',
                  tooltip  : '',
                  hidden   : false,
                  checked  : null,
                  disabled : false
                  ...
              }
          */
          // if items is a function
          if (options && typeof options.items == 'function') {
              options.items = options.items();
          }
          var defaults = {
              type       : 'normal',    // can be normal, radio, check
              index      : null,        // current selected
              items      : [],
              render     : null,
              msgNoItems : 'No items',
              onSelect   : null,
              tmp        : {}
          };
          var obj  = this;
          var name = '';
          if (menu === 'refresh') {
              // if not show - call blur
              if ($('#w2ui-overlay'+ name).length > 0) {
                  options = $.extend($.fn.w2menuOptions, options);
                  var scrTop = $('#w2ui-overlay'+ name +' div.menu').scrollTop();
                  $('#w2ui-overlay'+ name +' div.menu').html(getMenuHTML());
                  $('#w2ui-overlay'+ name +' div.menu').scrollTop(scrTop);
                  mresize();
              } else {
                  $(this).w2menu(options);
              }
          } else if (menu === 'refresh-index') {
              var $menu  = $('#w2ui-overlay'+ name +' div.menu');
              var cur    = $menu.find('tr[index='+ options.index +']');
              var scrTop = $menu.scrollTop();
              $menu.find('tr.w2ui-selected').removeClass('w2ui-selected'); // clear all
              cur.addClass('w2ui-selected'); // select current
              // scroll into view
              if (cur.length > 0) {
                  var top    = cur[0].offsetTop - 5; // 5 is margin top
                  var height = $menu.height();
                  $menu.scrollTop(scrTop);
                  if (top < scrTop || top + cur.height() > scrTop + height) {
                      $menu.animate({ 'scrollTop': top - (height - cur.height() * 2) / 2 }, 200, 'linear');
                  }
              }
              mresize();
          } else {
              if (arguments.length === 1) options = menu; else options.items = menu;
              if (typeof options !== 'object') options = {};
              options = $.extend({}, defaults, options);
              $.fn.w2menuOptions = options;
              if (options.name) name = '-' + options.name;
              if (typeof options.select === 'function' && typeof options.onSelect !== 'function') options.onSelect = options.select;
              if (typeof options.onRender === 'function' && typeof options.render !== 'function') options.render = options.onRender;
              // 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 (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);
                  }
                  // 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) {
                      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') {
                     var item = options.items[index];
                     item.checked = !item.checked;
                     if (item.checked) {
                          if (options.type == 'radio') {
                             tmp.parents('table').find('.w2ui-icon')
                                 .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
                  $el.parent().find('tr').removeClass('w2ui-selected');
                  $el.addClass('w2ui-selected');
              };
              var html = '';
              if (options.search) {
                  html +=
                      '<div style="position: absolute; top: 0px; height: 40px; left: 0px; right: 0px; border-bottom: 1px solid silver; background-color: #ECECEC; padding: 8px 5px;">'+
                      '    <div class="w2ui-icon icon-search" style="position: absolute; margin-top: 4px; margin-left: 6px; width: 11px; background-position: left !important;"></div>'+
                      '    <input id="menu-search" type="text" style="width: 100%; outline: none; padding-left: 20px;" onclick="event.stopPropagation();"/>'+
                      '</div>';
                  options.style += ';background-color: #ECECEC';
                  options.index = 0;
                  for (var i = 0; i < options.items.length; i++) options.items[i].hidden = false;
              }
              html += '<div class="menu" style="position: absolute; top: '+ (options.search ? 40 : 0) + 'px; bottom: 0px; width: 100%;">' +
                          getMenuHTML() +
                      '</div>';
              var ret = $(this).w2overlay(html, options);
              setTimeout(function () {
                  $('#w2ui-overlay'+ name +' #menu-search')
                      .on('keyup', change)
                      .on('keydown', function (event) {
                          // cancel tab key
                          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;
                      $('#w2ui-overlay'+ name +' #menu-search').focus();
                  }
                  mresize();
              }, 200);
              mresize();
              return ret;
          }
          return;
  
          function mresize() {
              setTimeout(function () {
                  // show selected
                  $('#w2ui-overlay'+ name +' tr.w2ui-selected').removeClass('w2ui-selected');
                  var cur    = $('#w2ui-overlay'+ name +' tr[index='+ options.index +']');
                  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 ($('#w2ui-overlay'+ name).length > 0) $('#w2ui-overlay'+ name)[0].resize();
                  // scroll into view
                  if (cur.length > 0) {
                      var top    = cur[0].offsetTop - 5; // 5 is margin top
                      var el     = $('#w2ui-overlay'+ name +' div.menu');
                      var height = el.height();
                      $('#w2ui-overlay'+ name +' div.menu').scrollTop(scrTop);
                      if (top < scrTop || top + cur.height() > scrTop + height) {
                          $('#w2ui-overlay'+ name +' div.menu').animate({ 'scrollTop': top - (height - cur.height() * 2) / 2 }, 200, 'linear');
                      }
                  }
              }, 1);
          }
  
          function change(event) {
              var search  = this.value;
              var key     = event.keyCode;
              var cancel  = false;
              switch (key) {
                  case 13: // enter
                      $('#w2ui-overlay'+ name).remove();
                      $.fn.w2menuClick(event, options.index);
                      break;
                  case 9:  // tab
                  case 27: // escape
                      $('#w2ui-overlay'+ name).remove();
                      $.fn.w2menuClick(event, -1);
                      break;
                  case 38: // up
                      options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
                      options.index--;
                      while (options.index > 0 && options.items[options.index].hidden) options.index--;
                      if (options.index === 0 && options.items[options.index].hidden) {
                          while (options.items[options.index] && options.items[options.index].hidden) options.index++;
                      }
                      if (options.index < 0) options.index = 0;
                      cancel = true;
                      break;
                  case 40: // down
                      options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
                      options.index++;
                      while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
                      if (options.index === options.items.length-1 && options.items[options.index].hidden) {
                          while (options.items[options.index] && options.items[options.index].hidden) options.index--;
                      }
                      if (options.index >= options.items.length) options.index = options.items.length - 1;
                      cancel = true;
                      break;
              }
              // filter
              if (!cancel) {
                  var shown  = 0;
                  for (var i = 0; i < options.items.length; i++) {
                      var item = options.items[i];
                      var prefix = '';
                      var suffix = '';
                      if (['is', 'begins with'].indexOf(options.match) !== -1) prefix = '^';
                      if (['is', 'ends with'].indexOf(options.match) !== -1) suffix = '$';
                      try {
                          var re = new RegExp(prefix + search + suffix, 'i');
                          if (re.test(item.text) || item.text === '...') item.hidden = false; else item.hidden = true;
                      } catch (e) {}
                      // do not show selected items
                      if (obj.type === 'enum' && $.inArray(item.id, ids) !== -1) item.hidden = true;
                      if (item.hidden !== true) shown++;
                  }
                  options.index = 0;
                  while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
                  if (shown <= 0) options.index = -1;
              }
              $(obj).w2menu('refresh', options);
              mresize();
          }
  
          function getMenuHTML() {
              if (options.spinner) {
                  return  '<table class="w2ui-drop-menu"><tbody><tr><td style="padding: 5px 10px 10px 10px; text-align: center">'+
                          '    <div class="w2ui-spinner" style="width: 18px; height: 18px; position: relative; top: 5px;"></div> '+
                          '    <div style="display: inline-block; padding: 3px; color: #999;">'+ w2utils.lang('Loading...') +'</div>'+
                          '</td></tr></tbody></table>';
              }
              var count        = 0;
              var menu_html    = '<table cellspacing="0" cellpadding="0" class="w2ui-drop-menu"><tbody>';
              var img = null, icon = null;
              for (var f = 0; f < options.items.length; f++) {
                  var mitem = options.items[f];
                  if (typeof mitem === 'string') {
                      mitem = { id: mitem, text: mitem };
                  } else {
                      if (mitem.text != null && mitem.id == null) mitem.id = mitem.text;
                      if (mitem.text == null && mitem.id != null) mitem.text = mitem.id;
                      if (mitem.caption != null) mitem.text = mitem.caption;
                      img  = mitem.img;
                      icon = mitem.icon;
                      if (img  == null) img  = null; // img might be undefined
                      if (icon == null) icon = null; // icon might be undefined
                  }
                  if (['radio', 'check'].indexOf(options.type) != -1) {
                      if (mitem.checked === true) icon = 'w2ui-icon-check'; else icon = 'w2ui-icon-empty';
                  }
                  if (mitem.hidden !== true) {
                      var imgd = '';
                      var txt = mitem.text;
                      if (typeof options.render === 'function') txt = options.render(mitem, options);
                      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))) {
                          var bg = (count % 2 === 0 ? 'w2ui-item-even' : 'w2ui-item-odd');
                          if (options.altRows !== true) bg = '';
                          var colspan = 1;
                          if (imgd === '') colspan++;
                          if (mitem.count == null && mitem.hotkey == null) colspan++;
                          if (mitem.tooltip == null && mitem.hint != null) mitem.tooltip = mitem.hint; // for backward compatibility
                          menu_html +=
                              '<tr index="'+ f + '" style="'+ (mitem.style ? mitem.style : '') +'" '+ (mitem.tooltip ? 'title="'+ w2utils.lang(mitem.tooltip) +'"' : '') +
                              '        class="'+ bg +' '+ (options.index === f ? 'w2ui-selected' : '') + ' ' + (mitem.disabled === true ? 'w2ui-disabled' : '') +'"'+
                              '        onmousedown="if ('+ (mitem.disabled === true ? 'true' : 'false') + ') return;'+
                              '               jQuery.fn.w2menuDown(event, \''+ f +'\');"'+
                              '        onclick="event.stopPropagation(); '+
                              '               if ('+ (mitem.disabled === true ? 'true' : 'false') + ') return;'+
                              '               jQuery.fn.w2menuClick(event, \''+ f +'\');">'+
                                  imgd +
                              '   <td class="menu-text" colspan="'+ colspan +'">'+ w2utils.lang(txt) +'</td>'+
                              '   <td class="menu-count">'+
                                      (mitem.count != null ? '<span>' + mitem.count + '</span>' : '') +
                                      (mitem.hotkey != null ? '<span class="hotkey">' + mitem.hotkey + '</span>' : '') +
                              '</td>' +
                              '</tr>';
                          count++;
                      } else {
                          // horizontal line
                          menu_html += '<tr><td colspan="3" style="padding: 6px; pointer-events: none"><div style="border-top: 1px solid silver;"></div></td></tr>';
                      }
                  }
                  options.items[f] = mitem;
              }
              if (count === 0) {
                  menu_html += '<tr><td style="padding: 13px; color: #999; text-align: center">'+ options.msgNoItems +'</div></td></tr>';
              }
              menu_html += "</tbody></table>";
              return menu_html;
          }
      };
  
      $.fn.w2color = function (options, callBack) {
          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', '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']
                  // ['660205', '783F0B', '7F6011', '274E12', '0C343D', '063762', '20124D', '4C1030'],
                  // ['F2F2F2', 'F2F2F2', 'F2F2F2', 'F2F2F2', 'F2F2F2'] // custom colors (up to 4)
              ];
          }
          var pal = $.fn.w2colorPalette;
          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] == '333333') {
              pal[0].splice(1, 1);
              pal[0].push('');
          }
          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 (options.color.substr(0,1) == '#') options.color = options.color.substr(1);
  
          if ($('#w2ui-overlay').length === 0) {
              $(el).w2overlay(getColorHTML(options), options);
          } else { // only refresh contents
              $('#w2ui-overlay .w2ui-color').parent().html(getColorHTML(options));
          }
          // bind events
          $('#w2ui-overlay .color')
              .off('.w2color')
              .on('mousedown.w2color', function (event) {
                  var color = '#' + $(event.originalEvent.target).attr('name');
                  if (color == '#') color = '';
                  index = $(event.originalEvent.target).attr('index').split(':');
                  if (el.tagName == 'INPUT') {
                      $(el).val(color).data('skipInit', true).change();
                      $(el).next().find('>div').css('background-color', color);
                  } else {
                      $(el).data('_color', color);
                      if (typeof options.onSelect == 'function') options.onSelect($(el).data('_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) {
                  $('#w2ui-overlay').data('keepOpen', true);
                  setTimeout(function () { $('#w2ui-overlay').data('keepOpen', true); }, 10);
                  event.stopPropagation();
              })
              .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;
                  }
              });
              if (!silent) {
                  if (el.tagName == 'INPUT') {
                      $(el).val(newColor).data('skipInit', true).change();
                      $(el).next().find('>div').css('background-color', newColor);
                  } else {
                      $(el).data('_color', newColor);
                      if (typeof options.onSelect == 'function') options.onSelect($(el).data('_color'));
                  }
              } 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 x = parseInt($el.css('left')) + offset;
              var y = parseInt($el.css('top')) + offset;
              var name = $el.parent().attr('name');
              // console.log(name, x, y, Math.round(x / $el.parent().width() * 100), Math.round(100 - (y / $el.parent().height() * 100)));
              if (name == 'palette') {
                  setColor({
                      s: Math.round(x / $el.parent().width() * 100),
                      v: Math.round(100 - (y / $el.parent().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();
          }
          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':
                      index[0]--;
                      break;
                  case 'down':
                      index[0]++;
                      break;
                  case 'right':
                      index[1]++;
                      break;
                  case 'left':
                      index[1]--;
                      break;
              }
              if (index[0] < 0) index[0] = 0;
              if (index[0] > pal.length - 2) index[0] = pal.length - 2;
              if (index[1] < 0) index[1] = 0;
              if (index[1] > pal[0].length - 1) index[1] = pal[0].length - 1;
  
              color = pal[index[0]][index[1]];
              $(el).data('_color', color);
              return color;
          };
  
          function getColorHTML(options) {
              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; 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] + bor +';" ' +
                              '       name="'+ pal[i][j] +'" index="'+ i + ':' + j +'">'+ (options.color == pal[i][j] ? '&#149;' : '&#160;') +
                              '    </div>'+
                              '</td>';
                      if (options.color == pal[i][j]) index = [i, j];
                  }
                  html += '</tr>';
                  if (i < 2) html += '<tr><td style="height: 8px" colspan="8"></td></tr>';
              }
              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);
  
  /************************************************************************
  *   Library: Web 2.0 UI for jQuery (using prototypical inheritance)
  *   - Following objects defined
  *        - w2field        - various field controls
  *        - $().w2field    - jQuery wrapper
  *   - Dependencies: jQuery, w2utils
  *
  * == NICE TO HAVE ==
  *   - upload (regular files)
  *   - BUG with prefix/postfix and arrows (test in different contexts)
  *   - multiple date selection
  *   - month selection, year selections
  *   - arrows no longer work (for int)
  *   - form to support custom types
  *   - rewrite suffix and prefix positioning with translateY()
  *   - prefix and suffix are slow (100ms or so)
  *   - MultiSelect - Allow Copy/Paste for single and multi values
  *   - add routeData to list/enum
  *   - for type: list -> read value from attr('value')
  *   - ENUM, LIST: should have same as grid (limit, offset, search, sort)
  *   - ENUM, LIST: should support wild chars
  *   - add selection of predefined times (used for appointments)
  *
  ************************************************************************/
  
  (function ($) {
  
      var w2field = function (options) {
          // public properties
          this.el          = null;
          this.helpers     = {}; // object or helper elements
          this.type        = options.type || 'text';
          this.options     = $.extend(true, {}, options);
          this.onSearch    = options.onSearch    || null;
          this.onRequest   = options.onRequest   || null;
          this.onLoad      = options.onLoad      || null;
          this.onError     = options.onError     || null;
          this.onClick     = options.onClick     || null;
          this.onAdd       = options.onAdd       || null;
          this.onNew       = options.onNew       || null;
          this.onRemove    = options.onRemove    || null;
          this.onMouseOver = options.onMouseOver || null;
          this.onMouseOut  = options.onMouseOut  || null;
          this.onIconClick = options.onIconClick || null;
          this.onScroll    = options.onScroll || null;
          this.tmp         = {}; // temp object
          // clean up some options
          delete this.options.type;
          delete this.options.onSearch;
          delete this.options.onRequest;
          delete this.options.onLoad;
          delete this.options.onError;
          delete this.options.onClick;
          delete this.options.onMouseOver;
          delete this.options.onMouseOut;
          delete this.options.onIconClick;
          delete this.options.onScroll;
          // extend with defaults
          $.extend(true, this, w2obj.field);
      };
  
      // ====================================================
      // -- Registers as a jQuery plugin
  
      $.fn.w2field = function (method, options) {
          // call direct
          if (this.length === 0) {
              var pr = w2field.prototype;
              if (pr[method]) {
                  return pr[method].apply(pr, Array.prototype.slice.call(arguments, 1));
              }
          } else {
              // if without arguments - return the object
              if (arguments.length === 0) {
                  var obj = $(this).data('w2field');
                  return obj;
              }
              if (typeof method == 'string' && typeof options == 'object') {
                  method = $.extend(true, {}, options, { type: method });
              }
              if (typeof method == 'string' && options == null) {
                  method = { type: method };
              }
              method.type = String(method.type).toLowerCase();
              return this.each(function (index, el) {
                  var obj = $(el).data('w2field');
                  // if object is not defined, define it
                  if (obj == null) {
                      var obj = new w2field(method);
                      $.extend(obj, { handlers: [] });
                      if (el) obj.el = $(el)[0];
                      obj.init();
                      $(el).data('w2field', obj);
                      return obj;
                  } else { // fully re-init
                      obj.clear();
                      if (method.type == 'clear') return;
                      var obj = new w2field(method);
                      $.extend(obj, { handlers: [] });
                      if (el) obj.el = $(el)[0];
                      obj.init();
                      $(el).data('w2field', obj);
                      return obj;
                  }
                  return null;
              });
          }
      };
  
      // ====================================================
      // -- Implementation of core functionality
  
      /*     To add custom types
          $().w2field('addType', 'myType', function (options) {
              $(this.el).on('keypress', function (event) {
                  if (event.metaKey || event.ctrlKey || event.altKey
                      || (event.charCode != event.keyCode && event.keyCode > 0)) return;
                  var ch = String.fromCharCode(event.charCode);
                  if (ch != 'a' && ch != 'b' && ch != 'c') {
                      if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                      return false;
                  }
              });
              $(this.el).on('blur', function (event)  { // keyCode & charCode differ in FireFox
                  var ch = this.value;
                  if (ch != 'a' && ch != 'b' && ch != 'c') {
                      $(this).w2tag(w2utils.lang("Not a single character from the set of 'abc'"));
                  }
              });
          });
      */
  
      w2field.prototype = {
  
          custom: {},  // map of custom types
  
          addType: function (type, handler) {
              type = String(type).toLowerCase();
              this.custom[type] = handler;
              return true;
          },
  
          removeType: function (type) {
              type = String(type).toLowerCase();
              if (!this.custom[type]) return false;
              delete this.custom[type];
              return true;
          },
  
          init: function () {
              var obj     = this;
              var options = this.options;
              var defaults;
  
              // Custom Types
              if (typeof this.custom[this.type] == 'function') {
                  this.custom[this.type].call(this, options);
                  return;
              }
              // only for INPUT or TEXTAREA
              if (['INPUT', 'TEXTAREA'].indexOf(this.el.tagName.toUpperCase()) == -1) {
                  console.log('ERROR: w2field could only be applied to INPUT or TEXTAREA.', this.el);
                  return;
              }
  
              switch (this.type) {
                  case 'text':
                  case 'int':
                  case 'float':
                  case 'money':
                  case 'currency':
                  case 'percent':
                  case 'alphanumeric':
                  case 'bin':
                  case 'hex':
                      defaults = {
                          min                : null,
                          max                : null,
                          step               : 1,
                          autoFormat         : true,
                          currencyPrefix     : w2utils.settings.currencyPrefix,
                          currencySuffix     : w2utils.settings.currencySuffix,
                          currencyPrecision  : w2utils.settings.currencyPrecision,
                          decimalSymbol      : w2utils.settings.decimalSymbol,
                          groupSymbol        : w2utils.settings.groupSymbol,
                          arrows             : false,
                          keyboard           : true,
                          precision          : null,
                          silent             : true,
                          prefix             : '',
                          suffix             : ''
                      };
                      this.options = $.extend(true, {}, defaults, options);
                      options = this.options; // since object is re-created, need to re-assign
                      options.numberRE  = new RegExp('['+ options.groupSymbol + ']', 'g');
                      options.moneyRE   = new RegExp('['+ options.currencyPrefix + options.currencySuffix + options.groupSymbol +']', 'g');
                      options.percentRE = new RegExp('['+ options.groupSymbol + '%]', 'g');
                      // no keyboard support needed
                      if (['text', 'alphanumeric', 'hex', 'bin'].indexOf(this.type) != -1) {
                          options.arrows   = false;
                          options.keyboard = false;
                      }
                      this.addPrefix(); // only will add if needed
                      this.addSuffix();
                      break;
  
                  case 'color':
                      defaults = {
                          prefix      : '',
                          suffix      : '<div style="width: '+ (parseInt($(this.el).css('font-size')) || 12) +'px">&#160;</div>',
                          arrows      : false,
                          keyboard    : false,
                          advanced    : null, // open advanced by default
                          transparent : true
                      };
                      $.extend(options, defaults);
                      this.addPrefix();    // only will add if needed
                      this.addSuffix();    // only will add if needed
                      // additional checks
                      if ($(this.el).val() !== '') setTimeout(function () { obj.change(); }, 1);
                      break;
  
                  case 'date':
                      defaults = {
                          format       : w2utils.settings.dateFormat, // date format
                          keyboard     : true,
                          silent       : true,
                          start        : '',       // string or jquery object
                          end          : '',       // string or jquery object
                          blocked      : {},       // { '4/11/2011': 'yes' }
                          colored      : {},        // { '4/11/2011': 'red:white' }
                          blockWeekDays : null       // array of numbers of weekday to block
                      };
                      this.options = $.extend(true, {}, defaults, options);
                      options = this.options; // since object is re-created, need to re-assign
                      if ($(this.el).attr('placeholder') == null) $(this.el).attr('placeholder', options.format);
                      break;
  
                  case 'time':
                      defaults = {
                          format    : w2utils.settings.timeFormat,
                          keyboard  : true,
                          silent    : true,
                          start     : '',
                          end       : '',
                          noMinutes : false
                      };
                      this.options = $.extend(true, {}, defaults, options);
                      options = this.options; // since object is re-created, need to re-assign
                      if ($(this.el).attr('placeholder') == null) $(this.el).attr('placeholder', options.format);
                      break;
  
                  case 'datetime':
                      defaults = {
                          format      : w2utils.settings.dateFormat + ' | ' + w2utils.settings.timeFormat,
                          keyboard    : true,
                          silent      : true,
                          start       : '',       // string or jquery object or Date object
                          end         : '',       // string or jquery object or Date object
                          blocked     : [],       // [ '4/11/2011', '4/12/2011' ] or [ new Date(2011, 4, 11), new Date(2011, 4, 12) ]
                          colored     : {},       // { '12/17/2014': 'blue:green', '12/18/2014': 'gray:white'  }; // key has to be formatted with w2utils.settings.dateFormat
                          placeholder : null,     // optional. will fall back to this.format if not specified. Only used if this.el has no placeholder attribute.
                          btn_now     : true,     // show/hide the use-current-date-and-time button
                          noMinutes   : false
                      };
                      this.options = $.extend(true, {}, defaults, options);
                      options = this.options; // since object is re-created, need to re-assign
                      if ($(this.el).attr('placeholder') == null) $(this.el).attr('placeholder', options.placeholder || options.format);
                      break;
  
                  case 'list':
                  case 'combo':
                      defaults = {
                          items           : [],
                          selected        : {},
                          url             : null,          // url to pull data from
                          recId           : null,          // map retrieved data from url to id, can be string or function
                          recText         : null,          // map retrieved data from url to text, can be string or function
                          method          : null,          // default comes from w2utils.settings.dataType
                          interval        : 350,           // number of ms to wait before sending server call on search
                          postData        : {},
                          minLength       : 1,            // min number of chars when trigger search
                          cacheMax        : 250,
                          maxDropHeight   : 350,          // max height for drop down menu
                          maxDropWidth    : null,         // if null then auto set
                          match           : 'begins',     // ['contains', 'is', 'begins', 'ends']
                          silent          : true,
                          icon            : null,
                          iconStyle       : '',
                          onSearch        : null,         // when search needs to be performed
                          onRequest       : null,         // when request is submitted
                          onLoad          : null,         // when data is received
                          onError         : null,         // when data fails to load due to server error or other failure modes
                          onIconClick     : null,
                          renderDrop      : null,         // render function for drop down item
                          compare         : null,         // compare function for filtering
                          filter          : true,         // weather to filter at all
                          prefix          : '',
                          suffix          : '',
                          openOnFocus     : false,        // if to show overlay onclick or when typing
                          markSearch      : false
                      };
                      options.items = this.normMenu(options.items); // need to be first
                      if (this.type == 'list') {
                          // defaults.search = (options.items && options.items.length >= 10 ? true : false);
                          defaults.openOnFocus = true;
                          $(this.el).addClass('w2ui-select');
                          // if simple value - look it up
                          if (!$.isPlainObject(options.selected) && options.items) {
                              for (var i = 0; i< options.items.length; i++) {
                                  var item = options.items[i];
                                  if (item && item.id == options.selected) {
                                      options.selected = $.extend(true, {}, item);
                                      break;
                                  }
                              }
                          }
                          this.watchSize();
                      }
                      options = $.extend({}, defaults, options, {
                          align   : 'both',      // same width as control
                          altRows : true         // alternate row color
                      });
                      this.options = options;
                      if (!$.isPlainObject(options.selected)) options.selected = {};
                      $(this.el).data('selected', options.selected);
                      if (options.url) {
                          options.items = [];
                          this.request(0);
                      }
                      if (this.type == 'list') this.addFocus();
                      this.addPrefix();
                      this.addSuffix();
                      setTimeout(function () { obj.refresh(); }, 10); // need this for icon refresh
                      $(this.el).attr('autocomplete', 'off');
                      if (options.selected.text != null) $(this.el).val(options.selected.text);
                      break;
  
                  case 'enum':
                      defaults = {
                          items           : [],
                          selected        : [],
                          max             : 0,             // max number of selected items, 0 - unlim
                          url             : null,          // not implemented
                          recId           : null,          // map retrieved data from url to id, can be string or function
                          recText         : null,          // map retrieved data from url to text, can be string or function
                          interval        : 350,           // number of ms to wait before sending server call on search
                          method          : null,          // default comes from w2utils.settings.dataType
                          postData        : {},
                          minLength       : 1,            // min number of chars when trigger search
                          cacheMax        : 250,
                          maxWidth        : 250,           // max width for a single item
                          maxHeight       : 350,           // max height for input control to grow
                          maxDropHeight   : 350,           // max height for drop down menu
                          maxDropWidth    : null,          // if null then auto set
                          match           : 'contains',    // ['contains', 'is', 'begins', 'ends']
                          silent          : true,
                          openOnFocus     : false,         // if to show overlay onclick or when typing
                          markSearch      : true,
                          renderDrop      : null,          // render function for drop down item
                          renderItem      : null,          // render selected item
                          compare         : null,          // compare function for filtering
                          filter          : true,          // alias for compare
                          style           : '',            // style for container div
                          onSearch        : null,          // when search needs to be performed
                          onRequest       : null,          // when request is submitted
                          onLoad          : null,          // when data is received
                          onError         : null,          // when data fails to load due to server error or other failure modes
                          onClick         : null,          // when an item is clicked
                          onAdd           : null,          // when an item is added
                          onNew           : null,          // when new item should be added
                          onRemove        : null,          // when an item is removed
                          onMouseOver     : null,          // when an item is mouse over
                          onMouseOut      : null,          // when an item is mouse out
                          onScroll        : null           // when div with selected items is scrolled
                      };
                      options = $.extend({}, defaults, options, {
                          align    : 'both',    // same width as control
                          suffix   : '',
                          altRows  : true       // alternate row color
                      });
                      options.items    = this.normMenu(options.items);
                      options.selected = this.normMenu(options.selected);
                      this.options = options;
                      if (!$.isArray(options.selected)) options.selected = [];
                      $(this.el).data('selected', options.selected);
                      if (options.url) {
                          options.items = [];
                          this.request(0);
                      }
                      this.addSuffix();
                      this.addMulti();
                      this.watchSize();
                      break;
  
                  case 'file':
                      defaults = {
                          selected      : [],
                          max           : 0,
                          maxSize       : 0,        // max size of all files, 0 - unlim
                          maxFileSize   : 0,        // max size of a single file, 0 -unlim
                          maxWidth      : 250,      // max width for a single item
                          maxHeight     : 350,      // max height for input control to grow
                          maxDropHeight : 350,      // max height for drop down menu
                          maxDropWidth  : null,     // if null then auto set
                          readContent   : true,     // if true, it will readAsDataURL content of the file
                          silent        : true,
                          renderItem    : null,     // render selected item
                          style         : '',       // style for container div
                          onClick       : null,     // when an item is clicked
                          onAdd         : null,     // when an item is added
                          onRemove      : null,     // when an item is removed
                          onMouseOver   : null,     // when an item is mouse over
                          onMouseOut    : null      // when an item is mouse out
                      };
                      options = $.extend({}, defaults, options, {
                          align         : 'both',   // same width as control
                          altRows        : true     // alternate row color
                      });
                      this.options = options;
                      if (!$.isArray(options.selected)) options.selected = [];
                      $(this.el).data('selected', options.selected);
                      if ($(this.el).attr('placeholder') == null) {
                          $(this.el).attr('placeholder', w2utils.lang('Attach files by dragging and dropping or Click to Select'));
                      }
                      this.addMulti();
                      this.watchSize();
                      break;
              }
              // attach events
              this.tmp = {
                  onChange    : function (event) { obj.change.call(obj, event); },
                  onClick     : function (event) { obj.click.call(obj, event); },
                  onFocus     : function (event) { obj.focus.call(obj, event); },
                  onBlur      : function (event) { obj.blur.call(obj, event); },
                  onKeydown   : function (event) { obj.keyDown.call(obj, event); },
                  onKeyup     : function (event) { obj.keyUp.call(obj, event); },
                  onKeypress  : function (event) { obj.keyPress.call(obj, event); }
              };
              $(this.el)
                  .addClass('w2field w2ui-input')
                  .data('w2field', this)
                  .on('change.w2field',   this.tmp.onChange)
                  .on('click.w2field',    this.tmp.onClick)         // ignore click because it messes overlays
                  .on('focus.w2field',    this.tmp.onFocus)
                  .on('blur.w2field',     this.tmp.onBlur)
                  .on('keydown.w2field',  this.tmp.onKeydown)
                  .on('keyup.w2field',    this.tmp.onKeyup)
                  .on('keypress.w2field', this.tmp.onKeypress)
                  .css(w2utils.cssPrefix('box-sizing', 'border-box'));
              // format initial value
              this.change($.Event('change'));
          },
  
          watchSize: function () {
              var obj = this;
              var tmp = $(obj.el).data('tmp') || {};
              tmp.sizeTimer = setInterval(function () {
                  if ($(obj.el).parents('body').length > 0) {
                      obj.resize();
                  } else {
                      clearInterval(tmp.sizeTimer);
                  }
              }, 200);
              $(obj.el).data('tmp', tmp);
          },
  
          get: function () {
              var ret;
              if (['list', 'enum', 'file'].indexOf(this.type) != -1) {
                  ret = $(this.el).data('selected');
              } else {
                  ret = $(this.el).val();
              }
              return ret;
          },
  
          set: function (val, append) {
              if (['list', 'enum', 'file'].indexOf(this.type) != -1) {
                  if (this.type != 'list' && append) {
                      if ($(this.el).data('selected') == null) $(this.el).data('selected', []);
                      $(this.el).data('selected').push(val);
                      $(this.el).change();
                  } else {
                      var it = (this.type == 'enum' ? [val] : val);
                      $(this.el).data('selected', it).change();
                  }
                  this.refresh();
              } else {
                  $(this.el).val(val);
              }
          },
  
          setIndex: function (ind, append) {
              if (['list', 'enum'].indexOf(this.type) != -1) {
                  var items = this.options.items;
                  if (items && items[ind]) {
                      if (this.type != 'list' && append) {
                          if ($(this.el).data('selected') == null) $(this.el).data('selected', []);
                          $(this.el).data('selected').push(items[ind]);
                          $(this.el).change();
                      } else {
                          var it = (this.type == 'enum' ? [items[ind]] : items[ind]);
                          $(this.el).data('selected', it).change();
                      }
                      this.refresh();
                      return true;
                  }
              }
              return false;
          },
  
          clear: function () {
              var obj        = this;
              var options    = this.options;
              // if money then clear value
              if (['money', 'currency'].indexOf(this.type) != -1) {
                  $(this.el).val($(this.el).val().replace(options.moneyRE, ''));
              }
              if (this.type == 'percent') {
                  $(this.el).val($(this.el).val().replace(/%/g, ''));
              }
              if (this.type == 'list') {
                  $(this.el).removeClass('w2ui-select');
              }
              this.type = 'clear';
              var tmp = $(this.el).data('tmp');
              if (!this.tmp) return;
              // restore paddings
              if (tmp != null) {
                  $(this.el).height('auto');
                  if (tmp && tmp['old-padding-left'])  $(this.el).css('padding-left',  tmp['old-padding-left']);
                  if (tmp && tmp['old-padding-right']) $(this.el).css('padding-right', tmp['old-padding-right']);
                  if (tmp && tmp['old-background-color']) $(this.el).css('background-color', tmp['old-background-color']);
                  if (tmp && tmp['old-border-color']) $(this.el).css('border-color', tmp['old-border-color']);
                  // remove resize watcher
                  clearInterval(tmp.sizeTimer);
              }
              // remove events and (data)
              $(this.el)
                  .val(this.clean($(this.el).val()))
                  .removeClass('w2field')
                  .removeData()       // removes all attached data
                  .off('.w2field');   // remove only events added by w2field
              // remove helpers
              for (var h in this.helpers) $(this.helpers[h]).remove();
              this.helpers = {};
          },
  
          refresh: function () {
              var obj       = this;
              var options   = this.options;
              var selected  = $(this.el).data('selected');
              var time      = (new Date()).getTime();
              // enum
              if (['list'].indexOf(this.type) != -1) {
                  $(obj.el).parent().css('white-space', 'nowrap'); // needs this for arrow always to appear on the right side
                  // hide focus and show text
                  if (obj.helpers.prefix) obj.helpers.prefix.hide();
                  setTimeout(function () {
                      if (!obj.helpers.focus) return;
                      // if empty show no icon
                      if (!$.isEmptyObject(selected) && options.icon) {
                          options.prefix = '<span class="w2ui-icon '+ options.icon +'"style="cursor: pointer; font-size: 14px;' +
                                           ' display: inline-block; margin-top: -1px; color: #7F98AD;'+ options.iconStyle +'">'+
                              '</span>';
                          obj.addPrefix();
                      } else {
                          options.prefix = '';
                          obj.addPrefix();
                      }
                      // focus helper
                      var focus = obj.helpers.focus.find('input');
                      if ($(focus).val() === '') {
                          $(focus).css('text-indent', '-9999em').prev().css('opacity', 0);
                          $(obj.el).val(selected && selected.text != null ? w2utils.lang(selected.text) : '');
                      } else {
                          $(focus).css('text-indent', 0).prev().css('opacity', 1);
                          $(obj.el).val('');
                          setTimeout(function () {
                              if (obj.helpers.prefix) obj.helpers.prefix.hide();
                              var tmp = 'position: absolute; opacity: 0; margin: 4px 0px 0px 2px; background-position: left !important;';
                              if (options.icon) {
                                  $(focus).css('margin-left', '17px');
                                  $(obj.helpers.focus).find('.icon-search').attr('style', tmp + 'width: 11px !important; opacity: 1; display: block');
                              } else {
                                  $(focus).css('margin-left', '0px');
                                  $(obj.helpers.focus).find('.icon-search').attr('style', tmp + 'width: 0px !important; opacity: 0; display: none');
                              }
                          }, 1);
                      }
                      // if readonly or disabled
                      if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) {
                          setTimeout(function () {
                              $(obj.helpers.prefix).css('opacity', '0.6');
                              $(obj.helpers.suffix).css('opacity', '0.6');
                          }, 1);
                      } else {
                          setTimeout(function () {
                              $(obj.helpers.prefix).css('opacity', '1');
                              $(obj.helpers.suffix).css('opacity', '1');
                          }, 1);
                      }
                  }, 1);
              }
              if (['enum', 'file'].indexOf(this.type) != -1) {
                  var html = '';
                  if (selected) {
                     for (var s = 0; s < selected.length; s++) {
                         var it  = selected[s];
                         var ren = '';
                         if (typeof options.renderItem == 'function') {
                             ren = options.renderItem(it, s, '<div class="w2ui-list-remove" title="'+ w2utils.lang('Remove') +'" index="'+ s +'">&#160;&#160;</div>');
                         } else {
                             ren = '<div class="w2ui-list-remove" title="'+ w2utils.lang('Remove') +'" index="'+ s +'">&#160;&#160;</div>'+
                                   (obj.type == 'enum' ? it.text : it.name + '<span class="file-size"> - '+ w2utils.formatSize(it.size) +'</span>');
                         }
                         html += '<li index="'+ s +'" style="max-width: '+ parseInt(options.maxWidth) + 'px; '+ (it.style ? it.style : '') +'">'+
                                 ren +'</li>';
                     }
                  }
                  var div = obj.helpers.multi;
                  var ul  = div.find('ul');
                  div.attr('style', div.attr('style') + ';' + options.style);
                  $(obj.el).css('z-index', '-1');
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) {
                      setTimeout(function () {
                          div[0].scrollTop = 0; // scroll to the top
                          div.addClass('w2ui-readonly')
                              .find('li').css('opacity', '0.9')
                              .parent().find('li.nomouse').hide()
                              .find('input').prop('readonly', true)
                              .parents('ul')
                              .find('.w2ui-list-remove').hide();
                      }, 1);
                  } else {
                      setTimeout(function () {
                          div.removeClass('w2ui-readonly')
                              .find('li').css('opacity', '1')
                              .parent().find('li.nomouse').show()
                              .find('input').prop('readonly', false)
                              .parents('ul')
                              .find('.w2ui-list-remove').show();
                      }, 1);
                  }
  
                  // clean
                  div.find('.w2ui-enum-placeholder').remove();
                  ul.find('li').not('li.nomouse').remove();
                  // add new list
                  if (html !== '') {
                      ul.prepend(html);
                  } else if ($(obj.el).attr('placeholder') != null && div.find('input').val() === '') {
                      var style =
                          'padding-top: ' + $(this.el).css('padding-top') + ';'+
                          'padding-left: ' + $(this.el).css('padding-left') + '; ' +
                          'box-sizing: ' + $(this.el).css('box-sizing') + '; ' +
                          'line-height: ' + $(this.el).css('line-height') + '; ' +
                          'font-size: ' + $(this.el).css('font-size') + '; ' +
                          'font-family: ' + $(this.el).css('font-family') + '; ';
                      div.prepend('<div class="w2ui-enum-placeholder" style="'+ style +'">'+ $(obj.el).attr('placeholder') +'</div>');
                  }
                  // ITEMS events
                  div.off('scroll.w2field').on('scroll.w2field', function (event) {
                          var edata = obj.trigger({ phase: 'before', type: 'scroll', target: obj.el, originalEvent: event });
                          if (edata.isCancelled === true) return;
                          // event after
                          obj.trigger($.extend(edata, { phase: 'after' }));
                      })
                      .find('li')
                      .data('mouse', 'out')
                      .on('click', function (event) {
                          var target = (event.target.tagName.toUpperCase() == 'LI' ? event.target : $(event.target).parents('LI'));
                          var item   = selected[$(target).attr('index')];
                          if ($(target).hasClass('nomouse')) return;
                          event.stopPropagation();
                          // default behavior
                          if ($(event.target).hasClass('w2ui-list-remove')) {
                              if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                              // trigger event
                              var edata = obj.trigger({ phase: 'before', type: 'remove', target: obj.el, originalEvent: event.originalEvent, item: item });
                              if (edata.isCancelled === true) return;
                              // default behavior
                              $().w2overlay();
                              selected.splice($(event.target).attr('index'), 1);
                              $(obj.el).trigger('change');
                              $(event.target).parent().fadeOut('fast');
                              setTimeout(function () {
                                  obj.refresh();
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              }, 300);
                          } else {
                              // trigger event
                              var edata = obj.trigger({ phase: 'before', type: 'click', target: obj.el, originalEvent: event.originalEvent, item: item });
                              if (edata.isCancelled === true) return;
                              // if file - show image preview
                              if (obj.type == 'file') {
                                  var preview = '';
                                  if ((/image/i).test(item.type)) { // image
                                      preview = '<div style="padding: 3px;">'+
                                          '    <img src="'+ (item.content ? 'data:'+ item.type +';base64,'+ item.content : '') +'" style="max-width: 300px;" '+
                                          '        onload="var w = jQuery(this).width(); var h = jQuery(this).height(); '+
                                          '            if (w < 300 & h < 300) return; '+
                                          '            if (w >= h && w > 300) jQuery(this).width(300);'+
                                          '            if (w < h && h > 300) jQuery(this).height(300);"'+
                                          '        onerror="this.style.display = \'none\'"'+
                                          '    >'+
                                          '</div>';
                                  }
                                  var td1 = 'style="padding: 3px; text-align: right; color: #777;"';
                                  var td2 = 'style="padding: 3px"';
                                  preview += '<div style="padding: 8px;">'+
                                      '    <table cellpadding="2"><tbody>'+
                                      '    <tr><td '+ td1 +'>'+ w2utils.lang('Name') +':</td><td '+ td2 +'>'+ item.name +'</td></tr>'+
                                      '    <tr><td '+ td1 +'>'+ w2utils.lang('Size') +':</td><td '+ td2 +'>'+ w2utils.formatSize(item.size) +'</td></tr>'+
                                      '    <tr><td '+ td1 +'>'+ w2utils.lang('Type') +':</td><td '+ td2 +'>' +
                                      '        <span style="width: 200px; display: block-inline; overflow: hidden; text-overflow: ellipsis; white-space: nowrap="nowrap";">'+ item.type +'</span>'+
                                      '    </td></tr>'+
                                      '    <tr><td '+ td1 +'>'+ w2utils.lang('Modified') +':</td><td '+ td2 +'>'+ w2utils.date(item.modified) +'</td></tr>'+
                                      '    </tbody></table>'+
                                      '</div>';
                                  $('#w2ui-overlay').remove();
                                  $(target).w2overlay(preview);
                              }                            // event after
                              obj.trigger($.extend(edata, { phase: 'after' }));
                          }
                      })
                      .on('mouseover', function (event) {
                          var target = (event.target.tagName.toUpperCase() == 'LI' ? event.target : $(event.target).parents('LI'));
                          if ($(target).hasClass('nomouse')) return;
                          if ($(target).data('mouse') == 'out') {
                              var item = selected[$(event.target).attr('index')];
                              // trigger event
                              var edata = obj.trigger({ phase: 'before', type: 'mouseOver', target: obj.el, originalEvent: event.originalEvent, item: item });
                              if (edata.isCancelled === true) return;
                              // event after
                              obj.trigger($.extend(edata, { phase: 'after' }));
                          }
                          $(target).data('mouse', 'over');
                      })
                      .on('mouseout', function (event) {
                          var target = (event.target.tagName.toUpperCase() == 'LI' ? event.target : $(event.target).parents('LI'));
                          if ($(target).hasClass('nomouse')) return;
                          $(target).data('mouse', 'leaving');
                          setTimeout(function () {
                              if ($(target).data('mouse') == 'leaving') {
                                  $(target).data('mouse', 'out');
                                  var item = selected[$(event.target).attr('index')];
                                  // trigger event
                                  var edata = obj.trigger({ phase: 'before', type: 'mouseOut', target: obj.el, originalEvent: event.originalEvent, item: item });
                                  if (edata.isCancelled === true) return;
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              }
                          }, 0);
                      });
                  // adjust height
                  $(this.el).height('auto');
                  var cntHeight = $(div).find('> div.w2ui-multi-items').height() + w2utils.getSize(div, '+height') * 2;
                  if (cntHeight < 26) cntHeight = 26;
                  if (cntHeight > options.maxHeight) cntHeight = options.maxHeight;
                  if (div.length > 0) div[0].scrollTop = 1000;
                  var inpHeight = w2utils.getSize($(this.el), 'height') - 2;
                  if (inpHeight > cntHeight) cntHeight = inpHeight;
                  $(div).css({ 'height': cntHeight + 'px', overflow: (cntHeight == options.maxHeight ? 'auto' : 'hidden') });
                  if (cntHeight < options.maxHeight) $(div).prop('scrollTop', 0);
                  $(this.el).css({ 'height' : (cntHeight + 2) + 'px' });
                  // update size
                  if (obj.type == 'enum') {
                      var tmp = obj.helpers.multi.find('input');
                      tmp.width(((tmp.val().length + 2) * 8) + 'px');
                  }
              }
              return (new Date()).getTime() - time;
          },
  
          reset: function () {
              var type = this.type;
              this.clear();
              this.type = type;
              this.init();
          },
  
          // resizing width of list, enum, file controls
          resize: function () {
              var obj = this;
              var new_width  = $(obj.el).width();
              var new_height = $(obj.el).height();
              if (obj.tmp.current_width == new_width && new_height > 0) return;
  
              var focus  = this.helpers.focus;
              var multi  = this.helpers.multi;
              var suffix = this.helpers.suffix;
              var prefix = this.helpers.prefix;
  
              // resize helpers
              if (focus) {
                  focus.width($(obj.el).width());
              }
              if (multi) {
                  var width = (w2utils.getSize(obj.el, 'width')
                      - parseInt($(obj.el).css('margin-left'), 10)
                      - parseInt($(obj.el).css('margin-right'), 10));
                  $(multi).width(width);
              }
              if (suffix) {
                  obj.options.suffix = '<div class="arrow-down" style="margin-top: '+ ((parseInt($(obj.el).height()) - 6) / 2) +'px;"></div>';
                  obj.addSuffix();
              }
              if (prefix) {
                  obj.addPrefix();
              }
              // remember width
              obj.tmp.current_width = new_width;
          },
  
          clean: function (val) {
              //issue #499
              if(typeof val == 'number'){
                   return val;
              }
              var options = this.options;
              val = String(val).trim();
              // clean
              if (['int', 'float', 'money', 'currency', 'percent'].indexOf(this.type) != -1) {
                  if (typeof val == 'string') {
                      if (options.autoFormat && ['money', 'currency'].indexOf(this.type) != -1) val = String(val).replace(options.moneyRE, '');
                      if (options.autoFormat && this.type == 'percent') val = String(val).replace(options.percentRE, '');
                      if (options.autoFormat && ['int', 'float'].indexOf(this.type) != -1) val = String(val).replace(options.numberRE, '');
                      val = val.replace(/\s+/g, '').replace(w2utils.settings.groupSymbol, '').replace(w2utils.settings.decimalSymbol, '.');
                  }
                  if (parseFloat(val) == val) {
                      if (options.min != null && val < options.min) { val = options.min; $(this.el).val(options.min); }
                      if (options.max != null && val > options.max) { val = options.max; $(this.el).val(options.max); }
                  }
                  if (val !== '' && w2utils.isFloat(val)) val = Number(val); else val = '';
              }
              return val;
          },
  
          format: function (val) {
              var options = this.options;
              // autoformat numbers or money
              if (options.autoFormat && val !== '') {
                  switch (this.type) {
                      case 'money':
                      case 'currency':
                          val = w2utils.formatNumber(val, options.currencyPrecision, options.groupSymbol);
                          if (val !== '') val = options.currencyPrefix + val + options.currencySuffix;
                          break;
                      case 'percent':
                          val = w2utils.formatNumber(val, options.precision, options.groupSymbol);
                          if (val !== '') val += '%';
                          break;
                      case 'float':
                          val = w2utils.formatNumber(val, options.precision, options.groupSymbol);
                          break;
                      case 'int':
                          val = w2utils.formatNumber(val, 0, options.groupSymbol);
                          break;
                  }
              }
              return val;
          },
  
          change: function (event) {
              var obj     = this;
              var options = obj.options;
              // numeric
              if (['int', 'float', 'money', 'currency', 'percent'].indexOf(this.type) != -1) {
                  // check max/min
                  var val     =  $(this.el).val();
                  var new_val = this.format(this.clean($(this.el).val()));
                  // if was modified
                  if (val !== '' && val != new_val) {
                      $(this.el).val(new_val).change();
                      // cancel event
                      event.stopPropagation();
                      event.preventDefault();
                      return false;
                  }
              }
              // color
              if (this.type == 'color') {
                  var color = '#' + $(this.el).val();
                  if ($(this.el).val().length != 6 && $(this.el).val().length != 3) color = '';
                  $(this.el).next().find('div').css('background-color', color);
                  if ($(obj.el).is(':focus')) this.updateOverlay();
              }
              // list, enum
              if (['list', 'enum', 'file'].indexOf(this.type) != -1) {
                  obj.refresh();
                  // need time out to show icon indent properly
                  setTimeout(function () { obj.refresh(); }, 5);
              }
              // date, time
              if (['date', 'time', 'datetime'].indexOf(this.type) != -1) {
                  // convert linux timestamps
                  var tmp = parseInt(obj.el.value);
                  if (w2utils.isInt(obj.el.value) && tmp > 3000) {
                      if (this.type == 'time') $(obj.el).val(w2utils.formatTime(new Date(tmp), options.format)).change();
                      if (this.type == 'date') $(obj.el).val(w2utils.formatDate(new Date(tmp), options.format)).change();
                      if (this.type == 'datetime') $(obj.el).val(w2utils.formatDateTime(new Date(tmp), options.format)).change();
                  }
              }
          },
  
          click: function (event) {
              event.stopPropagation();
              // lists
              if (['list', 'combo', 'enum'].indexOf(this.type) != -1) {
                  if (!$(this.el).is(':focus')) this.focus(event);
              }
              // other fields with drops
              if (['date', 'time', 'color', 'datetime'].indexOf(this.type) != -1) {
                  this.updateOverlay();
              }
          },
  
          focus: function (event) {
              var obj     = this;
              var options = this.options;
              // color, date, time
              if (['color', 'date', 'time', 'datetime'].indexOf(obj.type) !== -1) {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                  setTimeout(function () { obj.updateOverlay(); }, 150);
              }
              // menu
              if (['list', 'combo', 'enum'].indexOf(obj.type) != -1) {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                  obj.resize();
                  setTimeout(function () {
                      if (obj.type == 'list' && $(obj.el).is(':focus')) {
                          $(obj.helpers.focus).find('input').focus();
                          return;
                      }
                      obj.search();
                      setTimeout(function () { obj.updateOverlay(); }, 1);
                  }, 1);
              }
              // file
              if (obj.type == 'file') {
                  $(obj.helpers.multi).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
              }
          },
  
          blur: function (event) {
              var obj     = this;
              var options = obj.options;
              var val     = $(obj.el).val().trim();
              // hide overlay
              if (['color', 'date', 'time', 'list', 'combo', 'enum', 'datetime'].indexOf(obj.type) != -1) {
                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
              }
              if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) != -1) {
                  if (val !== '' && !obj.checkType(val)) {
                      $(obj.el).val('').change();
                      if (options.silent === false) {
                          $(obj.el).w2tag('Not a valid number');
                          setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
                      }
                  }
              }
              // date or time
              if (['date', 'time', 'datetime'].indexOf(obj.type) != -1) {
                  // check if in range
                  if (val !== '' && !obj.inRange(obj.el.value)) {
                      $(obj.el).val('').removeData('selected').change();
                      if (options.silent === false) {
                          $(obj.el).w2tag('Not in range');
                          setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
                      }
                  } else {
                      if (obj.type == 'date' && val !== '' && !w2utils.isDate(obj.el.value, options.format)) {
                          $(obj.el).val('').removeData('selected').change();
                          if (options.silent === false) {
                              $(obj.el).w2tag('Not a valid date');
                              setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
                          }
                      }
                      else if (obj.type == 'time' && val !== '' && !w2utils.isTime(obj.el.value)) {
                          $(obj.el).val('').removeData('selected').change();
                          if (options.silent === false) {
                              $(obj.el).w2tag('Not a valid time');
                              setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
                          }
                      }
                      else if (obj.type == 'datetime' && val !== '' && !w2utils.isDateTime(obj.el.value, options.format)) {
                          $(obj.el).val('').removeData('selected').change();
                          if (options.silent === false) {
                              $(obj.el).w2tag('Not a valid date');
                              setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
                          }
                      }
                  }
              }
              // clear search input
              if (obj.type == 'enum') {
                  $(obj.helpers.multi).find('input').val('').width(20);
              }
              // file
              if (obj.type == 'file') {
                  $(obj.helpers.multi).css({ 'outline': 'none' });
              }
          },
  
          keyPress: function (event) {
              var obj     = this;
              var options = obj.options;
              // ignore wrong pressed key
              if (['int', 'float', 'money', 'currency', 'percent', 'hex', 'bin', 'color', 'alphanumeric'].indexOf(obj.type) != -1) {
                  // keyCode & charCode differ in FireFox
                  if (event.metaKey || event.ctrlKey || event.altKey || (event.charCode != event.keyCode && event.keyCode > 0)) return;
                  var ch = String.fromCharCode(event.charCode);
                  if (!obj.checkType(ch, true) && event.keyCode != 13) {
                      event.preventDefault();
                      if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                      return false;
                  }
              }
              // update date popup
              if (['date', 'time', 'datetime'].indexOf(obj.type) != -1) {
                  if (event.keyCode !== 9) setTimeout(function () { obj.updateOverlay(); }, 1);
              }
          },
  
          keyDown: function (event, extra) {
              var obj     = this;
              var options = obj.options;
              var key     = event.keyCode || (extra && extra.keyCode);
              // numeric
              if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) != -1) {
                  if (!options.keyboard || $(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  var cancel = false;
                  var val = parseFloat($(obj.el).val().replace(options.moneyRE, '')) || 0;
                  var inc = options.step;
                  if (event.ctrlKey || event.metaKey) inc = 10;
                  switch (key) {
                      case 38: // up
                          if (event.shiftKey) break; // no action if shift key is pressed
                          $(obj.el).val((val + inc <= options.max || options.max == null ? Number((val + inc).toFixed(12)) : options.max)).change();
                          cancel = true;
                          break;
                      case 40: // down
                          if (event.shiftKey) break; // no action if shift key is pressed
                          $(obj.el).val((val - inc >= options.min || options.min == null ? Number((val - inc).toFixed(12)) : options.min)).change();
                          cancel = true;
                          break;
                  }
                  if (cancel) {
                      event.preventDefault();
                      setTimeout(function () {
                          // set cursor to the end
                          obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
                      }, 0);
                  }
              }
              // date
              if (obj.type == 'date') {
                  if (!options.keyboard || $(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  var cancel  = false;
                  var daymil  = 24*60*60*1000;
                  var inc     = 1;
                  if (event.ctrlKey || event.metaKey) inc = 10;
                  var dt = w2utils.isDate($(obj.el).val(), options.format, true);
                  if (!dt) { dt = new Date(); daymil = 0; }
                  switch (key) {
                      case 38: // up
                          if (event.shiftKey) break; // no action if shift key is pressed
                          var newDT = w2utils.formatDate(dt.getTime() + daymil, options.format);
                          if (inc == 10) newDT = w2utils.formatDate(new Date(dt.getFullYear(), dt.getMonth()+1, dt.getDate()), options.format);
                          $(obj.el).val(newDT).change();
                          cancel = true;
                          break;
                      case 40: // down
                          if (event.shiftKey) break; // no action if shift key is pressed
                          var newDT = w2utils.formatDate(dt.getTime() - daymil, options.format);
                          if (inc == 10) newDT = w2utils.formatDate(new Date(dt.getFullYear(), dt.getMonth()-1, dt.getDate()), options.format);
                          $(obj.el).val(newDT).change();
                          cancel = true;
                          break;
                  }
                  if (cancel) {
                      event.preventDefault();
                      setTimeout(function () {
                          // set cursor to the end
                          obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
                          obj.updateOverlay();
                      }, 0);
                  }
              }
              // time
              if (obj.type == 'time') {
                  if (!options.keyboard || $(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  var cancel  = false;
                  var inc     = (event.ctrlKey || event.metaKey ? 60 : 1);
                  var val     = $(obj.el).val();
                  var time    = obj.toMin(val) || obj.toMin((new Date()).getHours() + ':' + ((new Date()).getMinutes() - 1));
                  switch (key) {
                      case 38: // up
                          if (event.shiftKey) break; // no action if shift key is pressed
                          time += inc;
                          cancel = true;
                          break;
                      case 40: // down
                          if (event.shiftKey) break; // no action if shift key is pressed
                          time -= inc;
                          cancel = true;
                          break;
                  }
                  if (cancel) {
                      $(obj.el).val(obj.fromMin(time)).change();
                      event.preventDefault();
                      setTimeout(function () {
                          // set cursor to the end
                          obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
                      }, 0);
                  }
              }
              // datetime
              if (obj.type == 'datetime') {
                  if (!options.keyboard || $(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  var cancel  = false;
                  var daymil  = 24*60*60*1000;
                  var inc     = 1;
                  if (event.ctrlKey || event.metaKey) inc = 10;
                  var str = $(obj.el).val();
                  var dt  = w2utils.isDateTime(str, this.options.format, true);
                  if (!dt) { dt = new Date(); daymil = 0; }
                  switch (key) {
                      case 38: // up
                          if (event.shiftKey) break; // no action if shift key is pressed
                          var newDT = w2utils.formatDateTime(dt.getTime() + daymil, options.format);
                          if (inc == 10) newDT = w2utils.formatDateTime(new Date(dt.getFullYear(), dt.getMonth()+1, dt.getDate()), options.format);
                          $(obj.el).val(newDT).change();
                          cancel = true;
                          break;
                      case 40: // down
                          if (event.shiftKey) break; // no action if shift key is pressed
                          var newDT = w2utils.formatDateTime(dt.getTime() - daymil, options.format);
                          if (inc == 10) newDT = w2utils.formatDateTime(new Date(dt.getFullYear(), dt.getMonth()-1, dt.getDate()), options.format);
                          $(obj.el).val(newDT).change();
                          cancel = true;
                          break;
                  }
                  if (cancel) {
                      event.preventDefault();
                      setTimeout(function () {
                          // set cursor to the end
                          obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
                          obj.updateOverlay();
                      }, 0);
                  }
              }
              // color
              if (obj.type == 'color') {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  // paste
                  if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {
                      var dir      = null;
                      var newColor = null;
                      switch (key) {
                          case 38: // up
                              dir = 'up';
                              break;
                          case 40: // down
                              dir = 'down';
                              break;
                          case 39: // right
                              dir = 'right';
                              break;
                          case 37: // left
                              dir = 'left';
                              break;
                      }
                      if (obj.el.nav && dir != null) {
                          newColor = obj.el.nav(dir);
                          $(obj.el).val(newColor).change();
                          event.preventDefault();
                      }
                  }
              }
              // list/select/combo
              if (['list', 'combo', 'enum'].indexOf(obj.type) != -1) {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  var selected  = $(obj.el).data('selected');
                  var focus     = $(obj.el);
                  var indexOnly = false;
                  if (['list', 'enum'].indexOf(obj.type) != -1) {
                      if (obj.type == 'list') {
                          focus = $(obj.helpers.focus).find('input');
                      }
                      if (obj.type == 'enum') {
                          focus = $(obj.helpers.multi).find('input');
                      }
                      // not arrows - refresh
                      if ([37, 38, 39, 40].indexOf(key) == -1) {
                          setTimeout(function () { obj.refresh(); }, 1);
                      }
                      // paste
                      if (event.keyCode == 86 && (event.ctrlKey || event.metaKey)) {
                          setTimeout(function () {
                              obj.refresh();
                              obj.search();
                              obj.request();
                          }, 50);
                      }
                  }
                  // apply arrows
                  switch (key) {
                      case 27: // escape
                          if (obj.type == 'list') {
                              if (focus.val() !== '') focus.val('');
                              event.stopPropagation(); // escape in field should not close popup
                          }
                          break;
                      case 37: // left
                      case 39: // right
                          // indexOnly = true;
                          break;
                      case 13: // enter
                          if ($('#w2ui-overlay').length === 0) break; // no action if overlay not open
                          var item  = options.items[options.index];
                          if (obj.type == 'enum') {
                              if (item != null) {
                                  // trigger event
                                  var edata = obj.trigger({ phase: 'before', type: 'add', target: obj.el, originalEvent: event.originalEvent, item: item });
                                  if (edata.isCancelled === true) return;
                                  item = edata.item; // need to reassign because it could be recreated by user
                                  // default behavior
                                  if (selected.length >= options.max && options.max > 0) selected.pop();
                                  delete item.hidden;
                                  delete obj.tmp.force_open;
                                  selected.push(item);
                                  $(obj.el).change();
                                  focus.val('').width(20);
                                  obj.refresh();
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              } else {
                                  // trigger event
                                  item = { id: focus.val(), text: focus.val() };
                                  var edata = obj.trigger({ phase: 'before', type: 'new', target: obj.el, originalEvent: event.originalEvent, item: item });
                                  if (edata.isCancelled === true) return;
                                  item = edata.item; // need to reassign because it could be recreated by user
                                  // default behavior
                                  if (typeof obj.onNew == 'function') {
                                      if (selected.length >= options.max && options.max > 0) selected.pop();
                                      delete obj.tmp.force_open;
                                      selected.push(item);
                                      $(obj.el).change();
                                      focus.val('').width(20);
                                      obj.refresh();
                                  }
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              }
                          } else {
                              if (item) $(obj.el).data('selected', item).val(item.text).change();
                              if ($(obj.el).val() === '' && $(obj.el).data('selected')) $(obj.el).removeData('selected').val('').change();
                              if (obj.type == 'list') {
                                  focus.val('');
                                  obj.refresh();
                              }
                              // hide overlay
                              obj.tmp.force_hide = true;
                          }
                          break;
                      case 8:  // backspace
                      case 46: // delete
                          if (obj.type == 'enum' && key == 8) {
                              if (focus.val() === '' && selected.length > 0) {
                                  var item = selected[selected.length - 1];
                                  // trigger event
                                  var edata = obj.trigger({ phase: 'before', type: 'remove', target: obj.el, originalEvent: event.originalEvent, item: item });
                                  if (edata.isCancelled === true) return;
                                  // default behavior
                                  selected.pop();
                                  $(obj.el).trigger('change');
                                  obj.refresh();
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              }
                          }
                          if (obj.type == 'list' && focus.val() === '') {
                              $(obj.el).data('selected', {}).change();
                              obj.refresh();
                          }
                          break;
                      case 38: // up
                          options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
                          options.index--;
                          while (options.index > 0 && options.items[options.index].hidden) options.index--;
                          if (options.index === 0 && options.items[options.index].hidden) {
                              while (options.items[options.index] && options.items[options.index].hidden) options.index++;
                          }
                          indexOnly = true;
                          break;
                      case 40: // down
                          options.index = w2utils.isInt(options.index) ? parseInt(options.index) : -1;
                          options.index++;
                          while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
                          if (options.index == options.items.length-1 && options.items[options.index].hidden) {
                              while (options.items[options.index] && options.items[options.index].hidden) options.index--;
                          }
                          // show overlay if not shown
                          if (focus.val() === '' && $('#w2ui-overlay').length === 0) {
                              obj.tmp.force_open = true;
                          } else {
                              indexOnly = true;
                          }
                          break;
                  }
                  if (indexOnly) {
                      if (options.index < 0) options.index = 0;
                      if (options.index >= options.items.length) options.index = options.items.length -1;
                      obj.updateOverlay(indexOnly);
                      // cancel event
                      event.preventDefault();
                      setTimeout(function () {
                          // set cursor to the end
                          if (obj.type == 'enum') {
                              var tmp = focus.get(0);
                              tmp.setSelectionRange(tmp.value.length, tmp.value.length);
                          } else if (obj.type == 'list') {
                              var tmp = focus.get(0);
                              tmp.setSelectionRange(tmp.value.length, tmp.value.length);
                          } else {
                              obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
                          }
                      }, 0);
                      return;
                  }
                  // expand input
                  if (obj.type == 'enum') {
                      focus.width(((focus.val().length + 2) * 8) + 'px');
                  }
              }
          },
  
          keyUp: function (event) {
              var obj = this;
              if (['list', 'combo', 'enum'].indexOf(this.type) != -1) {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  // need to be here for ipad compa
                  if ([16, 17, 18, 20, 37, 39, 91].indexOf(event.keyCode) == -1) { // no refreah on crtl, shift, left/right arrows, etc
                      var input = $(this.helpers.focus).find('input');
                      if (input.length === 0) input = $(this.el); // for combo list
                      // trigger event
                      var edata = this.trigger({ phase: 'before', type: 'search', originalEvent: event, target: input, search: input.val() });
                      if (edata.isCancelled === true) return;
                      // regular
                      if (!this.tmp.force_hide) this.request();
                      if (input.val().length == 1) this.refresh();
                      if ($('#w2ui-overlay').length === 0 || [38, 40].indexOf(event.keyCode) == -1) { // no search on arrows
                          this.search();
                      }
                      // event after
                      this.trigger($.extend(edata, { phase: 'after' }));
                  }
              }
          },
  
          clearCache: function () {
              var options          = this.options;
              options.items        = [];
              this.tmp.xhr_loading = false;
              this.tmp.xhr_search  = '';
              this.tmp.xhr_total   = -1;
          },
  
          request: function (interval) {
              var obj      = this;
              var options  = this.options;
              var search   = $(obj.el).val() || '';
              // if no url - do nothing
              if (!options.url) return;
              // --
              if (obj.type == 'enum') {
                  var tmp = $(obj.helpers.multi).find('input');
                  if (tmp.length === 0) search = ''; else search = tmp.val();
              }
              if (obj.type == 'list') {
                  var tmp = $(obj.helpers.focus).find('input');
                  if (tmp.length === 0) search = ''; else search = tmp.val();
              }
              if (options.minLength !== 0 && search.length < options.minLength) {
                  options.items = []; // need to empty the list
                  this.updateOverlay();
                  return;
              }
              if (interval == null) interval = options.interval;
              if (obj.tmp.xhr_search == null) obj.tmp.xhr_search = '';
              if (obj.tmp.xhr_total == null) obj.tmp.xhr_total = -1;
              // check if need to search
              if (options.url && $(obj.el).prop('readonly') !== true && $(obj.el).prop('disabled') !== true && (
                      (options.items.length === 0 && obj.tmp.xhr_total !== 0) ||
                      (obj.tmp.xhr_total == options.cacheMax && search.length > obj.tmp.xhr_search.length) ||
                      (search.length >= obj.tmp.xhr_search.length && search.substr(0, obj.tmp.xhr_search.length) != obj.tmp.xhr_search) ||
                      (search.length < obj.tmp.xhr_search.length)
                  )) {
                  // empty list
                  if (obj.tmp.xhr) obj.tmp.xhr.abort();
                  obj.tmp.xhr_loading = true;
                  obj.search();
                  // timeout
                  clearTimeout(obj.tmp.timeout);
                  obj.tmp.timeout = setTimeout(function () {
                      // trigger event
                      var url      = options.url;
                      var postData = {
                          search : search,
                          max    : options.cacheMax
                      };
                      $.extend(postData, options.postData);
                      var edata = obj.trigger({ phase: 'before', type: 'request', search: search, target: obj.el, url: url, postData: postData });
                      if (edata.isCancelled === true) return;
                      url      = edata.url;
                      postData = edata.postData;
                      var ajaxOptions = {
                          type     : 'GET',
                          url      : url,
                          data     : postData,
                          dataType : 'JSON' // expected from server
                      };
                      if (options.method) ajaxOptions.type = options.method;
                      if (w2utils.settings.dataType == 'JSON') {
                          ajaxOptions.type        = 'POST';
                          ajaxOptions.data        = JSON.stringify(ajaxOptions.data);
                          ajaxOptions.contentType = 'application/json';
                      }
                      if (w2utils.settings.dataType == 'HTTPJSON') {
                          ajaxOptions.data = { request: JSON.stringify(ajaxOptions.data) };
                      }
                      if (options.method != null) ajaxOptions.type = options.method;
                      obj.tmp.xhr = $.ajax(ajaxOptions)
                          .done(function (data, status, xhr) {
                              // trigger event
                              var edata2 = obj.trigger({ phase: 'before', type: 'load', target: obj.el, search: postData.search, data: data, xhr: xhr });
                              if (edata2.isCancelled === true) return;
                              // default behavior
                              data = edata2.data;
                              if (typeof data == 'string') data = JSON.parse(data);
                              if (data.records == null && data.items != null) {
                                  // needed for backward compatibility
                                  data.records = data.items;
                                  delete data.items;
                              }
                              if (data.status != 'success' || !Array.isArray(data.records)) {
                                  console.log('ERROR: server did not return proper structure. It should return', { status: 'success', records: [{ id: 1, text: 'item' }] });
                                  return;
                              }
                              // remove all extra items if more then needed for cache
                              if (data.records.length > options.cacheMax) data.records.splice(options.cacheMax, 100000);
                              // map id and text
                              if (options.recId == null && options.recid != null) options.recId = options.recid; // since lower-case recid is used in grid
                              if (options.recId || options.recText) {
                                  data.records.forEach(function (item) {
                                      if (typeof options.recId == 'string') item.id   = item[options.recId];
                                      if (typeof options.recId == 'function') item.id = options.recId(item);
                                      if (typeof options.recText == 'string') item.text   = item[options.recText];
                                      if (typeof options.recText == 'function') item.text = options.recText(item);
                                  });
                              }
                              // remember stats
                              obj.tmp.xhr_loading = false;
                              obj.tmp.xhr_search  = search;
                              obj.tmp.xhr_total   = data.records.length;
                              options.items       = obj.normMenu(data.records);
                              if (search === '' && data.records.length === 0) obj.tmp.emptySet = true; else obj.tmp.emptySet = false;
                              obj.search();
                              // event after
                              obj.trigger($.extend(edata2, { phase: 'after' }));
                          })
                          .fail(function (xhr, status, error) {
                              // trigger event
                              var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
                              var edata2 = obj.trigger({ phase: 'before', type: 'error', target: obj.el, search: search, error: errorObj, xhr: xhr });
                              if (edata2.isCancelled === true) return;
                              // default behavior
                              if (status != 'abort') {
                                  var data;
                                  try { data = $.parseJSON(xhr.responseText); } catch (e) {}
                                  console.log('ERROR: Server communication failed.',
                                      '\n   EXPECTED:', { status: 'success', records: [{ id: 1, text: 'item' }] },
                                      '\n         OR:', { status: 'error', message: 'error message' },
                                      '\n   RECEIVED:', typeof data == 'object' ? data : xhr.responseText);
                              }
                              // reset stats
                              obj.clearCache();
                              obj.search();
                              // event after
                              obj.trigger($.extend(edata2, { phase: 'after' }));
                          });
                      // event after
                      obj.trigger($.extend(edata, { phase: 'after' }));
                  }, interval);
              }
          },
  
          search: function () {
              var obj      = this;
              var options  = this.options;
              var search   = $(obj.el).val();
              var target   = obj.el;
              var ids      = [];
              var selected = $(obj.el).data('selected');
              if (obj.type == 'enum') {
                  target = $(obj.helpers.multi).find('input');
                  search = target.val();
                  for (var s in selected) { if (selected[s]) ids.push(selected[s].id); }
              }
              else if (obj.type == 'list') {
                  target = $(obj.helpers.focus).find('input');
                  search = target.val();
                  for (var s in selected) { if (selected[s]) ids.push(selected[s].id); }
              }
              if (obj.tmp.xhr_loading !== true) {
                  var shown = 0;
                  for (var i = 0; i < options.items.length; i++) {
                      var item = options.items[i];
                      if (options.compare != null) {
                          if (typeof options.compare == 'function') {
                              item.hidden = (options.compare.call(this, item, search) === false ? true : false);
                          }
                      } else {
                          var prefix = '';
                          var suffix = '';
                          if (['is', 'begins'].indexOf(options.match) != -1) prefix = '^';
                          if (['is', 'ends'].indexOf(options.match) != -1) suffix = '$';
                          try {
                              var re = new RegExp(prefix + search + suffix, 'i');
                              if (re.test(item.text) || item.text == '...') item.hidden = false; else item.hidden = true;
                          } catch (e) {}
                      }
                      if (options.filter === false) item.hidden = false;
                      // do not show selected items
                      if (obj.type == 'enum' && $.inArray(item.id, ids) != -1) item.hidden = true;
                      if (item.hidden !== true) { shown++; delete item.hidden; }
                  }
                  // preselect first item
                  options.index = -1;
                  while (options.items[options.index] && options.items[options.index].hidden) options.index++;
                  if (shown <= 0) options.index = -1;
                  options.spinner = false;
                  obj.updateOverlay();
                  setTimeout(function () {
                      var html = $('#w2ui-overlay').html() || '';
                      if (options.markSearch && html.indexOf('$.fn.w2menuHandler') != -1) { // do not highlight when no items
                          $('#w2ui-overlay').w2marker(search);
                      }
                  }, 1);
              } else {
                  options.items.splice(0, options.cacheMax);
                  options.spinner = true;
                  obj.updateOverlay();
              }
          },
  
          updateOverlay: function (indexOnly) {
              var obj     = this;
              var options = this.options;
              // color
              if (this.type == 'color') {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  $(this.el).w2color({
                      color       : $(this.el).val(),
                      transparent : options.transparent,
                      advanced    : options.advanced
                  },
                  function (color) {
                      if (color == null) return;
                      $(obj.el).val(color).change();
                  });
              }
              // date
              if (this.type == 'date') {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  if ($('#w2ui-overlay').length === 0) {
                      $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar" onclick="event.stopPropagation();"></div>', {
                          css: { "background-color": "#f5f5f5" }
                      });
                  }
                  var month, year;
                  var dt = w2utils.isDate($(obj.el).val(), obj.options.format, true);
                  if (dt) { month = dt.getMonth() + 1; year = dt.getFullYear(); }
                  (function refreshCalendar(month, year) {
                      $('#w2ui-overlay > div > div').html(obj.getMonthHTML(month, year, $(obj.el).val()));
                      $('#w2ui-overlay .w2ui-calendar-title')
                          .on('mousedown', function () {
                              if ($(this).next().hasClass('w2ui-calendar-jump')) {
                                  $(this).next().remove();
                              } else {
                                  var selYear, selMonth;
                                  $(this).after('<div class="w2ui-calendar-jump" style=""></div>');
                                  $(this).next().hide().html(obj.getYearHTML()).fadeIn(200);
                                  setTimeout(function () {
                                      $('#w2ui-overlay .w2ui-calendar-jump')
                                          .find('.w2ui-jump-month, .w2ui-jump-year')
                                          .on('click', function () {
                                              if ($(this).hasClass('w2ui-jump-month')) {
                                                  $(this).parent().find('.w2ui-jump-month').removeClass('selected');
                                                  $(this).addClass('selected');
                                                  selMonth = $(this).attr('name');
                                              }
                                              if ($(this).hasClass('w2ui-jump-year')) {
                                                  $(this).parent().find('.w2ui-jump-year').removeClass('selected');
                                                  $(this).addClass('selected');
                                                  selYear = $(this).attr('name');
                                              }
                                              if (selYear != null && selMonth != null) {
                                                  $('#w2ui-overlay .w2ui-calendar-jump').fadeOut(100);
                                                  setTimeout(function () { refreshCalendar(parseInt(selMonth)+1, selYear); }, 100);
                                              }
                                          });
                                      $('#w2ui-overlay .w2ui-calendar-jump >:last-child').prop('scrollTop', 2000);
                                  }, 1);
                              }
                          });
                      $('#w2ui-overlay .w2ui-date')
                          .on('mousedown', function () {
                              var day = $(this).attr('date');
                              $(obj.el).val(day).change();
                              $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                          })
                          .on('mouseup', function () {
                              setTimeout(function () {
                                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide();
                              }, 10);
                          });
                      $('#w2ui-overlay .previous').on('mousedown', function () {
                          var tmp = obj.options.current.split('/');
                          tmp[0]  = parseInt(tmp[0]) - 1;
                          refreshCalendar(tmp[0], tmp[1]);
                      });
                      $('#w2ui-overlay .next').on('mousedown', function () {
                          var tmp = obj.options.current.split('/');
                          tmp[0]  = parseInt(tmp[0]) + 1;
                          refreshCalendar(tmp[0], tmp[1]);
                      });
                  }) (month, year);
              }
              // time
              if (this.type == 'time') {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  if ($('#w2ui-overlay').length === 0) {
                      $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time" onclick="event.stopPropagation();"></div>', {
                          css: { "background-color": "#fff" }
                      });
                  }
                  var h24 = (this.options.format == 'h24');
                  $('#w2ui-overlay > div').html(obj.getHourHTML());
                  $('#w2ui-overlay .w2ui-time')
                      .on('mousedown', function (event) {
                          $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                          var hour = $(this).attr('hour');
                          $(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':00' + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
                      });
                      if (this.options.noMinutes == null || this.options.noMinutes === false) {
                          $('#w2ui-overlay .w2ui-time')
                              .on('mouseup', function () {
                                  var hour = $(this).attr('hour');
                                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                                  $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time"></div>', { css: { "background-color": "#fff" } });
                                  $('#w2ui-overlay > div').html(obj.getMinHTML(hour));
                                  $('#w2ui-overlay .w2ui-time')
                                      .on('mousedown', function () {
                                          $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                                          var min = $(this).attr('min');
                                          $(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':' + (min < 10 ? 0 : '') + min + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
                                      })
                                      .on('mouseup', function () {
                                          setTimeout(function () { if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide(); }, 10);
                                      });
                              });
                      } else {
                          $('#w2ui-overlay .w2ui-time')
                              .on('mouseup', function () {
                                      setTimeout(function () { if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide(); }, 10);
                              });
                      }
              }
              // datetime
              if (this.type == 'datetime') {
                  if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                  // hide overlay if we are in the time selection
                  if ($("#w2ui-overlay .w2ui-time").length > 0) $('#w2ui-overlay')[0].hide();
                  if ($('#w2ui-overlay').length === 0) {
                      $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar" onclick="event.stopPropagation();"></div>', {
                          css: { "background-color": "#f5f5f5" }
                      });
                  }
                  var month, year;
                  var dt = w2utils.isDateTime($(obj.el).val(), obj.options.format, true);
                  if (dt) { month = dt.getMonth() + 1; year = dt.getFullYear(); }
                  var selDate = null;
                  (function refreshCalendar(month, year) {
                      $('#w2ui-overlay > div > div').html(
                          obj.getMonthHTML(month, year, $(obj.el).val())
                          + (options.btn_now ? '<div class="w2ui-calendar-now now">'+ w2utils.lang('Current Date & Time') + '</div>' : '')
                      );
                      $('#w2ui-overlay .w2ui-calendar-title')
                          .on('mousedown', function () {
                              if ($(this).next().hasClass('w2ui-calendar-jump')) {
                                  $(this).next().remove();
                              } else {
                                  var selYear, selMonth;
                                  $(this).after('<div class="w2ui-calendar-jump" style=""></div>');
                                  $(this).next().hide().html(obj.getYearHTML()).fadeIn(200);
                                  setTimeout(function () {
                                      $('#w2ui-overlay .w2ui-calendar-jump')
                                          .find('.w2ui-jump-month, .w2ui-jump-year')
                                          .on('click', function () {
                                              if ($(this).hasClass('w2ui-jump-month')) {
                                                  $(this).parent().find('.w2ui-jump-month').removeClass('selected');
                                                  $(this).addClass('selected');
                                                  selMonth = $(this).attr('name');
                                              }
                                              if ($(this).hasClass('w2ui-jump-year')) {
                                                  $(this).parent().find('.w2ui-jump-year').removeClass('selected');
                                                  $(this).addClass('selected');
                                                  selYear = $(this).attr('name');
                                              }
                                              if (selYear != null && selMonth != null) {
                                                  $('#w2ui-overlay .w2ui-calendar-jump').fadeOut(100);
                                                  setTimeout(function () { refreshCalendar(parseInt(selMonth)+1, selYear); }, 100);
                                              }
                                          });
                                      $('#w2ui-overlay .w2ui-calendar-jump >:last-child').prop('scrollTop', 2000);
                                  }, 1);
                              }
                          });
                      $('#w2ui-overlay .w2ui-date')
                          .on('mousedown', function () {
                              var day = $(this).attr('date');
                              $(obj.el).val(day).change();
                              $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                              selDate = new Date($(this).attr('data-date'));
                          })
                          .on('mouseup', function () {
                              // continue with time picker
                              var selHour, selMin;
                              if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                              $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time"></div>', { css: { "background-color": "#fff" } });
                              var h24 = (obj.options.format == 'h24');
                              $('#w2ui-overlay > div').html(obj.getHourHTML());
                              $('#w2ui-overlay .w2ui-time')
                                  .on('mousedown', function (event) {
                                      $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                                      selHour = $(this).attr('hour');
                                      selDate.setHours(selHour);
                                      var txt = w2utils.formatDateTime(selDate, obj.options.format);
                                      $(obj.el).val(txt).change();
                                      //$(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':00' + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
                                  });
                              if (obj.options.noMinutes == null || obj.options.noMinutes === false) {
                                  $('#w2ui-overlay .w2ui-time')
                                      .on('mouseup', function () {
                                          var hour = $(this).attr('hour');
                                          if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                                          $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time"></div>', { css: { "background-color": "#fff" } });
                                          $('#w2ui-overlay > div').html(obj.getMinHTML(hour));
                                          $('#w2ui-overlay .w2ui-time')
                                              .on('mousedown', function () {
                                                  $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
                                                  selMin = $(this).attr('min');
                                                  selDate.setHours(selHour, selMin);
                                                  var txt = w2utils.formatDateTime(selDate, obj.options.format);
                                                  $(obj.el).val(txt).change();
                                                  //$(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':' + (min < 10 ? 0 : '') + min + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
                                              })
                                              .on('mouseup', function () {
                                                  setTimeout(function () { if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide(); }, 10);
                                              });
                                      });
                              } else {
                                  $('#w2ui-overlay .w2ui-time')
                                      .on('mouseup', function () {
                                              setTimeout(function () { if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide(); }, 10);
                                      });
                              }
                          });
                      $('#w2ui-overlay .previous').on('mousedown', function () {
                          var tmp = obj.options.current.split('/');
                          tmp[0]  = parseInt(tmp[0]) - 1;
                          refreshCalendar(tmp[0], tmp[1]);
                      });
                      $('#w2ui-overlay .next').on('mousedown', function () {
                          var tmp = obj.options.current.split('/');
                          tmp[0]  = parseInt(tmp[0]) + 1;
                          refreshCalendar(tmp[0], tmp[1]);
                      });
                      // "now" button
                      $('#w2ui-overlay .now')
                          .on('mousedown', function () {
                              // this currently ignores blocked days or start / end dates!
                              var tmp = w2utils.formatDateTime(new Date(), obj.options.format);
                              $(obj.el).val(tmp).change();
                              return false;
                          })
                          .on('mouseup', function () {
                              setTimeout(function () {
                                  if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide();
                              }, 10);
                          });
                  }) (month, year);
              }
              // list
              if (['list', 'combo', 'enum'].indexOf(this.type) != -1) {
                  var el    = this.el;
                  var input = this.el;
                  if (this.type == 'enum') {
                      el    = $(this.helpers.multi);
                      input = $(el).find('input');
                  }
                  if (this.type == 'list') {
                      var sel = $(input).data('selected');
                      if ($.isPlainObject(sel) && !$.isEmptyObject(sel) && options.index == -1) {
                          options.items.forEach(function (item, ind) {
                              if (item.id == sel.id) {
                                  options.index = ind;
                              }
                          });
                      }
                      input = $(this.helpers.focus).find('input');
                  }
                  if ($(input).is(':focus')) {
                      if (options.openOnFocus === false && $(input).val() === '' && obj.tmp.force_open !== true) {
                          $().w2overlay();
                          return;
                      }
                      if (obj.tmp.force_hide) {
                          $().w2overlay();
                          setTimeout(function () {
                              delete obj.tmp.force_hide;
                          }, 1);
                          return;
                      }
                      if ($(input).val() !== '') delete obj.tmp.force_open;
                      var msgNoItems = w2utils.lang('No matches');
                      if (options.url != null && $(input).val().length < options.minLength && obj.tmp.emptySet !== true) msgNoItems = options.minLength + ' ' + w2utils.lang('letters or more...');
                      if (options.url != null && $(input).val() === '' && obj.tmp.emptySet !== true) msgNoItems = w2utils.lang('Type to search...');
                      if (options.url == null && options.items.length === 0) msgNoItems = w2utils.lang('Empty list');
                      if (options.msgNoItems != null) msgNoItems = options.msgNoItems;
                      if (msgNoItems == 'function') msgNoItems = msgNoItems(options);
                      $(el).w2menu((!indexOnly ? 'refresh' : 'refresh-index'), $.extend(true, {}, options, {
                          search     : false,
                          render     : options.renderDrop,
                          maxHeight  : options.maxDropHeight,
                          maxWidth   : options.maxDropWidth,
                          msgNoItems : msgNoItems,
                          // selected with mouse
                          onSelect: function (event) {
                              if (obj.type == 'enum') {
                                  var selected = $(obj.el).data('selected');
                                  if (event.item) {
                                      // trigger event
                                      var edata = obj.trigger({ phase: 'before', type: 'add', target: obj.el, originalEvent: event.originalEvent, item: event.item });
                                      if (edata.isCancelled === true) return;
                                      // default behavior
                                      if (selected.length >= options.max && options.max > 0) selected.pop();
                                      delete event.item.hidden;
                                      selected.push(event.item);
                                      $(obj.el).data('selected', selected).change();
                                      $(obj.helpers.multi).find('input').val('').width(20);
                                      obj.refresh();
                                      if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
                                      // event after
                                      obj.trigger($.extend(edata, { phase: 'after' }));
                                  }
                              } else {
                                  $(obj.el).data('selected', event.item).val(event.item.text).change();
                                  if (obj.helpers.focus) obj.helpers.focus.find('input').val('');
                              }
                          }
                      }));
                  }
              }
          },
  
          inRange: function (str, onlyDate) {
              var inRange = false;
              if (this.type == 'date') {
                  var dt = w2utils.isDate(str, this.options.format, true);
                  if (dt) {
                      // enable range
                      if (this.options.start || this.options.end) {
                          var st = (typeof this.options.start == 'string' ? this.options.start : $(this.options.start).val());
                          var en = (typeof this.options.end == 'string' ? this.options.end : $(this.options.end).val());
                          var start   = w2utils.isDate(st, this.options.format, true);
                          var end     = w2utils.isDate(en, this.options.format, true);
                          var current = new Date(dt);
                          if (!start) start = current;
                          if (!end) end = current;
                          if (current >= start && current <= end) inRange = true;
                      } else {
                          inRange = true;
                      }
                      // block predefined dates
                      if (this.options.blocked && $.inArray(str, this.options.blocked) != -1) inRange = false;
  
                      /*
                      clockWeekDay - type: array or integers. every element - number of week day.
                      number of weekday (1 - monday, 2 - tuesday, 3 - wensday, 4 - thursday, 5 - friday, 6 - saturday, 0 - sunday)
                      for block in calendar (for example, block all sundays so user can't choose sunday in calendar)
                      */
                      if (this.options.blockWeekDays != null && this.options.blockWeekDays != undefined
                          && this.options.blockWeekDays.length != undefined){
                          var l = this.options.blockWeekDays.length;
                          for (var i=0; i<l; i++){
                              if (dt.getDay() == this.options.blockWeekDays[i]){
                                  inRange = false;
                              }
                          }
                      }
                  }
              } else if (this.type == 'time') {
                  if (this.options.start || this.options.end) {
                      var tm  = this.toMin(str);
                      var tm1 = this.toMin(this.options.start);
                      var tm2 = this.toMin(this.options.end);
                      if (!tm1) tm1 = tm;
                      if (!tm2) tm2 = tm;
                      if (tm >= tm1 && tm <= tm2) inRange = true;
                  } else {
                      inRange = true;
                  }
              } else if (this.type == 'datetime') {
                  var dt = w2utils.isDateTime(str, this.options.format, true);
                  if (dt) {
                      // enable range
                      if (this.options.start || this.options.end) {
                          var start, end;
                          if (typeof this.options.start == 'object' && this.options.start instanceof Date) {
                              start = this.options.start;
                          } else {
                              var st = (typeof this.options.start == 'string' ? this.options.start : $(this.options.start).val());
                              if (st.trim() !== '') {
                                  start = w2utils.isDateTime(st, this.options.format, true);
                              } else {
                                  start = '';
                              }
                          }
                          if (typeof this.options.end == 'object' && this.options.end instanceof Date) {
                              end = this.options.end;
                          } else {
                              var en = (typeof this.options.end == 'string' ? this.options.end : $(this.options.end).val());
                              if (en.trim() !== '') {
                                  end = w2utils.isDateTime(en, this.options.format, true);
                              } else {
                                  end = '';
                              }
                          }
                          var current = dt; // new Date(dt);
                          if (!start) start = current;
                          if (!end) end = current;
                          if (onlyDate && start instanceof Date) {
                              start.setHours(0);
                              start.setMinutes(0);
                              start.setSeconds(0);
                          }
                          if (current >= start && current <= end) inRange = true;
                      } else {
                          inRange = true;
                      }
                      // block predefined dates
                      if (inRange && this.options.blocked) {
                          for (var i=0; i<this.options.blocked.length; i++) {
                              var blocked = this.options.blocked[i];
                              if(typeof blocked == 'string') {
                                  // convert string to Date object
                                  blocked = w2utils.isDateTime(blocked, this.options.format, true);
                              }
                              // check for Date object with the same day
                              if(typeof blocked == 'object' && blocked instanceof Date && (blocked.getFullYear() == dt.getFullYear() && blocked.getMonth() == dt.getMonth() && blocked.getDate() == dt.getDate())) {
                                  inRange = false;
                                  break;
                              }
                          }
                      }
                  }
              }
              return inRange;
          },
  
          /*
          *  INTERNAL FUNCTIONS
          */
  
          checkType: function (ch, loose) {
              var obj = this;
              switch (obj.type) {
                  case 'int':
                      if (loose && ['-', obj.options.groupSymbol].indexOf(ch) != -1) return true;
                      return w2utils.isInt(ch.replace(obj.options.numberRE, ''));
                  case 'percent':
                      ch = ch.replace(/%/g, '');
                  case 'float':
                      if (loose && ['-', w2utils.settings.decimalSymbol, obj.options.groupSymbol].indexOf(ch) != -1) return true;
                      return w2utils.isFloat(ch.replace(obj.options.numberRE, ''));
                  case 'money':
                  case 'currency':
                      if (loose && ['-', obj.options.decimalSymbol, obj.options.groupSymbol, obj.options.currencyPrefix, obj.options.currencySuffix].indexOf(ch) != -1) return true;
                      return w2utils.isFloat(ch.replace(obj.options.moneyRE, ''));
                  case 'bin':
                      return w2utils.isBin(ch);
                  case 'hex':
                      return w2utils.isHex(ch);
                  case 'alphanumeric':
                      return w2utils.isAlphaNumeric(ch);
              }
              return true;
          },
  
          addPrefix: function () {
              var obj = this;
              setTimeout(function () {
                  if (obj.type === 'clear') return;
                  var helper;
                  var tmp = $(obj.el).data('tmp') || {};
                  if (tmp['old-padding-left']) $(obj.el).css('padding-left', tmp['old-padding-left']);
                  tmp['old-padding-left'] = $(obj.el).css('padding-left');
                  $(obj.el).data('tmp', tmp);
                  // remove if already displaed
                  if (obj.helpers.prefix) $(obj.helpers.prefix).remove();
                  if (obj.options.prefix !== '') {
                      // add fresh
                      $(obj.el).before(
                          '<div class="w2ui-field-helper">'+
                              obj.options.prefix +
                          '</div>'
                      );
                      helper = $(obj.el).prev();
                      helper
                          .css({
                              'color'          : $(obj.el).css('color'),
                              'font-family'    : $(obj.el).css('font-family'),
                              'font-size'      : $(obj.el).css('font-size'),
                              'padding-top'    : $(obj.el).css('padding-top'),
                              'padding-bottom' : $(obj.el).css('padding-bottom'),
                              'padding-left'   : $(obj.el).css('padding-left'),
                              'padding-right'  : 0,
                              'margin-top'     : (parseInt($(obj.el).css('margin-top'), 10) + 2) + 'px',
                              'margin-bottom'  : (parseInt($(obj.el).css('margin-bottom'), 10) + 1) + 'px',
                              'margin-left'    : $(obj.el).css('margin-left'),
                              'margin-right'   : 0
                          })
                          .on('click', function (event) {
                              if (obj.options.icon && typeof obj.onIconClick == 'function') {
                                  // event before
                                  var edata = obj.trigger({ phase: 'before', type: 'iconClick', target: obj.el, el: $(this).find('span.w2ui-icon')[0] });
                                  if (edata.isCancelled === true) return;
  
                                  // intentionally empty
  
                                  // event after
                                  obj.trigger($.extend(edata, { phase: 'after' }));
                              } else {
                                  if (obj.type == 'list') {
                                      $(obj.helpers.focus).find('input').focus();
                                  } else {
                                      $(obj.el).focus();
                                  }
                              }
                          });
                      $(obj.el).css('padding-left', (helper.width() + parseInt($(obj.el).css('padding-left'), 10)) + 'px');
                      // remember helper
                      obj.helpers.prefix = helper;
                  }
              }, 1);
          },
  
          addSuffix: function () {
              var obj = this;
              var helper, pr;
              setTimeout(function () {
                  if (obj.type === 'clear') return;
                  var tmp = $(obj.el).data('tmp') || {};
                  if (tmp['old-padding-right']) $(obj.el).css('padding-right', tmp['old-padding-right']);
                  tmp['old-padding-right'] = $(obj.el).css('padding-right');
                  $(obj.el).data('tmp', tmp);
                  pr = parseInt($(obj.el).css('padding-right'), 10);
                  if (obj.options.arrows) {
                      // remove if already displayed
                      if (obj.helpers.arrows) $(obj.helpers.arrows).remove();
                      // add fresh
                      $(obj.el).after(
                          '<div class="w2ui-field-helper" style="border: 1px solid transparent">&#160;'+
                          '    <div class="w2ui-field-up" type="up">'+
                          '        <div class="arrow-up" type="up"></div>'+
                          '    </div>'+
                          '    <div class="w2ui-field-down" type="down">'+
                          '        <div class="arrow-down" type="down"></div>'+
                          '    </div>'+
                          '</div>');
                      var height = w2utils.getSize(obj.el, 'height');
                      helper = $(obj.el).next();
                      helper.css({
                              'color'         : $(obj.el).css('color'),
                              'font-family'   : $(obj.el).css('font-family'),
                              'font-size'     : $(obj.el).css('font-size'),
                              'height'        : ($(obj.el).height() + parseInt($(obj.el).css('padding-top'), 10) + parseInt($(obj.el).css('padding-bottom'), 10) ) + 'px',
                              'padding'       : 0,
                              'margin-top'    : (parseInt($(obj.el).css('margin-top'), 10) + 1) + 'px',
                              'margin-bottom' : 0,
                              'border-left'   : '1px solid silver'
                          })
                          .css('margin-left', '-'+ (helper.width() + parseInt($(obj.el).css('margin-right'), 10) + 12) + 'px')
                          .on('mousedown', function (event) {
                              var body = $('body');
                              body.on('mouseup', tmp);
                              body.data('_field_update_timer', setTimeout(update, 700));
                              update(false);
                              // timer function
                              function tmp() {
                                  clearTimeout(body.data('_field_update_timer'));
                                  body.off('mouseup', tmp);
                              }
                              // update function
                              function update(notimer) {
                                  $(obj.el).focus();
                                  obj.keyDown($.Event("keydown"), {
                                      keyCode : ($(event.target).attr('type') == 'up' ? 38 : 40)
                                  });
                                  if (notimer !== false) $('body').data('_field_update_timer', setTimeout(update, 60));
                              }
                          });
                      pr += helper.width() + 12;
                      $(obj.el).css('padding-right', pr + 'px');
                      // remember helper
                      obj.helpers.arrows = helper;
                  }
                  if (obj.options.suffix !== '') {
                      // remove if already displayed
                      if (obj.helpers.suffix) $(obj.helpers.suffix).remove();
                      // add fresh
                      $(obj.el).after(
                          '<div class="w2ui-field-helper">'+
                              obj.options.suffix +
                          '</div>');
                      helper = $(obj.el).next();
                      helper
                          .css({
                              'color'          : $(obj.el).css('color'),
                              'font-family'    : $(obj.el).css('font-family'),
                              'font-size'      : $(obj.el).css('font-size'),
                              'padding-top'    : $(obj.el).css('padding-top'),
                              'padding-bottom' : $(obj.el).css('padding-bottom'),
                              'padding-left'   : '3px',
                              'padding-right'  : $(obj.el).css('padding-right'),
                              'margin-top'     : (parseInt($(obj.el).css('margin-top'), 10) + 2) + 'px',
                              'margin-bottom'  : (parseInt($(obj.el).css('margin-bottom'), 10) + 1) + 'px'
                          })
                          .on('click', function (event) {
                              if (obj.type == 'list') {
                                  $(obj.helpers.focus).find('input').focus();
                              } else {
                                  $(obj.el).focus();
                              }
                          });
  
                      helper.css('margin-left', '-'+ (w2utils.getSize(helper, 'width') + parseInt($(obj.el).css('margin-right'), 10) + 2) + 'px');
                      pr += helper.width() + 3;
                      $(obj.el).css('padding-right', pr + 'px');
                      // remember helper
                      obj.helpers.suffix = helper;
                  }
              }, 1);
          },
  
          addFocus: function () {
              var obj      = this;
              var options  = this.options;
              var width    = 0; // 11 - show search icon, 0 do not show
              var pholder;
              // clean up & init
              $(obj.helpers.focus).remove();
              // remember original tabindex
              var tabIndex = $(obj.el).attr('tabIndex');
              if (tabIndex && tabIndex != -1) obj.el._tabIndex = tabIndex;
              if (obj.el._tabIndex) tabIndex = obj.el._tabIndex;
              if (tabIndex == null) tabIndex = -1;
              // build helper
              var html =
                  '<div class="w2ui-field-helper">'+
                  '    <div class="w2ui-icon icon-search" style="opacity: 0; display: none"></div>'+
                  '    <input type="text" autocomplete="off" tabIndex="'+ tabIndex +'"/>'+
                  '</div>';
              $(obj.el).attr('tabindex', -1).before(html);
              var helper = $(obj.el).prev();
              obj.helpers.focus = helper;
              helper.css({
                      width           : $(obj.el).width(),
                      "margin-top"    : $(obj.el).css('margin-top'),
                      "margin-left"   : (parseInt($(obj.el).css('margin-left')) + parseInt($(obj.el).css('padding-left'))) + 'px',
                      "margin-bottom" : $(obj.el).css('margin-bottom'),
                      "margin-right"  : $(obj.el).css('margin-right')
                  })
                  .find('input')
                  .css({
                      cursor   : 'default',
                      width    : '100%',
                      outline  : 'none',
                      opacity  : 1,
                      margin   : 0,
                      border   : '1px solid transparent',
                      padding  : $(obj.el).css('padding-top'),
                      "padding-left"     : 0,
                      "margin-left"      : (width > 0 ? width + 6 : 0),
                      "background-color" : 'transparent'
                  });
              // INPUT events
              helper.find('input')
                  .on('click', function (event) {
                      if ($('#w2ui-overlay').length === 0) obj.focus(event);
                      event.stopPropagation();
                  })
                  .on('focus', function (event) {
                      pholder = $(obj.el).attr('placeholder');
                      $(obj.el).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
                      $(this).val('');
                      $(obj.el).triggerHandler('focus');
                      if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                  })
                  .on('blur', function (event) {
                      $(obj.el).css('outline', 'none');
                      $(this).val('');
                      obj.refresh();
                      $(obj.el).triggerHandler('blur');
                      if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                      if (pholder != null) $(obj.el).attr('placeholder', pholder);
                  })
                  .on('keydown', function (event) {
                      var el = this;
                      obj.keyDown(event);
                      setTimeout(function () {
                          if (el.value === '') $(obj.el).attr('placeholder', pholder); else $(obj.el).attr('placeholder', '');
                      }, 10);
                  })
                  .on('keyup', function (event) { obj.keyUp(event); })
                  .on('keypress', function (event) { obj.keyPress(event); });
              // MAIN div
              helper.on('click', function (event) { $(this).find('input').focus(); });
              obj.refresh();
          },
  
          addMulti: function () {
              var obj     = this;
              var options = this.options;
              // clean up & init
              $(obj.helpers.multi).remove();
              // build helper
              var html   = '';
              var margin =
                  'margin-top     : 0px; ' +
                  'margin-bottom  : 0px; ' +
                  'margin-left    : ' + $(obj.el).css('margin-left') + '; ' +
                  'margin-right   : ' + $(obj.el).css('margin-right') + '; '+
                  'width          : ' + (w2utils.getSize(obj.el, 'width')
                                      - parseInt($(obj.el).css('margin-left'), 10)
                                      - parseInt($(obj.el).css('margin-right'), 10))
                                      + 'px;';
              if (obj.type == 'enum') {
                  // remember original tabindex
                  var tabIndex = $(obj.el).attr('tabIndex');
                  if (tabIndex && tabIndex != -1) obj.el._tabIndex = tabIndex;
                  if (obj.el._tabIndex) tabIndex = obj.el._tabIndex;
                  if (tabIndex == null) tabIndex = -1;
  
                  html =  '<div class="w2ui-field-helper w2ui-list" style="'+ margin + '; box-sizing: border-box">'+
                          '    <div style="padding: 0px; margin: 0px; display: inline-block" class="w2ui-multi-items">'+
                          '    <ul>'+
                          '        <li style="padding-left: 0px; padding-right: 0px" class="nomouse">'+
                          '            <input type="text" style="width: 20px; margin: -3px 0 0; padding: 2px 0; border-color: white" autocomplete="off"' + ($(obj.el).prop('readonly') ? ' readonly="readonly"': '') + ($(obj.el).prop('disabled') ? ' disabled="disabled"': '') + ' tabindex="'+ tabIndex +'"/>'+
                          '        </li>'+
                          '    </ul>'+
                          '    </div>'+
                          '</div>';
              }
              if (obj.type == 'file') {
                  html =  '<div class="w2ui-field-helper w2ui-list" style="'+ margin + '; box-sizing: border-box">'+
                          '   <div style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px;">'+
                          '       <input class="file-input" type="file" style="width: 100%; height: 100%; opacity: 0;" name="attachment" multiple tabindex="-1"' + ($(obj.el).prop('readonly') ? ' readonly="readonly"': '') + ($(obj.el).prop('disabled') ? ' disabled="disabled"': '') + ($(obj.el).attr('accept') ? ' accept="'+ $(obj.el).attr('accept') +'"': '') + '/>'+
                          '   </div>'+
                          '    <div style="position: absolute; padding: 0px; margin: 0px; display: inline-block" class="w2ui-multi-items">'+
                          '        <ul><li style="padding-left: 0px; padding-right: 0px" class="nomouse"></li></ul>'+
                          '    </div>'+
                          '</div>';
              }
              // old bg and border
              var tmp = $(obj.el).data('tmp') || {};
              tmp['old-background-color'] = $(obj.el).css('background-color');
              tmp['old-border-color']     = $(obj.el).css('border-color');
              $(obj.el).data('tmp', tmp);
  
              $(obj.el)
                  .before(html)
                  .css({
                      'background-color' : 'transparent',
                      'border-color'     : 'transparent'
                  });
  
              var div    = $(obj.el).prev();
              obj.helpers.multi = div;
              if (obj.type == 'enum') {
                  $(obj.el).attr('tabindex', -1);
                  // INPUT events
                  div.find('input')
                      .on('click', function (event) {
                          if ($('#w2ui-overlay').length === 0) obj.focus(event);
                          $(obj.el).triggerHandler('click');
                      })
                      .on('focus', function (event) {
                          $(div).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
                          $(obj.el).triggerHandler('focus');
                          if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                      })
                      .on('blur', function (event) {
                          $(div).css('outline', 'none');
                          $(obj.el).triggerHandler('blur');
                          if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
                      })
                      .on('keyup',    function (event) { obj.keyUp(event); })
                      .on('keydown',  function (event) { obj.keyDown(event); })
                      .on('keypress', function (event) { obj.keyPress(event); });
                  // MAIN div
                  div.on('click', function (event) { $(this).find('input').focus(); });
              }
              if (obj.type == 'file') {
                  $(obj.el).css('outline', 'none');
                  div.on('click', function (event) {
                          $(obj.el).focus();
                          if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                          obj.blur(event);
                          obj.resize();
                          setTimeout(function () { div.find('input').click(); }, 10); // needed this when clicked on items div
                      })
                      .on('dragenter', function (event) {
                          if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                          $(div).addClass('w2ui-file-dragover');
                      })
                      .on('dragleave', function (event) {
                          if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                          var tmp = $(event.target).parents('.w2ui-field-helper');
                          if (tmp.length === 0) $(div).removeClass('w2ui-file-dragover');
                      })
                      .on('drop', function (event) {
                          if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
                          $(div).removeClass('w2ui-file-dragover');
                          var files = event.originalEvent.dataTransfer.files;
                          for (var i = 0, l = files.length; i < l; i++) obj.addFile.call(obj, files[i]);
                          // cancel to stop browser behaviour
                          event.preventDefault();
                          event.stopPropagation();
                      })
                      .on('dragover', function (event) {
                          // cancel to stop browser behaviour
                          event.preventDefault();
                          event.stopPropagation();
                      });
                  div.find('input')
                      .on('click', function (event) {
                          event.stopPropagation();
                      })
                      .on('change', function () {
                          if (typeof this.files !== "undefined") {
                              for (var i = 0, l = this.files.length; i < l; i++) {
                                  obj.addFile.call(obj, this.files[i]);
                              }
                          }
                      });
              }
              obj.refresh();
          },
  
          addFile: function (file) {
              var obj      = this;
              var options  = this.options;
              var selected = $(obj.el).data('selected');
              var newItem  = {
                  name     : file.name,
                  type     : file.type,
                  modified : file.lastModifiedDate,
                  size     : file.size,
                  content  : null,
                  file     : file
              };
              var size = 0;
              var cnt  = 0;
              var err;
              if (selected) {
                  for (var s = 0; s < selected.length; s++) {
                     // check for dups
                     if (selected[s].name == file.name && selected[s].size == file.size) return;
                     size += selected[s].size;
                     cnt++;
                 }
              }
              // trigger event
              var edata = obj.trigger({ phase: 'before', type: 'add', target: obj.el, file: newItem, total: cnt, totalSize: size });
              if (edata.isCancelled === true) return;
              // check params
              if (options.maxFileSize !== 0 && newItem.size > options.maxFileSize) {
                  err = 'Maximum file size is '+ w2utils.formatSize(options.maxFileSize);
                  if (options.silent === false) $(obj.el).w2tag(err);
                  console.log('ERROR: '+ err);
                  return;
              }
              if (options.maxSize !== 0 && size + newItem.size > options.maxSize) {
                  err = 'Maximum total size is '+ w2utils.formatSize(options.maxSize);
                  if (options.silent === false) $(obj.el).w2tag(err);
                  console.log('ERROR: '+ err);
                  return;
              }
              if (options.max !== 0 && cnt >= options.max) {
                  err = 'Maximum number of files is '+ options.max;
                  if (options.silent === false) $(obj.el).w2tag(err);
                  console.log('ERROR: '+ err);
                  return;
              }
              selected.push(newItem);
              // read file as base64
              if (typeof FileReader !== "undefined" && options.readContent === true) {
                  var reader = new FileReader();
                  // need a closure
                  reader.onload = (function () {
                      return function (event) {
                          var fl  = event.target.result;
                          var ind = fl.indexOf(',');
                          newItem.content = fl.substr(ind+1);
                          obj.refresh();
                          $(obj.el).trigger('change');
                          // event after
                          obj.trigger($.extend(edata, { phase: 'after' }));
                      };
                  })();
                  reader.readAsDataURL(file);
              } else {
                  obj.refresh();
                  $(obj.el).trigger('change');
              }
          },
  
          normMenu: function (menu) {
              if ($.isArray(menu)) {
                  for (var m = 0; m < menu.length; m++) {
                      if (typeof menu[m] == 'string') {
                          menu[m] = { id: menu[m], text: menu[m] };
                      } else {
                          if (menu[m].text != null && menu[m].id == null) menu[m].id = menu[m].text;
                          if (menu[m].text == null && menu[m].id != null) menu[m].text = menu[m].id;
                          if (menu[m].caption != null) menu[m].text = menu[m].caption;
                      }
                  }
                  return menu;
              } else if (typeof menu == 'function') {
                  return this.normMenu(menu());
              } else if (typeof menu == 'object') {
                  var tmp = [];
                  for (var m in menu) tmp.push({ id: m, text: menu[m] });
                  return tmp;
              }
          },
  
          getMonthHTML: function (month, year, selected) {
              var td          = new Date();
              var months      = w2utils.settings.fullmonths;
              var daysCount   = ['31', '28', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'];
              var today       = td.getFullYear() + '/' + (Number(td.getMonth()) + 1) + '/' + td.getDate();
              var days        = w2utils.settings.fulldays.slice();    // creates copy of the array
              var sdays       = w2utils.settings.shortdays.slice();   // creates copy of the array
              if (w2utils.settings.weekStarts != 'M') {
                  days.unshift(days.pop());
                  sdays.unshift(sdays.pop());
              }
              var options = this.options;
              if (options == null) options = {};
              // normalize date
              year  = w2utils.isInt(year)  ? parseInt(year)  : td.getFullYear();
              month = w2utils.isInt(month) ? parseInt(month) : td.getMonth() + 1;
              if (month > 12) { month -= 12; year++; }
              if (month < 1 || month === 0)  { month += 12; year--; }
              if (year/4 == Math.floor(year/4)) { daysCount[1] = '29'; } else { daysCount[1] = '28'; }
              options.current = month + '/' + year;
  
              // start with the required date
              td = new Date(year, month-1, 1);
              var weekDay = td.getDay();
              var dayTitle = '';
              for (var i = 0; i < sdays.length; i++) dayTitle += '<td title="'+ days[i] +'">' + sdays[i] + '</td>';
  
              var html  =
                  '<div class="w2ui-calendar-title title">'+
                  '    <div class="w2ui-calendar-previous previous"> <div></div> </div>'+
                  '    <div class="w2ui-calendar-next next"> <div></div> </div> '+
                          months[month-1] +', '+ year +
                  '</div>'+
                  '<table class="w2ui-calendar-days" cellspacing="0"><tbody>'+
                  '    <tr class="w2ui-day-title">' + dayTitle + '</tr>'+
                  '    <tr>';
  
              var day = 1;
              if (w2utils.settings.weekStarts != 'M') weekDay++;
              if(this.type === 'datetime') {
                  var dt_sel = w2utils.isDateTime(selected, options.format, true);
                  selected = w2utils.formatDate(dt_sel, w2utils.settings.dateFormat);
              }
              for (var ci = 1; ci < 43; ci++) {
                  if (weekDay === 0 && ci == 1) {
                      for (var ti = 0; ti < 6; ti++) html += '<td class="w2ui-day-empty">&#160;</td>';
                      ci += 6;
                  } else {
                      if (ci < weekDay || day > daysCount[month-1]) {
                          html += '<td class="w2ui-day-empty">&#160;</td>';
                          if ((ci) % 7 === 0) html += '</tr><tr>';
                          continue;
                      }
                  }
                  var dt  = year + '/' + month + '/' + day;
                  var DT  = new Date(dt);
                  var className = '';
                  if (DT.getDay() === 6) className  = ' w2ui-saturday';
                  if (DT.getDay() === 0) className  = ' w2ui-sunday';
                  if (dt == today) className += ' w2ui-today';
  
                  var dspDay  = day;
                  var col     = '';
                  var bgcol   = '';
                  var tmp_dt, tmp_dt_fmt;
                  if(this.type === 'datetime') {
                      // var fm = options.format.split('|')[0].trim();
                      // tmp_dt      = w2utils.formatDate(dt, fm);
                      tmp_dt      = w2utils.formatDateTime(dt, options.format);
                      tmp_dt_fmt  = w2utils.formatDate(dt, w2utils.settings.dateFormat);
                  } else {
                      tmp_dt      = w2utils.formatDate(dt, options.format);
                      tmp_dt_fmt  = tmp_dt;
                  }
                  if (options.colored && options.colored[tmp_dt_fmt] !== undefined) { // if there is predefined colors for dates
                      var tmp = options.colored[tmp_dt_fmt].split(':');
                      bgcol   = 'background-color: ' + tmp[0] + ';';
                      col     = 'color: ' + tmp[1] + ';';
                  }
                  html += '<td class="'+ (this.inRange(tmp_dt, true) ? 'w2ui-date ' + (tmp_dt_fmt == selected ? 'w2ui-date-selected' : '') : 'w2ui-blocked') + className + '" '+
                          '   style="'+ col + bgcol + '" date="'+ tmp_dt +'" data-date="'+ DT +'">'+
                              dspDay +
                          '</td>';
                  if (ci % 7 === 0 || (weekDay === 0 && ci == 1)) html += '</tr><tr>';
                  day++;
              }
              html += '</tr></tbody></table>';
              return html;
          },
  
          getYearHTML: function () {
              var months = w2utils.settings.shortmonths;
              var start_year = w2utils.settings.dateStartYear;
              var end_year = w2utils.settings.dateEndYear;
              var mhtml  = '';
              var yhtml  = '';
              for (var m = 0; m < months.length; m++) {
                  mhtml += '<div class="w2ui-jump-month" name="'+ m +'">'+ months[m] + '</div>';
              }
              for (var y = start_year; y <= end_year; y++) {
                  yhtml += '<div class="w2ui-jump-year" name="'+ y +'">'+ y + '</div>';
              }
              return '<div>'+ mhtml +'</div><div>'+ yhtml +'</div>';
          },
  
          getHourHTML: function () {
              var tmp = [];
              var options = this.options;
              if (options == null) options = { format: w2utils.settings.timeFormat };
              var h24 = (options.format.indexOf('h24') > -1);
              for (var a = 0; a < 24; a++) {
                  var time = (a >= 12 && !h24 ? a - 12 : a) + ':00' + (!h24 ? (a < 12 ? ' am' : ' pm') : '');
                  if (a == 12 && !h24) time = '12:00 pm';
                  if (!tmp[Math.floor(a/8)]) tmp[Math.floor(a/8)] = '';
                  var tm1 = this.fromMin(this.toMin(time));
                  var tm2 = this.fromMin(this.toMin(time) + 59);
                  if (this.type === 'datetime') {
                      var dt = w2utils.isDateTime(this.el.value, options.format, true);
                      var fm = options.format.split('|')[0].trim();
                      tm1 = w2utils.formatDate(dt, fm) + ' ' + tm1;
                      tm2 = w2utils.formatDate(dt, fm) + ' ' + tm2;
                  }
                  tmp[Math.floor(a/8)] += '<div class="'+ (this.inRange(tm1) || this.inRange(tm2) ? 'w2ui-time ' : 'w2ui-blocked') + '" hour="'+ a +'">'+ time +'</div>';
              }
              var html =
                  '<div class="w2ui-calendar">'+
                  '   <div class="w2ui-calendar-title">'+ w2utils.lang('Select Hour') +'</div>'+
                  '   <div class="w2ui-calendar-time"><table><tbody><tr>'+
                  '       <td>'+ tmp[0] +'</td>' +
                  '       <td>'+ tmp[1] +'</td>' +
                  '       <td>'+ tmp[2] +'</td>' +
                  '   </tr></tbody></table></div>'+
                  '</div>';
              return html;
          },
  
          getMinHTML: function (hour) {
              if (hour == null) hour = 0;
              var options = this.options;
              if (options == null) options = { format: w2utils.settings.timeFormat };
              var h24 = (options.format.indexOf('h24') > -1);
              var tmp = [];
              for (var a = 0; a < 60; a += 5) {
                  var time = (hour > 12 && !h24 ? hour - 12 : hour) + ':' + (a < 10 ? 0 : '') + a + ' ' + (!h24 ? (hour < 12 ? 'am' : 'pm') : '');
                  var tm   = time;
                  var ind  = a < 20 ? 0 : (a < 40 ? 1 : 2);
                  if (!tmp[ind]) tmp[ind] = '';
                  if (this.type === 'datetime') {
                      var dt = w2utils.isDateTime(this.el.value, options.format, true);
                      var fm = options.format.split('|')[0].trim();
                      tm = w2utils.formatDate(dt, fm) + ' ' + tm;
                  }
                  tmp[ind] += '<div class="'+ (this.inRange(tm) ? 'w2ui-time ' : 'w2ui-blocked') + '" min="'+ a +'">'+ time +'</div>';
              }
              var html =
                  '<div class="w2ui-calendar">'+
                  '   <div class="w2ui-calendar-title">'+ w2utils.lang('Select Minute') +'</div>'+
                  '   <div class="w2ui-calendar-time"><table><tbody><tr>'+
                  '       <td>'+ tmp[0] +'</td>' +
                  '       <td>'+ tmp[1] +'</td>' +
                  '       <td>'+ tmp[2] +'</td>' +
                  '   </tr></tbody></table></div>'+
                  '</div>';
              return html;
          },
  
          toMin: function (str) {
              if (typeof str != 'string') return null;
              var tmp = str.split(':');
              if (tmp.length === 2) {
                  tmp[0] = parseInt(tmp[0]);
                  tmp[1] = parseInt(tmp[1]);
                  if (str.indexOf('pm') != -1 && tmp[0] != 12) tmp[0] += 12;
              } else {
                  return null;
              }
              return tmp[0] * 60 + tmp[1];
          },
  
          fromMin: function (time) {
              var ret = '';
              if (time >= 24 * 60) time = time % (24 * 60);
              if (time < 0) time = 24 * 60 + time;
              var hour = Math.floor(time/60);
              var min  = ((time % 60) < 10 ? '0' : '') + (time % 60);
              var options = this.options;
              if (options == null) options = { format: w2utils.settings.timeFormat };
              if (options.format.indexOf('h24') != -1) {
                  ret = hour + ':' + min;
              } else {
                  ret = (hour <= 12 ? hour : hour - 12) + ':' + min + ' ' + (hour >= 12 ? 'pm' : 'am');
              }
              return ret;
          }
      };
  
      $.extend(w2field.prototype, w2utils.event);
      w2obj.field = w2field;
  
  }) (jQuery);
  