fateforge-tool/lib/w2ui/w2ui-1.5.js
2023-03-05 23:58:21 +01:00

21501 lines
No EOL
1 MiB
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* w2ui 1.5 (c) http://w2ui.com, vitmalina@gmail.com */
var w2ui = w2ui || {};
var w2obj = w2obj || {}; // expose object to be able to overwrite default functions
/************************************************
* Library: Web 1.5 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
* - color.html
* - refactored w2tag object, it has more potential with $().data('w2tag')
* - added w2utils.tooltip
* - w2tag options.hideOnFocus
* - w2tag options.maxWidth
* - w2tag options.auto - if set to true, then tag will show on mouseover
* - w2tag options.showOn, hideOn - if set to true, then tag will show on mouseover
* - w2tag options.className: 'w2ui-light' - for light color tag
* - w2menu options.items... remove t/f
* - w2menu options.items... keepOpen t/f
* - w2menu options.onRemove
* - w2menu options.hideOnRemove
* - w2menu - can not nest items, item.items and item.expanded
* - w2menu.options.topHTML
* - w2menu.options.menuStyle
* - naturalCompare
*
************************************************/
var w2utils = (function ($) {
var tmp = {}; // for some temp variables
var obj = {
version : '1.5',
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" : 2030, // end year for date picker
"macButtonOrder" : false // if true, Yes on the right side
},
isBin : isBin,
isInt : isInt,
isFloat : isFloat,
isMoney : isMoney,
isHex : isHex,
isAlphaNumeric : isAlphaNumeric,
isEmail : isEmail,
isIpAddress : isIpAddress,
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,
naturalCompare : naturalCompare,
lang : lang,
locale : locale,
getSize : getSize,
getStrWidth : getStrWidth,
scrollBarSize : scrollBarSize,
checkName : checkName,
checkUniqueId : checkUniqueId,
parseRoute : parseRoute,
cssPrefix : cssPrefix,
parseColor : parseColor,
hsv2rgb : hsv2rgb,
rgb2hsv : rgb2hsv,
tooltip : tooltip,
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 ||
navigator.userAgent.toLowerCase().indexOf('mobile') !== -1 ||
navigator.userAgent.toLowerCase().indexOf('android') !== -1)
? true : false),
isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') !== -1 ||
navigator.userAgent.toLowerCase().indexOf('trident') !== -1 )
? 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 = /^(0x)?[0-9a-fA-F]+$/;
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]+$/;
return email.test(val);
}
function isIpAddress (val) {
var re = new RegExp('^' +
'((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' +
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' +
'$');
return re.test(val);
}
function isDate (val, format, retDate) {
if (!val) return false;
var dt = 'Invalid Date';
var month, day, year;
if (format == null) format = w2utils.settings.dateFormat;
if (typeof val.getFullYear === 'function') { // date object
year = val.getFullYear();
month = val.getMonth() + 1;
day = val.getDate();
} else if (parseInt(val) == val && parseInt(val) > 0) {
val = new Date(parseInt(val));
year = val.getFullYear();
month = val.getMonth() + 1;
day = val.getDate();
} 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);
dt.setFullYear(year);
// 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 (typeof val.getFullYear === 'function') { // date object
if (retDate !== true) return true;
return val;
}
var intVal = parseInt(val);
if (intVal === val) {
if (intVal < 0) return false;
else if (retDate !== true) return true;
else return new Date(intVal);
}
var tmp = String(val).indexOf(' ');
if (tmp < 0) {
if (String(val).indexOf('T') < 0 || String(new Date(val)) == 'Invalid Date') return false;
else if (retDate !== true) return true;
else return new Date(val);
} else {
if (format == null) format = w2utils.settings.datetimeFormat;
var formats = format.split('|');
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.getFullYear === '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, ('000' + year).slice(-4))
.replace(/yyy/g, ('000' + year).slice(-4))
.replace(/yy/g, ('0' + year).slice(-2))
.replace(/(^|[^a-z$])y/g, '$1' + year) // only y's that are not preceded by a letter
.replace(/mm/g, ('0' + (month + 1)).slice(-2))
.replace(/dd/g, ('0' + date).slice(-2))
.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;
if (hour === 0) 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.length > 1 ? fmt[1].trim() : this.settings.timeFormat);
}
// older formats support
if (fmt[1] === 'h12') fmt[1] = 'h:m pm';
if (fmt[1] === 'h24') fmt[1] = 'h24:m';
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(/<(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>/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, callBack) {
if (!locale) locale = 'en-us';
// if the locale is an object, not a string, than we assume it's a
if (typeof locale !== "string" ) {
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",
success : function (data, status, xhr) {
w2utils.settings = $.extend(true, w2utils.settings, data);
if (typeof callBack === 'function') callBack();
},
error : function (xhr, status, msg) {
console.log('ERROR: Cannot load locale '+ locale);
}
});
}
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 === 8) {
color = {
r: parseInt(str.substr(0, 2), 16),
g: parseInt(str.substr(2, 2), 16),
b: parseInt(str.substr(4, 2), 16),
a: Math.round(parseInt(str.substr(6, 2), 16) / 255 * 100) / 100 // alpha channel 0-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)
}
}
function tooltip(msg, options) {
var actions, showOn = 'mouseenter', hideOn = 'mouseleave'
options = options || {}
if (options.showOn) {
showOn = options.showOn
delete options.showOn
}
if (options.hideOn) {
hideOn = options.hideOn
delete options.hideOn
}
// base64 is needed to avoid '"<> and other special chars conflicts
actions = 'on'+ showOn +'="$(this).w2tag(w2utils.base64decode(\'' + w2utils.base64encode(msg) + '\'),'
+ 'JSON.parse(w2utils.base64decode(\'' + w2utils.base64encode(JSON.stringify(options)) + '\')))"'
+ 'on'+ hideOn +'="$(this).w2tag()"'
return actions
}
/*! from litejs.com / MIT Licence
https://github.com/litejs/natural-compare-lite/blob/master/index.js */
/*
* @version 1.4.0
* @date 2015-10-26
* @stability 3 - Stable
* @author Lauri Rooden (https://github.com/litejs/natural-compare-lite)
* @license MIT License
*/
function naturalCompare(a, b) {
var i, codeA
, codeB = 1
, posA = 0
, posB = 0
, alphabet = String.alphabet;
function getCode(str, pos, code) {
if (code) {
for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i;
return +str.slice(pos - 1, i);
}
code = alphabet && alphabet.indexOf(str.charAt(pos));
return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code
: code < 46 ? 65 // -
: code < 48 ? code - 1
: code < 58 ? code + 18 // 0-9
: code < 65 ? code - 11
: code < 91 ? code + 11 // A-Z
: code < 97 ? code - 37
: code < 123 ? code + 5 // a-z
: code - 63;
}
if ((a+="") != (b+="")) for (;codeB;) {
codeA = getCode(a, posA++);
codeB = getCode(b, posB++);
if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
codeA = getCode(a, posA, posA);
codeB = getCode(b, posB, posA = i);
posB = i;
}
if (codeA != codeB) return (codeA < codeB) ? -1 : 1;
}
return 0;
}
})(jQuery);
/***********************************************************
* 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 (scope) edata.scope = scope
}
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 });
return this; // needed for chaining
},
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];
if (edata === '') edata = '*'
}
// 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: null, 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;
return this;
},
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 != null && (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
auto : null, // if auto true, then tag will show on mouseEnter and hide on mouseLeave
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
maxWidth : null, // max width
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
hideOnFocus : false, // hide tag on focus
hideOnBlur : false, // hide tag on blur
hideOnClick : false, // hide tag on document click
hideOnChange : true
}, 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 tag = $(el).data('w2tag');
if (tag) tag.hide();
});
return;
}
if (options.auto === true || options.showOn != null || options.hideOn != null) {
if (arguments.length == 0 || !text) {
return $(this).each(function (index, el) {
$(el).off('.w2tooltip')
})
} else {
return $(this).each(function (index, el) {
var showOn = 'mouseenter', hideOn = 'mouseleave'
if (options.showOn) {
showOn = String(options.showOn).toLowerCase()
delete options.showOn
}
if (options.hideOn) {
hideOn = String(options.hideOn).toLowerCase()
delete options.hideOn
}
if (!options.potision) { options.position = 'top|bottom' }
$(el)
.off('.w2tooltip')
.on(showOn + '.w2tooltip', function () {
options.auto = false;
$(this).w2tag(text, options)
})
.on(hideOn + '.w2tooltip', function () {
$(this).w2tag()
})
})
}
} else {
return $(this).each(function (index, el) {
// main object
var tag;
var origID = (options.id ? options.id : el.id);
if (origID == '') { // search for an id
origID = $(el).find('input').attr('id');
}
if (!origID) {
origID = 'noid';
}
var tmpID = w2utils.escapeId(origID);
if ($(this).data('w2tag') != null) {
tag = $(this).data('w2tag');
$.extend(tag.options, options);
} else {
tag = {
id : origID,
attachedTo: el, // element attached to
box : $('#w2ui-tag-' + tmpID), // tag itself
options : $.extend({}, options),
// methods
init : init, // attach events
hide : hide, // hide tag
getPos : getPos, // gets position of tag
isMoved : isMoved, // if called, will adjust position
// internal
tmp : {} // for temp variables
};
}
// show or hide tag
if (text === '' || text == null) {
tag.hide();
} else if (tag.box.length !== 0) {
// if already present
tag.box.find('.w2ui-tag-body')
.css(tag.options.css)
.attr('style', tag.options.style)
.addClass(tag.options.className)
.html(tag.options.html);
} else {
tag.tmp.originalCSS = '';
if ($(tag.attachedTo).length > 0) tag.tmp.originalCSS = $(tag.attachedTo)[0].style.cssText;
var tagStyles = 'white-space: nowrap;'
if (tag.options.maxWidth && w2utils.getStrWidth(text) > tag.options.maxWidth) {
tagStyles = 'width: '+ tag.options.maxWidth + 'px'
}
// insert
$('body').append(
'<div onclick="event.stopPropagation()" style="display: none;" id="w2ui-tag-'+ tag.id +'" '+
' class="w2ui-tag '+ ($(tag.attachedTo).parents('.w2ui-popup, .w2ui-overlay-popup, .w2ui-message').length > 0 ? 'w2ui-tag-popup' : '') + '">'+
' <div style="margin: -2px 0px 0px -2px; '+ tagStyles +'">'+
' <div class="w2ui-tag-body '+ tag.options.className +'" style="'+ (tag.options.style || '') +'">'+ text +'</div>'+
' </div>' +
'</div>');
tag.box = $('#w2ui-tag-' + tmpID);
$(tag.attachedTo).data('w2tag', tag); // make available to element tag attached to
setTimeout(init, 1);
}
return;
function init() {
tag.box.css('display', 'block');
if (!tag || !tag.box || !$(tag.attachedTo).offset()) return;
var pos = tag.getPos();
tag.box.css({
opacity : '1',
left : pos.left + 'px',
top : pos.top + 'px'
})
.data('w2tag', tag)
.find('.w2ui-tag-body').addClass(pos['posClass']);
tag.tmp.pos = pos.left + 'x' + pos.top;
$(tag.attachedTo)
.off('.w2tag')
.css(tag.options.css)
.addClass(tag.options.inputClass);
if (tag.options.hideOnKeyPress) {
$(tag.attachedTo).on('keypress.w2tag', tag.hide);
}
if (tag.options.hideOnFocus) {
$(tag.attachedTo).on('focus.w2tag', tag.hide);
}
if (options.hideOnChange) {
if (el.nodeName === 'INPUT') {
$(el).on('change.w2tag', tag.hide);
} else {
$(el).find('input').on('change.w2tag', tag.hide);
}
}
if (tag.options.hideOnBlur) {
$(tag.attachedTo).on('blur.w2tag', tag.hide);
}
if (tag.options.hideOnClick) {
$('body').on('click.w2tag' + (tag.id || ''), tag.hide);
}
if (typeof tag.options.onShow === 'function') {
tag.options.onShow();
}
isMoved();
}
// bind event to hide it
function hide() {
if (tag.box.length <= 0) return;
if (tag.tmp.timer) clearTimeout(tag.tmp.timer);
tag.box.remove();
if (tag.options.hideOnClick) {
$('body').off('.w2tag' + (tag.id || ''));
}
$(tag.attachedTo).off('.w2tag')
.removeClass(tag.options.inputClass)
.removeData('w2tag');
// restore original CSS
if ($(tag.attachedTo).length > 0) {
$(tag.attachedTo)[0].style.cssText = tag.tmp.originalCSS;
}
if (typeof tag.options.onHide === 'function') {
tag.options.onHide();
}
}
function isMoved(instant) {
// monitor if destroyed
var offset = $(tag.attachedTo).offset();
if ($(tag.attachedTo).length === 0 || (offset.left === 0 && offset.top === 0) || tag.box.find('.w2ui-tag-body').length === 0) {
tag.hide();
return;
}
var pos = getPos();
if (tag.tmp.pos !== pos.left + 'x' + pos.top) {
tag.box
.css(w2utils.cssPrefix({ 'transition': (instant ? '0s' : '.2s') }))
.css({
left: pos.left + 'px',
top : pos.top + 'px'
});
tag.tmp.pos = pos.left + 'x' + pos.top;
}
if (tag.tmp.timer) clearTimeout(tag.tmp.timer);
tag.tmp.timer = setTimeout(isMoved, 100);
}
function getPos() {
var offset = $(tag.attachedTo).offset();
var posClass = 'w2ui-tag-right';
var posLeft = parseInt(offset.left + tag.attachedTo.offsetWidth + (tag.options.left ? tag.options.left : 0));
var posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0));
var tagBody = tag.box.find('.w2ui-tag-body');
var width = tagBody[0].offsetWidth;
var height = tagBody[0].offsetHeight;
if (typeof tag.options.position === 'string' && tag.options.position.indexOf('|') !== -1) {
tag.options.position = tag.options.position.split('|');
}
if (tag.options.position === 'top') {
posClass = 'w2ui-tag-top';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - 14;
posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0)) - height - 10;
} else if (tag.options.position === 'bottom') {
posClass = 'w2ui-tag-bottom';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - 14;
posTop = parseInt(offset.top + tag.attachedTo.offsetHeight + (tag.options.top ? tag.options.top : 0)) + 10;
} else if (tag.options.position === 'left') {
posClass = 'w2ui-tag-left';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - width - 20;
posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0));
} else if (Array.isArray(tag.options.position)) {
// try to fit the tag on screen in the order defined in the array
var maxWidth = window.innerWidth;
var maxHeight = window.innerHeight;
for (var i = 0; i < tag.options.position.length; i++) {
var pos = tag.options.position[i];
if (pos === 'right') {
posClass = 'w2ui-tag-right';
posLeft = parseInt(offset.left + tag.attachedTo.offsetWidth + (tag.options.left ? tag.options.left : 0));
posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0));
if (posLeft+width <= maxWidth) break;
} else if (pos === 'left') {
posClass = 'w2ui-tag-left';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - width - 20;
posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0));
if (posLeft >= 0) break;
} else if (pos === 'top') {
posClass = 'w2ui-tag-top';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - 14;
posTop = parseInt(offset.top + (tag.options.top ? tag.options.top : 0)) - height - 10;
if(posLeft+width <= maxWidth && posTop >= 0) break;
} else if (pos === 'bottom') {
posClass = 'w2ui-tag-bottom';
posLeft = parseInt(offset.left + (tag.options.left ? tag.options.left : 0)) - 14;
posTop = parseInt(offset.top + tag.attachedTo.offsetHeight + (tag.options.top ? tag.options.top : 0)) + 10;
if (posLeft+width <= maxWidth && posTop+height <= maxHeight) break;
}
}
if (tagBody.data('posClass') !== posClass) {
tagBody.removeClass('w2ui-tag-right w2ui-tag-left w2ui-tag-top w2ui-tag-bottom')
.addClass(posClass)
.data('posClass', posClass);
}
}
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 : null, // show above control (if not, then as best needed)
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('.w2overlay'+ name);
if (typeof tmp_hide === 'function') tmp_hide();
}
if (obj.length > 0 && (obj[0].tagName == null || obj[0].tagName.toUpperCase() === 'BODY')) options.contextMenu = true;
if (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.');
}
var data_str = ''
if (options.data) {
Object.keys(options.data).forEach(function (item) {
data_str += 'data-'+ item + '="' + options.data[item] +'"'
})
}
// append
$('body').append(
'<div id="w2ui-overlay'+ name +'" style="display: none; left: 0px; top: 0px; '+ options.overlayStyle +'" '+ data_str +
' 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) {
$('#w2ui-overlay'+ name).data('keepOpen', true);
// if there is label for input, it will produce 2 click events
if (event.target.tagName.toUpperCase() === 'LABEL') event.stopPropagation();
})
.on('mousedown', function (event) {
var tmp = event.target.tagName.toUpperCase();
if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(tmp) === -1 && !options.selectable) {
event.preventDefault();
}
});
div1[0].hide = hide;
div1[0].resize = resize;
// need time to display
setTimeout(function () {
$(document).off('.w2overlay'+ name).on('click.w2overlay'+ name, hide);
if (typeof options.onShow === 'function') options.onShow();
resize();
}, 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);
// Allow clicking inside other overlays which belong to the elements inside this overlay
if (event && $($(event.target).closest('.w2ui-overlay').data('element')).closest('.w2ui-overlay')[0] === div1[0]) return;
if (div1.data('keepOpen') === true) {
div1.removeData('keepOpen');
return;
}
var result;
if (typeof options.onHide === 'function') result = options.onHide();
if (result === false) return;
div1.remove();
$(document).off('.w2overlay'+ name);
clearInterval(div1.data('timer'));
}
function resize() {
var div1 = $('#w2ui-overlay'+ name);
var div2 = div1.find(' > div');
var menu = $('#w2ui-overlay'+ name +' div.w2ui-menu');
var pos = {};
if (menu.length > 0) {
menu.css('overflow-y', 'hidden');
pos.scrollTop = menu.scrollTop();
pos.scrollLeft = menu.scrollLeft();
}
// 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.w2ui-menu');
if (h > $div.height()) {
div2.find('div.w2ui-menu').css('overflow-y', 'hidden');
}
}, 1);
setTimeout(function () {
var $div = div2.find('div.w2ui-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.w2ui-menu > table').width()) {
div2.find('div.w2ui-menu > table').css('overflow-x', 'hidden');
}
}, 1);
setTimeout(function () {
div2.find('div.w2ui-menu > table').css('overflow-x', 'auto');
}, 10);
} else {
div2.find('div.w2ui-menu').css('width', '100%');
}
// 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) && options.openAbove !== false) {
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);
}
if (menu.length > 0) {
menu.css('overflow-y', 'auto');
menu.scrollTop(pos.scrollTop);
menu.scrollLeft(pos.scrollLeft);
}
}
};
$.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,
hideOnRemove : false,
tmp : {}
};
var ret;
var obj = this;
var name = '';
if (menu === 'refresh') {
// if not show - call blur
if ($.fn.w2menuOptions && $.fn.w2menuOptions.name) name = '-' + $.fn.w2menuOptions.name;
if (options.name) name = '-' + options.name;
if ($('#w2ui-overlay'+ name).length > 0) {
options = $.extend($.fn.w2menuOptions, options);
var scrTop = $('#w2ui-overlay'+ name +' div.w2ui-menu').scrollTop();
$('#w2ui-overlay'+ name +' div.w2ui-menu').html(getMenuHTML());
$('#w2ui-overlay'+ name +' div.w2ui-menu').scrollTop(scrTop);
mresize();
} else {
$(this).w2menu(options);
}
} else if (menu === 'refresh-index') {
var $menu = $('#w2ui-overlay'+ name +' div.w2ui-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.remove === 'function' && typeof options.onRemove !== 'function') options.onRemove = options.remove;
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, parentIndex) {
var keepOpen = false;
var $tr = $(event.target).closest('tr');
if (event.shiftKey || event.metaKey || event.ctrlKey) {
keepOpen = true;
}
if (parentIndex == null) {
items = options.items
} else {
items = options.items[parentIndex].items
}
if ($(event.target).hasClass('remove')) {
if (typeof options.onRemove === 'function') {
options.onRemove({
index: index,
parentIndex: parentIndex,
item: items[index],
keepOpen: keepOpen,
originalEvent: event
});
}
keepOpen = !options.hideOnRemove;
$(event.target).closest('tr').remove();
mresize();
} else if ($tr.hasClass('has-sub-menu')) {
keepOpen = true;
if ($tr.hasClass('expanded')) {
items[index].expanded = false;
$tr.removeClass('expanded').addClass('collapsed').next().hide();
} else {
items[index].expanded = true;
$tr.addClass('expanded').removeClass('collapsed').next().show();
}
mresize();
} else if (typeof options.onSelect === 'function') {
var tmp = items;
if (typeof items == 'function') {
tmp = items(options.items[parentIndex])
}
if (tmp[index].keepOpen != null) {
keepOpen = tmp[index].keepOpen
}
options.onSelect({
index: index,
parentIndex: parentIndex,
item: tmp[index],
keepOpen: keepOpen,
originalEvent: event
});
}
// -- hide
if (items[index] == null || items[index].keepOpen !== true) {
var div = $('#w2ui-overlay'+ name);
div.removeData('keepOpen');
if (div.length > 0 && typeof div[0].hide === 'function' && !keepOpen) {
div[0].hide();
}
}
};
$.fn.w2menuDown = function (event, index, parentIndex) {
var items;
var $el = $(event.target).closest('tr');
var tmp = $($el.get(0)).find('.w2ui-icon');
if (parentIndex == null) {
items = options.items
} else {
items = options.items[parentIndex].items
}
var item = items[index];
if ((options.type === 'check' || options.type === 'radio') && item.group !== false
&& !$(event.target).hasClass('remove')
&& !$(event.target).closest('tr').hasClass('has-sub-menu')) {
item.checked = !item.checked;
if (item.checked) {
if (options.type === 'radio') {
tmp.parents('table').find('.w2ui-icon') // should not be closest, but parents
.removeClass('w2ui-icon-check')
.addClass('w2ui-icon-empty');
}
if (options.type === 'check' && item.group != null) {
items.forEach(function (sub, ind) {
if (sub.id == item.id) return;
if (sub.group === item.group && sub.checked) {
tmp.closest('table').find('tr[index='+ ind +'] .w2ui-icon')
.removeClass('w2ui-icon-check')
.addClass('w2ui-icon-empty');
items[ind].checked = false;
}
});
}
tmp.removeClass('w2ui-icon-empty').addClass('w2ui-icon-check');
} else if (options.type === 'check' && item.group == null && item.group !== false) {
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 += (options.topHTML || '') +
'<div class="w2ui-menu" style="top: '+ (options.search ? 40 : 0) + 'px;' + (options.menuStyle || '') + '">' +
getMenuHTML() +
'</div>';
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();
}, 250);
mresize();
// map functions
var div = $('#w2ui-overlay'+ name);
if (div.length > 0) {
div[0].mresize = mresize;
div[0].change = change;
}
}
return ret;
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.w2ui-menu').scrollTop();
cur.addClass('w2ui-selected');
if (options.tmp) {
options.tmp.contentHeight = $('#w2ui-overlay'+ name +' table').height() + (options.search ? 50 : 10)
+ (parseInt($('#w2ui-overlay'+ name +' .w2ui-menu').css('top')) || 0) // it menu is moved with menuStyle
+ (parseInt($('#w2ui-overlay'+ name +' .w2ui-menu').css('bottom')) || 0); // it menu is moved with menuStyle
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.w2ui-menu');
var height = el.height();
$('#w2ui-overlay'+ name +' div.w2ui-menu').scrollTop(scrTop);
if (top < scrTop || top + cur.height() > scrTop + height) {
$('#w2ui-overlay'+ name +' div.w2ui-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(items, subMenu, expanded, parentIndex) {
if (options.spinner) {
return '<table><tbody><tr><td style="padding: 5px 10px 13px 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="'+ (subMenu ? ' sub-menu' : '') +'"><tbody>';
var img = null, icon = null;
if (items == null) items = options.items;
if (!Array.isArray(items)) items = []
for (var f = 0; f < items.length; f++) {
var mitem = 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 && !Array.isArray(mitem.items) && mitem.group !== false) {
if (mitem.checked === true) icon = 'w2ui-icon-check'; else icon = 'w2ui-icon-empty';
}
if (mitem.hidden !== true) {
var imgd = '';
var txt = mitem.text;
var subMenu_dsp = '';
if (typeof options.render === 'function') txt = options.render(mitem, options);
if (typeof txt == 'function') txt = txt(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 (mitem.type !== 'break' && txt != null && txt !== '' && String(txt).substr(0, 2) != '--') {
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 && mitem.remove !== true && mitem.items == null) colspan++;
if (mitem.tooltip == null && mitem.hint != null) mitem.tooltip = mitem.hint; // for backward compatibility
var count_dsp = '';
if (mitem.remove === true) {
count_dsp = '<span class="remove">X</span>'
} else if (mitem.items != null) {
var _items = []
if (typeof mitem.items == 'function') {
_items = mitem.items(mitem)
} else if (Array.isArray(mitem.items)) {
_items = mitem.items
}
count_dsp = '<span></span>'
subMenu_dsp = '<tr style="'+ (mitem.expanded ? '' : 'display: none') +'">'+
' <td colspan="3">' + getMenuHTML(_items, true, !mitem.expanded, f) + '</td>'+
'<tr>';
} else {
if (mitem.count != null) count_dsp += '<span>' + mitem.count + '</span>'
if (mitem.hotkey != null) count_dsp += '<span class="hotkey">' + mitem.hotkey + '</span>'
}
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' : '')
+ (subMenu_dsp !== '' ? ' has-sub-menu' + (mitem.expanded ? ' expanded' : ' collapsed') : '')
+ '"'+
' onmousedown="if ('+ (mitem.disabled === true ? 'true' : 'false') + ') return;'+
' jQuery.fn.w2menuDown(event, '+ f +', '+ parentIndex +');"'+
' onclick="event.stopPropagation(); '+
' if ('+ (mitem.disabled === true ? 'true' : 'false') + ') return;'+
' jQuery.fn.w2menuClick(event, '+ f +', '+ parentIndex +');">'+
(subMenu ? '<td></td>' : '') + imgd +
' <td class="menu-text" colspan="'+ colspan +'">'+ w2utils.lang(txt) +'</td>'+
' <td class="menu-count">'+ count_dsp +'</td>'+
'</tr>'+ subMenu_dsp;
count++;
} else {
// horizontal line
var divText = txt.replace(/^-+/g, '')
menu_html += '<tr><td colspan="3" class="menu-divider '+ (divText != '' ? 'divider-text' : '') +'">'+
' <div class="line">'+ divText +'</div>'+
' <div class="text">'+ divText +'</div>'+
'</td></tr>';
}
}
items[f] = mitem;
}
if (count === 0 && options.msgNoItems) {
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']
];
}
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 (typeof options.color === 'string' && options.color.substr(0,1) === '#') options.color = options.color.substr(1);
if (options.fireChange == null) options.fireChange = true;
if ($('#w2ui-overlay').length === 0) {
$(el).w2overlay(getColorHTML(options), options);
} else { // only refresh contents
$('#w2ui-overlay .w2ui-colors').parent().html(getColorHTML(options));
$('#w2ui-overlay').show();
}
// bind events
$('#w2ui-overlay .w2ui-color')
.off('.w2color')
.on('mousedown.w2color', function (event) {
var color = $(event.originalEvent.target).attr('name'); // should not have #
index = $(event.originalEvent.target).attr('index').split(':');
if (el.tagName.toUpperCase() === 'INPUT') {
if (options.fireChange) $(el).change();
$(el).next().find('>div').css('background-color', color);
} else {
$(el).data('_color', color);
}
if (typeof options.onSelect === 'function') options.onSelect(color);
})
.on('mouseup.w2color', function () {
setTimeout(function () {
if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide();
}, 10);
});
$('#w2ui-overlay .color-original')
.off('.w2color')
.on('click.w2color', function (event) {
// restore original color
var tmp = w2utils.parseColor($(event.target).css('background-color'));
if (tmp != null) {
rgb = tmp;
hsv = w2utils.rgb2hsv(rgb);
setColor(hsv);
updateSlides();
refreshPalette();
}
});
$('#w2ui-overlay input')
.off('.w2color')
.on('mousedown.w2color', function (event) {
$('#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);
// console.log(rgb)
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(),
(Math.round(Number(rgb.a)*255)).toString(16).toUpperCase()
];
cl.forEach(function (item, ind) { if (item.length === 1) cl[ind] = '0' + item; });
newColor = cl[0] + cl[1] + cl[2] + cl[3];
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.toUpperCase() === 'INPUT') {
$(el).val(newColor).data('skipInit', true);
if (options.fireChange) $(el).change();
$(el).next().find('>div').css('background-color', '#'+newColor);
} else {
$(el).data('_color', newColor);
}
if (typeof options.onSelect === 'function') options.onSelect(newColor);
} else {
$('#w2ui-overlay .color-original').css('background-color', '#'+newColor);
}
}
var updateSlides = function () {
var $el1 = $('#w2ui-overlay .palette .value1');
var $el2 = $('#w2ui-overlay .rainbow .value2');
var $el3 = $('#w2ui-overlay .alpha .value2');
var offset1 = parseInt($el1.width()) / 2;
var offset2 = parseInt($el2.width()) / 2;
$el1.css({ 'left': hsv.s * 150 / 100 - offset1, 'top': (100 - hsv.v) * 125 / 100 - offset1});
$el2.css('left', hsv.h/(360/150) - offset2);
$el3.css('left', rgb.a*150 - offset2);
}
var refreshPalette = function() {
var cl = w2utils.hsv2rgb(hsv.h, 100, 100);
var rgb = cl.r + ',' + cl.g + ',' + cl.b;
$('#w2ui-overlay .palette').css('background-image',
'linear-gradient(90deg, rgba('+ rgb +',0) 0%, rgba(' + rgb + ',1) 100%)');
}
var mouseDown = function (event) {
var $el = $(this).find('.value1, .value2');
var offset = parseInt($el.width()) / 2;
if ($el.hasClass('move-x')) $el.css({ left: (event.offsetX - offset) + 'px' });
if ($el.hasClass('move-y')) $el.css({ top: (event.offsetY - offset) + 'px' });
initial = {
$el : $el,
x : event.pageX,
y : event.pageY,
width : $el.parent().width(),
height : $el.parent().height(),
left : parseInt($el.css('left')),
top : parseInt($el.css('top'))
};
mouseMove(event);
$('body').off('.w2color')
.on(mMove, mouseMove)
.on(mUp, mouseUp);
};
var mouseUp = function(event) {
$('body').off('.w2color');
};
var mouseMove = function(event) {
var $el = initial.$el;
var divX = event.pageX - initial.x;
var divY = event.pageY - initial.y;
var newX = initial.left + divX;
var newY = initial.top + divY;
var offset = parseInt($el.width()) / 2;
if (newX < -offset) newX = -offset;
if (newY < -offset) newY = -offset;
if (newX > initial.width - offset) newX = initial.width - offset;
if (newY > initial.height - offset) newY = initial.height - offset
if ($el.hasClass('move-x')) $el.css({ left : newX + 'px' });
if ($el.hasClass('move-y')) $el.css({ top : newY + 'px' });
// move
var name = $el.parent().attr('name');
var x = parseInt($el.css('left')) + offset;
var y = parseInt($el.css('top')) + offset;
if (name === 'palette') {
setColor({
s: Math.round(x / initial.width * 100),
v: Math.round(100 - (y / initial.height * 100))
});
}
if (name === 'rainbow') {
var h = Math.round(360 / 150 * x);
setColor({ h: h });
refreshPalette();
}
if (name === 'alpha') {
setColor({ a: parseFloat(Number(x / 150).toFixed(2)) });
}
}
if ($.fn._colorAdvanced === true || options.advanced === true) {
$('#w2ui-overlay .w2ui-color-tabs :nth-child(2)').click();
$('#w2ui-overlay').removeData('keepOpen');
}
setColor({}, true);
refreshPalette();
updateSlides();
// Events of iOS
var mDown = 'mousedown.w2color';
var mUp = 'mouseup.w2color';
var mMove = 'mousemove.w2color';
if (w2utils.isIOS) {
mDown = 'touchstart.w2color';
mUp = 'touchend.w2color';
mMove = 'touchmove.w2color ';
}
$('#w2ui-overlay .palette')
.off('.w2color')
.on('mousedown.w2color', mouseDown);
$('#w2ui-overlay .rainbow')
.off('.w2color')
.on('mousedown.w2color', mouseDown);
$('#w2ui-overlay .alpha')
.off('.w2color')
.on('mousedown.w2color', mouseDown);
// keyboard navigation
el.nav = function (direction) {
switch (direction) {
case 'up':
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-colors" 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="w2ui-color '+ (pal[i][j] === '' ? 'w2ui-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 style="padding: 8px; text-align: right;">' + (typeof options.html == 'string' ? options.html : '') + '</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
* - w2grid - grid widget
* - $().w2grid - jQuery wrapper
* - Dependencies: jQuery, w2utils, w2toolbar, w2fields
*
* == TODO ==
* - column autosize based on largest content
* - reorder columns/records
* - problem with .set() and arrays, array get extended too, but should be replaced
* - after edit stay on the same record option
* - if supplied array of ids, get should return array of records
* - allow functions in routeData (also add routeData to list/enum)
* - implement global routeData and all elements read from there
* - send parsed URL to the event if there is routeData
* - if you set searchData or sortData and call refresh() it should work
* - add selectType: 'none' so that no selection can be make but with mouse
* - reorder records with frozen columns
* - focus/blur for selectType = cell not display grayed out selection
* - frozen columns
- load more only on the right side
- scrolling on frozen columns is not working only on regular columns
* - copy or large number of records is slow
* - reusable search component (see https://github.com/vitmalina/w2ui/issues/914#issuecomment-107340524)
* - allow enum in inline edit (see https://github.com/vitmalina/w2ui/issues/911#issuecomment-107341193)
* - if record has no recid, then it should be index in the aray (should not be 0)
*
* == KNOWN ISSUES ==
* - bug: vs_start = 100 and more then 500 records, when scrolling empty sets
* - row drag and drop has bugs
* - Shift-click/Ctrl-click/Ctrl-Shift-Click selection is not as robust as it should be
*
* == 1.5 changes
* - $('#grid').w2grid() - if called w/o argument then it returns grid object
* - added statusRange : true,
* statusBuffered : false,
* statusRecordID : true,
* statusSelection : true,
* statusResponse : true,
* statusSort : true,
* statusSearch : true,
* - change selectAll() and selectNone() - return time it took
* - added vs_start and vs_extra
* - added update(cells) - updates only data in the grid (or cells)
* - add to docs onColumnDragStart, onColumnDragEnd
* - onSelect and onSelect should fire 1 time for selects with shift or selectAll(), selectNone()
* - record.w2ui.style[field_name]
* - use column field for style: { 1: 'color: red' }
* - added focus(), blur(), onFocus, onBlur
* - search.simple - if false, will not show up in simple search
* - search.operator - default operator to use with search field
* - search.operators - array of operators for the search
* - search.hidden - could not be cleared by the user
* - search.value - only for hidden searches
* - if .search(val) - search all fields
* - refactor reorderRow (not finished)
* - return JSON can now have summary array
* - frozen columns
* - added selectionSave, selectionRestore - for internal use
* - added additional search filter options for int, float, date, time
* - added getLineHTML
* - added lineNumberWidth
* - add searches.style
* - getColumn without params returns fields of all columns
* - getSearch without params returns fields of all searches
* - added column.tooltip
* - added hasFocus, refactored w2utils.keyboard
* - do not clear selection when clicked and it was not in focus
* - added record.w2ui.colspan
* - editable area extends with typing
* - removed onSubmit and onDeleted - now it uses onSave and onDelete
* - column.seachable - can be an object, which will create search
* - added null, not null filters
* - update(cells) - added argument cells
* - scrollIntoView(..., ..., instant) - added third argument
* - added onResizeDblClick
* - added onColumnDblClick
* - implemented showBubble
* - added show.searchAll
* - added show.searchHiddenMsg
* - added w2grid.operators
* - added w2grid.operatorsMap
* - move events into prototype
* - move rec.summary, rec.style, rec.editable -> into rec.w2ui.summary, rec.w2ui.style, rec.w2ui.editable
* - record: {
recid
field1
...
fieldN
w2ui: {
colspan: { field: 5, ...}
editable: true/false
hideCheckBox: true/false,
changes: {
field: chagned_value,
....
},
children: [
// similar to records array
// can have sub children
]
parent_recid: (internally set, id of the parent record, when children are copied to records array)
summary: true/false
style: 'string' - for entire row OR { field: 'string', ...} - per field
class: 'string' - for entire row OR { field: 'string', ...} - per field
}
}
* - added this.show.toolbarInput
* - disableCVS
* - added nestedFields: use field name containing dots as separator to look into objects
* - grid.message
* - added noReset option to localSort()
* - onColumnSelect
* - need to update PHP example
* - added scrollToColumn(field)
* - textSearch: 'begins' (default), 'contains', 'is', ...
* - added refreshBody
* - added response.total = -1 (or not present) to indicate that number of records is unknown
* - message(.., callBack) - added callBack
* - grid.msgEmpty
* - field.render(..., data) -- added last argument which is what grid thinks should be there
* - field.render can return { html, class, style } as an object
* - onSearchOpen (onSearch will have mutli and reset flags)
* - added httpHeaders
* - col.editable can be a function which will be called with the same args as col.render()
* - col.clipboardCopy - display icon to copy to clipboard
* - clipboardCopy - new function on grid level
* - getCellEditable(index, col_ind) -- return an 'editable' descriptor if cell is really editable
* - added stateId
* - rec.w2ui.class (and rec.w2ui.class { fname: '...' })
* - columnTooltip
* - expendable grids are still working
* - added search.type = 'color'
* - added getFirst
* - added stateColProps
* - added stateColDefaults
* - deprecated search.caption -> search.label
* - deprecated column.caption -> column.text
* - deprecated columnGroup.caption -> columnGroup.text
* - moved a lot of properties into prototype
* - showExtraOnSearch
* - added sortMap, searchMap
* - added column.hideable
* - added updateColumn
* - column.info {
icon : string|function|object,
style : string|function|object,
render : function,
fields : array|object,
showOn : 'mouseover|mouseenter|...',
hideOn : 'mouseout|mouseleave|...',
options : {} - will be passed to w2tag (for example options.potions = 'top')
}
* - added msgDeleteBtn
* - grid.toolbar.item batch
* - order.column
* - fixed select/unselect, not it can take array of ids
* - menuClick - changed parameters
* - column.text can be a function
* - columnGroup.text can be a function
*
************************************************************************/
(function ($) {
var w2grid = function(options) {
// public properties
this.name = null;
this.box = null; // HTML element that hold this element
this.columns = []; // { field, text, size, attr, render, hidden, gridMinWidth, editable }
this.columnGroups = []; // { span: int, text: 'string', master: true/false }
this.records = []; // { recid: int(requied), field1: 'value1', ... fieldN: 'valueN', style: 'string', changes: object }
this.summary = []; // arry of summary records, same structure as records array
this.searches = []; // { type, label, field, inTag, outTag, hidden }
this.sortMap = {}; // remap sort Fields
this.toolbar = {}; // if not empty object; then it is toolbar object
this.ranges = [];
this.menu = [];
this.searchData = [];
this.sortData = [];
this.total = 0; // server total
this.recid = null; // field from records to be used as recid
// internal
this.last = {
field : '',
label : '',
logic : 'OR',
search : '',
searchIds : [],
selection : {
indexes : [],
columns : {}
},
multi : false,
scrollTop : 0,
scrollLeft : 0,
colStart : 0, // for column virtual scrolling
colEnd : 0,
sortData : null,
sortCount : 0,
xhr : null,
loaded : false,
range_start : null,
range_end : null,
sel_ind : null,
sel_col : null,
sel_type : null,
edit_col : null,
isSafari : (/^((?!chrome|android).)*safari/i).test(navigator.userAgent)
}
$.extend(true, this, w2obj.grid);
this.show = $.extend(true, {}, w2grid.prototype.show);
this.postData = $.extend(true, {}, w2grid.prototype.postData);
this.routeData = $.extend(true, {}, w2grid.prototype.routeData);
this.httpHeaders = $.extend(true, {}, w2grid.prototype.httpHeaders);
this.buttons = $.extend(true, {}, w2grid.prototype.buttons);
this.operators = $.extend(true, {}, w2grid.prototype.operators);
this.operatorsMap = $.extend(true, {}, w2grid.prototype.operatorsMap);
this.stateColProps = $.extend(true, {}, w2grid.prototype.stateColProps);
this.stateColDefaults = $.extend(true, {}, w2grid.prototype.stateColDefaults);
$.extend(true, this, options);
}
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2grid = function(method) {
if ($.isPlainObject(method)) {
// check name parameter
if (!w2utils.checkName(method, 'w2grid')) return;
// remember items
var columns = method.columns;
var columnGroups = method.columnGroups;
var records = method.records;
var searches = method.searches;
var searchData = method.searchData;
var sortData = method.sortData;
// extend items
var object = new w2grid(method);
$.extend(object, { records: [], columns: [], searches: [], sortData: [], searchData: [], handlers: [] });
// reassign variables
var p;
if (columns) for (p = 0; p < columns.length; p++) object.columns[p] = $.extend(true, {}, columns[p]);
if (columnGroups) for (p = 0; p < columnGroups.length; p++) object.columnGroups[p] = $.extend(true, {}, columnGroups[p]);
if (searches) for (p = 0; p < searches.length; p++) object.searches[p] = $.extend(true, {}, searches[p]);
if (searchData) for (p = 0; p < searchData.length; p++) object.searchData[p] = $.extend(true, {}, searchData[p]);
if (sortData) for (p = 0; p < sortData.length; p++) object.sortData[p] = $.extend(true, {}, sortData[p]);
// check if there are records without recid
if (records) for (var r = 0; r < records.length; r++) {
if (records[r][object.recid] != null) {
records[r].recid = records[r][object.recid];
}
if (records[r].recid == null) {
console.log('ERROR: Cannot add records without recid. (obj: '+ object.name +')');
return;
}
object.records[r] = $.extend(true, {}, records[r]);
}
// add searches
for (var i = 0; i < object.columns.length; i++) {
var col = object.columns[i];
var search = col.searchable;
if (search == null || search === false || object.getSearch(col.field) != null) continue;
if ($.isPlainObject(search)) {
object.addSearch($.extend({ field: col.field, label: col.text, type: 'text' }, search));
} else {
var stype = col.searchable, attr = '';
if (col.searchable === true) { stype = 'text'; attr = 'size="20"'; }
object.addSearch({ field: col.field, label: col.text, type: stype, attr: attr });
}
}
// register new object
w2ui[object.name] = object;
// init toolbar
object.initToolbar();
object.updateToolbar();
// render if necessary
if ($(this).length !== 0) {
object.render($(this)[0]);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
}
// ====================================================
// -- Implementation of core functionality
w2grid.prototype = {
header : '',
url : '',
limit : 100,
offset : 0, // how many records to skip (for infinite scroll) when pulling from server
postData : {},
routeData : {},
httpHeaders : {},
show: {
header : false,
toolbar : false,
footer : false,
columnHeaders : true,
lineNumbers : false,
orderColumn : false,
expandColumn : false,
selectColumn : false,
emptyRecords : true,
toolbarReload : true,
toolbarColumns : true,
toolbarSearch : true,
toolbarInput : true,
toolbarAdd : false,
toolbarEdit : false,
toolbarDelete : false,
toolbarSave : false,
searchAll : true,
searchHiddenMsg : false,
statusRange : true,
statusBuffered : false,
statusRecordID : true,
statusSelection : true,
statusResponse : true,
statusSort : false,
statusSearch : false,
recordTitles : true,
selectionBorder : true,
skipRecords : true,
saveRestoreState: true
},
stateId : null, // Custom state name for stateSave, stateRestore and stateReset
hasFocus : false,
autoLoad : true, // for infinite scroll
fixedBody : true, // if false; then grid grows with data
recordHeight : 24, // should be in prototype
lineNumberWidth : null,
keyboard : true,
selectType : 'row', // can be row|cell
multiSearch : true,
multiSelect : true,
multiSort : true,
reorderColumns : false,
reorderRows : false,
showExtraOnSearch : 0, // show extra records before and after on search
markSearch : true,
columnTooltip : 'top|bottom', // can be normal, top, bottom, left, right
disableCVS : false, // disable Column Virtual Scroll
textSearch : 'begins', // default search type for text
nestedFields : true, // use field name containing dots as separator to look into object
vs_start : 150,
vs_extra : 15,
style : '',
method : null, // if defined, then overwrited ajax method
dataType : null, // if defined, then overwrited w2utils.settings.dataType
parser : null,
// these column properties will be saved in stateSave()
stateColProps: {
text : false,
field : true,
size : true,
min : false,
max : false,
gridMinWidth : false,
sizeCorrected : false,
sizeCalculated : true,
sizeOriginal : true,
sizeType : true,
hidden : true,
sortable : false,
searchable : false,
clipboardCopy : false,
resizable : false,
hideable : false,
attr : false,
style : false,
render : false,
title : false,
editable : false,
frozen : true,
info : false,
},
// these are the stateSave() fallback values if the property to save is not a property of the column object
stateColDefaults: {
text : '', // column text
field : '', // field name to map column to a record
size : null, // size of column in px or %
min : 20, // minimum width of column in px
max : null, // maximum width of column in px
gridMinWidth : null, // minimum width of the grid when column is visible
sizeCorrected : null, // read only, corrected size (see explanation below)
sizeCalculated : null, // read only, size in px (see explanation below)
sizeOriginal : null,
sizeType : null,
hidden : false, // indicates if column is hidden
sortable : false, // indicates if column is sortable
searchable : false, // indicates if column is searchable, bool/string: int,float,date,...
clipboardCopy : false,
resizable : true, // indicates if column is resizable
hideable : true, // indicates if column can be hidden
attr : '', // string that will be inside the <td ... attr> tag
style : '', // additional style for the td tag
render : null, // string or render function
title : null, // string or function for the title property for the column cells
editable : {}, // editable object if column fields are editable
frozen : false, // indicates if the column is fixed to the left
info : null // info bubble, can be bool/object
},
msgDelete : 'Are you sure you want to delete NN records?',
msgDeleteBtn : 'Delete',
msgNotJSON : 'Returned data is not in valid JSON format.',
msgAJAXerror : 'AJAX error. See console for more details.',
msgRefresh : 'Refreshing...',
msgNeedReload : 'Your remote data source record count has changed, reloading from the first record.',
msgEmpty : '', // if not blank, then it is message when server returns no records
buttons: {
'reload' : { type: 'button', id: 'w2ui-reload', icon: 'w2ui-icon-reload', tooltip: 'Reload data in the list' },
'columns' : { type: 'drop', id: 'w2ui-column-on-off', icon: 'w2ui-icon-columns', tooltip: 'Show/hide columns', arrow: false, html: '' },
'search' : { type: 'html', id: 'w2ui-search',
html: '<div class="w2ui-icon icon-search-down w2ui-search-down" '+
'onclick="var grid = w2ui[jQuery(this).parents(\'div.w2ui-grid\').attr(\'name\')]; grid.searchShowFields()"></div>'
},
'search-go': { type: 'drop', id: 'w2ui-search-advanced', icon: 'w2ui-icon-search', text: 'Search', tooltip: 'Open Search Fields' },
'add' : { type: 'button', id: 'w2ui-add', text: 'Add New', tooltip: 'Add new record', icon: 'w2ui-icon-plus' },
'edit' : { type: 'button', id: 'w2ui-edit', text: 'Edit', tooltip: 'Edit selected record', icon: 'w2ui-icon-pencil', disabled: true },
'delete' : { type: 'button', id: 'w2ui-delete', text: 'Delete', tooltip: 'Delete selected records', icon: 'w2ui-icon-cross', disabled: true },
'save' : { type: 'button', id: 'w2ui-save', text: 'Save', tooltip: 'Save changed records', icon: 'w2ui-icon-check' }
},
operators: { // for search fields
"text" : ['is', 'begins', 'contains', 'ends'],
"number" : ['=', 'between', '>', '<', '>=', '<='],
"date" : ['is', 'between', { oper: 'less', text: 'before'}, { oper: 'more', text: 'after' }],
"list" : ['is'],
"hex" : ['is', 'between'],
"color" : ['is', 'begins', 'contains', 'ends'],
"enum" : ['in', 'not in']
// -- all possible
// "text" : ['is', 'begins', 'contains', 'ends'],
// "number" : ['is', 'between', 'less:less than', 'more:more than', 'null:is null', 'not null:is not null'],
// "list" : ['is', 'null:is null', 'not null:is not null'],
// "enum" : ['in', 'not in', 'null:is null', 'not null:is not null']
},
operatorsMap: {
"text" : "text",
"int" : "number",
"float" : "number",
"money" : "number",
"currency" : "number",
"percent" : "number",
"hex" : "hex",
"alphanumeric" : "text",
"color" : "color",
"date" : "date",
"time" : "date",
"datetime" : "date",
"list" : "list",
"combo" : "text",
"enum" : "enum",
"file" : "enum",
"select" : "list",
"radio" : "list",
"checkbox" : "list",
"toggle" : "list"
},
// events
onAdd : null,
onEdit : null,
onRequest : null, // called on any server event
onLoad : null,
onDelete : null,
onSave : null,
onSelect : null,
onUnselect : null,
onClick : null,
onDblClick : null,
onContextMenu : null,
onMenuClick : null, // when context menu item selected
onColumnClick : null,
onColumnDblClick : null,
onColumnResize : null,
onColumnAutoResize : null,
onSort : null,
onSearch : null,
onSearchOpen : null,
onChange : null, // called when editable record is changed
onRestore : null, // called when editable record is restored
onExpand : null,
onCollapse : null,
onError : null,
onKeydown : null,
onToolbar : null, // all events from toolbar
onColumnOnOff : null,
onCopy : null,
onPaste : null,
onSelectionExtend : null,
onEditField : null,
onRender : null,
onRefresh : null,
onReload : null,
onResize : null,
onDestroy : null,
onStateSave : null,
onStateRestore : null,
onFocus : null,
onBlur : null,
onReorderRow : null,
add: function (record, first) {
if (!$.isArray(record)) record = [record];
var added = 0;
for (var i = 0; i < record.length; i++) {
var rec = record[i];
if (rec[this.recid] != null) {
rec.recid = rec[this.recid];
}
if (rec.recid == null) {
console.log('ERROR: Cannot add record without recid. (obj: '+ this.name +')');
continue;
}
if (rec.w2ui && rec.w2ui.summary === true) {
if (first) this.summary.unshift(rec); else this.summary.push(rec);
} else {
if (first) this.records.unshift(rec); else this.records.push(rec);
}
added++;
}
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
this.total = this.records.length;
this.localSort(false, true);
this.localSearch();
// do not call this.refresh(), this is unnecessary, heavy, and messes with the toolbar.
this.refreshBody();
this.resizeRecords();
return added;
}
this.refresh(); // ?? should it be reload?
return added;
},
find: function (obj, returnIndex) {
if (obj == null) obj = {};
var recs = [];
var hasDots = false;
// check if property is nested - needed for speed
for (var o in obj) if (String(o).indexOf('.') != -1) hasDots = true;
// look for an item
for (var i = 0; i < this.records.length; i++) {
var match = true;
for (var o in obj) {
var val = this.records[i][o];
if (hasDots && String(o).indexOf('.') != -1) val = this.parseField(this.records[i], o);
if (obj[o] == 'not-null') {
if (val == null || val === '') match = false;
} else {
if (obj[o] != val) match = false;
}
}
if (match && returnIndex !== true) recs.push(this.records[i].recid);
if (match && returnIndex === true) recs.push(i);
}
return recs;
},
set: function (recid, record, noRefresh) { // does not delete existing, but overrides on top of it
if ((typeof recid == 'object') && (recid !== null)) {
noRefresh = record;
record = recid;
recid = null;
}
// update all records
if (recid == null) {
for (var i = 0; i < this.records.length; i++) {
$.extend(true, this.records[i], record); // recid is the whole record
}
if (noRefresh !== true) this.refresh();
} else { // find record to update
var ind = this.get(recid, true);
if (ind == null) return false;
var isSummary = (this.records[ind] && this.records[ind].recid == recid ? false : true);
if (isSummary) {
$.extend(true, this.summary[ind], record);
} else {
$.extend(true, this.records[ind], record);
}
if (noRefresh !== true) this.refreshRow(recid, ind); // refresh only that record
}
return true;
},
get: function (recid, returnIndex) {
// search records
if ($.isArray(recid)) {
var recs = [];
for (var i = 0; i < recid.length; i++) {
var v = this.get(recid[i], returnIndex);
if (v !== null)
recs.push(v);
}
return recs;
} else {
// get() must be fast, implements a cache to bypass loop over all records
// most of the time.
var idCache = this.last.idCache;
if (!idCache) {
this.last.idCache = idCache = {};
}
var i = idCache[recid];
if (typeof(i) === "number") {
if (i >= 0 && i < this.records.length && this.records[i].recid == recid) {
if (returnIndex === true) return i; else return this.records[i];
}
// summary indexes are stored as negative numbers, try them now.
i = ~i;
if (i >= 0 && i < this.summary.length && this.summary[i].recid == recid) {
if (returnIndex === true) return i; else return this.summary[i];
}
// wrong index returned, clear cache
this.last.idCache = idCache = {};
}
for (var i = 0; i < this.records.length; i++) {
if (this.records[i].recid == recid) {
idCache[recid] = i;
if (returnIndex === true) return i; else return this.records[i];
}
}
// search summary
for (var i = 0; i < this.summary.length; i++) {
if (this.summary[i].recid == recid) {
idCache[recid] = ~i;
if (returnIndex === true) return i; else return this.summary[i];
}
}
return null;
}
},
getFirst: function () {
if (this.records.length == 0) return null;
var recid = this.records[0].recid;
var tmp = this.last.searchIds;
if (this.searchData.length > 0) {
if (Array.isArray(tmp) && tmp.length > 0) {
recid = this.records[tmp[0]].recid;
} else {
recid = null;
}
}
return recid;
},
remove: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.records.length-1; r >= 0; r--) {
if (this.records[r].recid == arguments[a]) { this.records.splice(r, 1); removed++; }
}
for (var r = this.summary.length-1; r >= 0; r--) {
if (this.summary[r].recid == arguments[a]) { this.summary.splice(r, 1); removed++; }
}
}
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
this.localSort(false, true);
this.localSearch();
}
this.refresh();
return removed;
},
addColumn: function (before, columns) {
var added = 0;
if (arguments.length == 1) {
columns = before;
before = this.columns.length;
} else {
if (typeof before == 'string') before = this.getColumn(before, true);
if (before == null) before = this.columns.length;
}
if (!$.isArray(columns)) columns = [columns];
for (var i = 0; i < columns.length; i++) {
this.columns.splice(before, 0, columns[i]);
// if column is searchable, add search field
if (columns[i].searchable) {
var stype = columns[i].searchable;
var attr = '';
if (columns[i].searchable === true) { stype = 'text'; attr = 'size="20"'; }
this.addSearch({ field: columns[i].field, label: columns[i].label, type: stype, attr: attr });
}
before++;
added++;
}
this.refresh();
return added;
},
removeColumn: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.columns.length-1; r >= 0; r--) {
if (this.columns[r].field == arguments[a]) {
if (this.columns[r].searchable) this.removeSearch(arguments[a]);
this.columns.splice(r, 1);
removed++;
}
}
}
this.refresh();
return removed;
},
getColumn: function (field, returnIndex) {
// no arguments - return fields of all columns
if (arguments.length === 0) {
var ret = [];
for (var i = 0; i < this.columns.length; i++) ret.push(this.columns[i].field);
return ret;
}
// find column
for (var i = 0; i < this.columns.length; i++) {
if (this.columns[i].field == field) {
if (returnIndex === true) return i; else return this.columns[i];
}
}
return null;
},
updateColumn: function (names, updates) {
var obj = this;
var effected = 0;
names = (Array.isArray(names) ? names : [names]);
names.forEach(function (colName) {
obj.columns.forEach(function (col) {
if (col.field == colName) {
var _updates = $.extend(true, {}, updates);
Object.keys(_updates).forEach(function (key) {
// if it is a function
if (typeof _updates[key] == 'function') {
_updates[key] = _updates[key](col);
}
if (col[key] != _updates[key]) effected++
})
$.extend(true, col, _updates)
}
})
});
if (effected > 0) {
this.refresh(); // need full refresh due to colgroups not resiging properly
}
return effected;
},
toggleColumn: function () {
return this.updateColumn(Array.from(arguments), { hidden: function (col) { return !col.hidden } });
},
showColumn: function () {
return this.updateColumn(Array.from(arguments), { hidden: false });
},
hideColumn: function () {
return this.updateColumn(Array.from(arguments), { hidden: true });
},
addSearch: function (before, search) {
var added = 0;
if (arguments.length == 1) {
search = before;
before = this.searches.length;
} else {
if (typeof before == 'string') before = this.getSearch(before, true);
if (before == null) before = this.searches.length;
}
if (!$.isArray(search)) search = [search];
for (var i = 0; i < search.length; i++) {
this.searches.splice(before, 0, search[i]);
before++;
added++;
}
this.searchClose();
return added;
},
removeSearch: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.searches.length-1; r >= 0; r--) {
if (this.searches[r].field == arguments[a]) { this.searches.splice(r, 1); removed++; }
}
}
this.searchClose();
return removed;
},
getSearch: function (field, returnIndex) {
// no arguments - return fields of all searches
if (arguments.length === 0) {
var ret = [];
for (var i = 0; i < this.searches.length; i++) ret.push(this.searches[i].field);
return ret;
}
// find search
for (var i = 0; i < this.searches.length; i++) {
if (this.searches[i].field == field) {
if (returnIndex === true) return i; else return this.searches[i];
}
}
return null;
},
toggleSearch: function () {
var effected = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.searches.length-1; r >= 0; r--) {
if (this.searches[r].field == arguments[a]) {
this.searches[r].hidden = !this.searches[r].hidden;
effected++;
}
}
}
this.searchClose();
return effected;
},
showSearch: function () {
var shown = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.searches.length-1; r >= 0; r--) {
if (this.searches[r].field == arguments[a] && this.searches[r].hidden !== false) {
this.searches[r].hidden = false;
shown++;
}
}
}
this.searchClose();
return shown;
},
hideSearch: function () {
var hidden = 0;
for (var a = 0; a < arguments.length; a++) {
for (var r = this.searches.length-1; r >= 0; r--) {
if (this.searches[r].field == arguments[a] && this.searches[r].hidden !== true) {
this.searches[r].hidden = true;
hidden++;
}
}
}
this.searchClose();
return hidden;
},
getSearchData: function (field) {
for (var i = 0; i < this.searchData.length; i++) {
if (this.searchData[i].field == field) return this.searchData[i];
}
return null;
},
localSort: function (silent, noResetRefresh) {
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (url) {
console.log('ERROR: grid.localSort can only be used on local data source, grid.url should be empty.');
return;
}
if ($.isEmptyObject(this.sortData)) return;
var time = (new Date()).getTime();
var obj = this;
// process date fields
obj.selectionSave();
obj.prepareData();
if (!noResetRefresh) {
obj.reset();
}
// process sortData
for (var i = 0; i < this.sortData.length; i++) {
var column = this.getColumn(this.sortData[i].field);
if (!column) return;
if (typeof column.render == 'string') {
if (['date', 'age'].indexOf(column.render.split(':')[0]) != -1) {
this.sortData[i]['field_'] = column.field + '_';
}
if (['time'].indexOf(column.render.split(':')[0]) != -1) {
this.sortData[i]['field_'] = column.field + '_';
}
}
}
// prepare paths and process sort
preparePaths();
this.records.sort(function (a, b) {
return compareRecordPaths(a, b);
});
cleanupPaths();
obj.selectionRestore(noResetRefresh);
time = (new Date()).getTime() - time;
if (silent !== true && obj.show.statusSort) {
setTimeout(function () {
obj.status(w2utils.lang('Sorting took') + ' ' + time/1000 + ' ' + w2utils.lang('sec'));
}, 10);
}
return time;
// grab paths before sorting for efficiency and because calling obj.get()
// while sorting 'obj.records' is unsafe, at least on webkit
function preparePaths() {
for (var i = 0; i < obj.records.length; i++) {
var rec = obj.records[i];
if (rec.w2ui && rec.w2ui.parent_recid != null) {
rec.w2ui._path = getRecordPath(rec);
}
}
}
// cleanup and release memory allocated by preparePaths()
function cleanupPaths() {
for (var i = 0; i < obj.records.length; i++) {
var rec = obj.records[i];
if (rec.w2ui && rec.w2ui.parent_recid != null) {
rec.w2ui._path = null;
}
}
}
// compare two paths, from root of tree to given records
function compareRecordPaths(a, b) {
if ((!a.w2ui || a.w2ui.parent_recid == null) && (!b.w2ui || b.w2ui.parent_recid == null)) {
return compareRecords(a, b); // no tree, fast path
}
var pa = getRecordPath(a);
var pb = getRecordPath(b);
for (var i = 0; i < Math.min(pa.length, pb.length); i++) {
var diff = compareRecords(pa[i], pb[i]);
if (diff !== 0) return diff; // different subpath
}
if (pa.length > pb.length) return 1;
if (pa.length < pb.length) return -1;
console.log('ERROR: two paths should not be equal.');
return 0;
}
// return an array of all records from root to and including 'rec'
function getRecordPath(rec) {
if (!rec.w2ui || rec.w2ui.parent_recid == null) return [rec];
if (rec.w2ui._path)
return rec.w2ui._path;
// during actual sort, we should never reach this point
var subrec = obj.get(rec.w2ui.parent_recid);
if (!subrec) {
console.log('ERROR: no parent record: '+rec.w2ui.parent_recid);
return [rec];
}
return (getRecordPath(subrec).concat(rec));
}
// compare two records according to sortData and finally recid
function compareRecords(a, b) {
if (a === b) return 0; // optimize, same object
for (var i = 0; i < obj.sortData.length; i++) {
var fld = obj.sortData[i].field;
var sortFld = (obj.sortData[i].field_) ? obj.sortData[i].field_ : fld;
var aa = a[sortFld];
var bb = b[sortFld];
if (String(fld).indexOf('.') != -1) {
aa = obj.parseField(a, sortFld);
bb = obj.parseField(b, sortFld);
}
var col = obj.getColumn(fld);
if (col && col.editable != null) { // for drop editable fields and drop downs
if ($.isPlainObject(aa) && aa.text) aa = aa.text;
if ($.isPlainObject(bb) && bb.text) bb = bb.text;
}
var ret = compareCells(aa, bb, i, obj.sortData[i].direction, col.sortMode || 'default');
if (ret !== 0) return ret;
}
// break tie for similar records,
// required to have consistent ordering for tree paths
var ret = compareCells(a.recid, b.recid, -1, 'asc');
return ret;
}
// compare two values, aa and bb, producing consistent ordering
function compareCells(aa, bb, i, direction, sortMode) {
// if both objects are strictly equal, we're done
if (aa === bb)
return 0;
// all nulls, empty and undefined on bottom
if ((aa == null || aa === "") && (bb != null && bb !== ""))
return 1;
if ((aa != null && aa !== "") && (bb == null || bb === ""))
return -1;
var dir = (direction.toLowerCase() === 'asc') ? 1 : -1;
// for different kind of objects, sort by object type
if (typeof aa != typeof bb)
return (typeof aa > typeof bb) ? dir : -dir;
// for different kind of classes, sort by classes
if (aa.constructor.name != bb.constructor.name)
return (aa.constructor.name > bb.constructor.name) ? dir : -dir;
// if we're dealing with non-null objects, call valueOf().
// this mean that Date() or custom objects will compare properly.
if (aa && typeof aa == 'object')
aa = aa.valueOf();
if (bb && typeof bb == 'object')
bb = bb.valueOf();
// if we're still dealing with non-null objects that have
// a useful Object => String conversion, convert to string.
var defaultToString = {}.toString;
if (aa && typeof aa == 'object' && aa.toString != defaultToString)
aa = String(aa);
if (bb && typeof bb == 'object' && bb.toString != defaultToString)
bb = String(bb);
// do case-insensitive string comparison
if (typeof aa == 'string')
aa = $.trim(aa.toLowerCase());
if (typeof bb == 'string')
bb = $.trim(bb.toLowerCase());
switch (sortMode) {
case 'natural':
sortMode = w2utils.naturalCompare;
break;
}
if (typeof sortMode == 'function') {
return sortMode(aa,bb) * dir;
}
// compare both objects
if (aa > bb)
return dir;
if (aa < bb)
return -dir;
return 0;
}
},
localSearch: function (silent) {
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (url) {
console.log('ERROR: grid.localSearch can only be used on local data source, grid.url should be empty.');
return;
}
var time = (new Date()).getTime();
var obj = this;
var defaultToString = {}.toString;
var duplicateMap = {};
this.total = this.records.length;
// mark all records as shown
this.last.searchIds = [];
// prepare date/time fields
this.prepareData();
// hide records that did not match
if (this.searchData.length > 0 && !url) {
this.total = 0;
for (var i = 0; i < this.records.length; i++) {
var rec = this.records[i];
var match = searchRecord(rec);
if (match) {
if (rec && rec.w2ui) addParent(rec.w2ui.parent_recid);
if (this.showExtraOnSearch > 0) {
var before = this.showExtraOnSearch;
var after = this.showExtraOnSearch;
if (i < before) before = i;
if (i + after > this.records.length) after = this.records.length - i;
if (before > 0) {
for (var j = i - before; j < i; j++) {
if (this.last.searchIds.indexOf(j) < 0)
this.last.searchIds.push(j);
}
}
if (this.last.searchIds.indexOf(i) < 0) this.last.searchIds.push(i);
if (after > 0) {
for (var j = (i + 1) ; j <= (i + after ) ; j++) {
if (this.last.searchIds.indexOf(j) < 0) this.last.searchIds.push(j);
}
}
} else {
this.last.searchIds.push(i);
}
}
}
this.total = this.last.searchIds.length;
}
time = (new Date()).getTime() - time;
if (silent !== true && obj.show.statusSearch) {
setTimeout(function () {
obj.status(w2utils.lang('Search took') + ' ' + time/1000 + ' ' + w2utils.lang('sec'));
}, 10);
}
return time;
// check if a record (or one of its closed children) matches the search data
function searchRecord(rec) {
var fl = 0;
var orEqual = false;
for (var j = 0; j < obj.searchData.length; j++) {
var sdata = obj.searchData[j];
var search = obj.getSearch(sdata.field);
if (sdata == null) continue;
if (search == null) search = { field: sdata.field, type: sdata.type };
var val1b = obj.parseField(rec, search.field);
var val1 = (val1b !== null && val1b !== undefined &&
(typeof val1b != "object" || val1b.toString != defaultToString)) ?
String(val1b).toLowerCase() : ""; // do not match a bogus string
if (sdata.value != null) {
if (!$.isArray(sdata.value)) {
var val2 = String(sdata.value).toLowerCase();
} else {
var val2 = sdata.value[0];
var val3 = sdata.value[1];
}
}
switch (sdata.operator) {
case '=':
case 'is':
if (obj.parseField(rec, search.field) == sdata.value) fl++; // do not hide record
else if (search.type == 'date') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
var val2 = w2utils.formatDate(w2utils.isDate(val2, w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
if (val1 == val2) fl++;
}
else if (search.type == 'time') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatTime(tmp, 'hh24:mi');
var val2 = w2utils.formatTime(val2, 'hh24:mi');
if (val1 == val2) fl++;
}
else if (search.type == 'datetime') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatDateTime(tmp, 'yyyy-mm-dd|hh24:mm:ss');
var val2 = w2utils.formatDateTime(w2utils.isDateTime(val2, w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
if (val1 == val2) fl++;
}
break;
case 'between':
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
if (parseFloat(obj.parseField(rec, search.field)) >= parseFloat(val2) && parseFloat(obj.parseField(rec, search.field)) <= parseFloat(val3)) fl++;
}
else if (search.type == 'date') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.isDate(tmp, w2utils.settings.dateFormat, true);
var val2 = w2utils.isDate(val2, w2utils.settings.dateFormat, true);
var val3 = w2utils.isDate(val3, w2utils.settings.dateFormat, true);
if (val3 != null) val3 = new Date(val3.getTime() + 86400000); // 1 day
if (val1 >= val2 && val1 < val3) fl++;
}
else if (search.type == 'time') {
var val1 = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val2 = w2utils.isTime(val2, true);
var val3 = w2utils.isTime(val3, true);
val2 = (new Date()).setHours(val2.hours, val2.minutes, val2.seconds ? val2.seconds : 0, 0);
val3 = (new Date()).setHours(val3.hours, val3.minutes, val3.seconds ? val3.seconds : 0, 0);
if (val1 >= val2 && val1 < val3) fl++;
}
else if (search.type == 'datetime') {
var val1 = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val2 = w2utils.isDateTime(val2, w2utils.settings.datetimeFormat, true);
var val3 = w2utils.isDateTime(val3, w2utils.settings.datetimeFormat, true);
if (val3) val3 = new Date(val3.getTime() + 86400000); // 1 day
if (val1 >= val2 && val1 < val3) fl++;
}
break;
case '<=':
orEqual = true
case '<':
case 'less':
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
var val1 = parseFloat(obj.parseField(rec, search.field));
var val2 = parseFloat(sdata.value);
if (val1 < val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'date') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.isDate(tmp, w2utils.settings.dateFormat, true);
var val2 = w2utils.isDate(val2, w2utils.settings.dateFormat, true);
if (val1 < val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'time') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatTime(tmp, 'hh24:mi');
var val2 = w2utils.formatTime(val2, 'hh24:mi');
if (val1 < val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'datetime') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatDateTime(tmp, 'yyyy-mm-dd|hh24:mm:ss');
var val2 = w2utils.formatDateTime(w2utils.isDateTime(val2, w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
if (val1.length == val2.length && (val1 < val2 || (orEqual && val1 === val2))) fl++;
}
break;
case '>=':
orEqual = true
case '>':
case 'more':
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
var val1 = parseFloat(obj.parseField(rec, search.field));
var val2 = parseFloat(sdata.value);
if (val1 > val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'date') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.isDate(tmp, w2utils.settings.dateFormat, true);
var val2 = w2utils.isDate(val2, w2utils.settings.dateFormat, true);
if (val1 > val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'time') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatTime(tmp, 'hh24:mi');
var val2 = w2utils.formatTime(val2, 'hh24:mi');
if (val1 > val2 || (orEqual && val1 === val2)) fl++;
}
else if (search.type == 'datetime') {
var tmp = (obj.parseField(rec, search.field + '_') instanceof Date ? obj.parseField(rec, search.field + '_') : obj.parseField(rec, search.field));
var val1 = w2utils.formatDateTime(tmp, 'yyyy-mm-dd|hh24:mm:ss');
var val2 = w2utils.formatDateTime(w2utils.isDateTime(val2, w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
if (val1.length == val2.length && (val1 > val2 || (orEqual && val1 === val2))) fl++;
}
break;
case 'in':
var tmp = sdata.value;
if (sdata.svalue) tmp = sdata.svalue;
if ((tmp.indexOf(w2utils.isFloat(val1b) ? parseFloat(val1b) : val1b) !== -1) || tmp.indexOf(val1) !== -1) fl++;
break;
case 'not in':
var tmp = sdata.value;
if (sdata.svalue) tmp = sdata.svalue;
if (!((tmp.indexOf(w2utils.isFloat(val1b) ? parseFloat(val1b) : val1b) !== -1) || tmp.indexOf(val1) !== -1)) fl++;
break;
case 'begins':
case 'begins with': // need for back compatib.
if (val1.indexOf(val2) === 0) fl++; // do not hide record
break;
case 'contains':
if (val1.indexOf(val2) >= 0) fl++; // do not hide record
break;
case 'null':
if (obj.parseField(rec, search.field) == null) fl++; // do not hide record
break;
case 'not null':
if (obj.parseField(rec, search.field) != null) fl++; // do not hide record
break;
case 'ends':
case 'ends with': // need for back compatib.
var lastIndex = val1.lastIndexOf(val2);
if (lastIndex !== -1 && lastIndex == val1.length - val2.length) fl++; // do not hide record
break;
}
}
if ((obj.last.logic == 'OR' && fl !== 0) ||
(obj.last.logic == 'AND' && fl == obj.searchData.length))
return true;
if (rec.w2ui && rec.w2ui.children && rec.w2ui.expanded !== true) {
// there are closed children, search them too.
for (var r = 0; r < rec.w2ui.children.length; r++) {
var subRec = rec.w2ui.children[r];
if (searchRecord(subRec))
return true;
}
}
return false;
}
// add parents nodes recursively
function addParent(recid) {
if (recid === undefined)
return;
if (duplicateMap[recid])
return; // already visited
duplicateMap[recid] = true;
var i = obj.get(recid, true);
if (i == null)
return;
if ($.inArray(i, obj.last.searchIds) != -1)
return;
var rec = obj.records[i];
if (rec && rec.w2ui)
addParent(rec.w2ui.parent_recid);
obj.last.searchIds.push(i);
}
},
getRangeData: function (range, extra) {
var rec1 = this.get(range[0].recid, true);
var rec2 = this.get(range[1].recid, true);
var col1 = range[0].column;
var col2 = range[1].column;
var res = [];
if (col1 == col2) { // one row
for (var r = rec1; r <= rec2; r++) {
var record = this.records[r];
var dt = record[this.columns[col1].field] || null;
if (extra !== true) {
res.push(dt);
} else {
res.push({ data: dt, column: col1, index: r, record: record });
}
}
} else if (rec1 == rec2) { // one line
var record = this.records[rec1];
for (var i = col1; i <= col2; i++) {
var dt = record[this.columns[i].field] || null;
if (extra !== true) {
res.push(dt);
} else {
res.push({ data: dt, column: i, index: rec1, record: record });
}
}
} else {
for (var r = rec1; r <= rec2; r++) {
var record = this.records[r];
res.push([]);
for (var i = col1; i <= col2; i++) {
var dt = record[this.columns[i].field];
if (extra !== true) {
res[res.length-1].push(dt);
} else {
res[res.length-1].push({ data: dt, column: i, index: r, record: record });
}
}
}
}
return res;
},
addRange: function (ranges) {
var added = 0;
if (this.selectType == 'row') return added;
if (!$.isArray(ranges)) ranges = [ranges];
// if it is selection
for (var i = 0; i < ranges.length; i++) {
if (typeof ranges[i] != 'object') ranges[i] = { name: 'selection' };
if (ranges[i].name == 'selection') {
if (this.show.selectionBorder === false) continue;
var sel = this.getSelection();
if (sel.length === 0) {
this.removeRange('selection');
continue;
} else {
var first = sel[0];
var last = sel[sel.length-1];
}
} else { // other range
var first = ranges[i].range[0];
var last = ranges[i].range[1];
}
if (first) {
var rg = {
name: ranges[i].name,
range: [{ recid: first.recid, column: first.column }, { recid: last.recid, column: last.column }],
style: ranges[i].style || ''
};
// add range
var ind = false;
for (var j = 0; j < this.ranges.length; j++) if (this.ranges[j].name == ranges[i].name) { ind = j; break; }
if (ind !== false) {
this.ranges[ind] = rg;
} else {
this.ranges.push(rg);
}
added++;
}
}
this.refreshRanges();
return added;
},
removeRange: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
var name = arguments[a];
$('#grid_'+ this.name +'_'+ name).remove();
$('#grid_'+ this.name +'_f'+ name).remove();
for (var r = this.ranges.length-1; r >= 0; r--) {
if (this.ranges[r].name == name) {
this.ranges.splice(r, 1);
removed++;
}
}
}
return removed;
},
refreshRanges: function () {
if (this.ranges.length === 0) return;
var obj = this;
var time = (new Date()).getTime();
var rec1 = $('#grid_'+ this.name +'_frecords');
var rec2 = $('#grid_'+ this.name +'_records');
for (var i = 0; i < this.ranges.length; i++) {
var rg = this.ranges[i];
var first = rg.range[0];
var last = rg.range[1];
if (first.index == null) first.index = this.get(first.recid, true);
if (last.index == null) last.index = this.get(last.recid, true);
var td1 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(first.recid) + ' td[col="'+ first.column +'"]');
var td2 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(last.recid) + ' td[col="'+ last.column +'"]');
var td1f = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(first.recid) + ' td[col="'+ first.column +'"]');
var td2f = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(last.recid) + ' td[col="'+ last.column +'"]');
var _lastColumn = last.column;
// adjustment due to column virtual scroll
if (first.column < this.last.colStart && last.column > this.last.colStart) {
td1 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(first.recid) + ' td[col="start"]');
}
if (first.column < this.last.colEnd && last.column > this.last.colEnd) {
_lastColumn = '"end"';
td2 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(last.recid) + ' td[col="end"]');
}
// if virtual scrolling kicked in
var index_top = parseInt($('#grid_'+ this.name +'_rec_top').next().attr('index'));
var index_bottom = parseInt($('#grid_'+ this.name +'_rec_bottom').prev().attr('index'));
var index_ftop = parseInt($('#grid_'+ this.name +'_frec_top').next().attr('index'));
var index_fbottom = parseInt($('#grid_'+ this.name +'_frec_bottom').prev().attr('index'));
if (td1.length === 0 && first.index < index_top && last.index > index_top) {
td1 = $('#grid_'+ this.name +'_rec_top').next().find('td[col='+ first.column +']');
}
if (td2.length === 0 && last.index > index_bottom && first.index < index_bottom) {
td2 = $('#grid_'+ this.name +'_rec_bottom').prev().find('td[col='+ _lastColumn +']');
}
if (td1f.length === 0 && first.index < index_ftop && last.index > index_ftop) { // frozen
td1f = $('#grid_'+ this.name +'_frec_top').next().find('td[col='+ first.column +']');
}
if (td2f.length === 0 && last.index > index_fbottom && first.index < index_fbottom) { // frozen
td2f = $('#grid_'+ this.name +'_frec_bottom').prev().find('td[col='+ last.column +']');
}
// do not show selection cell if it is editable
var edit = $(this.box).find('#grid_'+ this.name + '_editable');
var tmp = edit.find('.w2ui-input');
var tmp1 = tmp.attr('recid');
var tmp2 = tmp.attr('column');
if (rg.name == 'selection' && rg.range[0].recid == tmp1 && rg.range[0].column == tmp2) continue;
// frozen regular columns range
var $range = $('#grid_'+ this.name +'_f'+ rg.name);
if (td1f.length > 0 || td2f.length > 0) {
if ($range.length === 0) {
rec1.append('<div id="grid_'+ this.name +'_f' + rg.name +'" class="w2ui-selection" style="'+ rg.style +'">'+
(rg.name == 'selection' ? '<div id="grid_'+ this.name +'_resizer" class="w2ui-selection-resizer"></div>' : '')+
'</div>');
$range = $('#grid_'+ this.name +'_f'+ rg.name);
} else {
$range.attr('style', rg.style);
$range.find('.w2ui-selection-resizer').show();
}
if (td2f.length === 0) {
td2f = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(last.recid) +' td:last-child');
if (td2f.length === 0) td2f = $('#grid_'+ this.name +'_frec_bottom td:first-child');
$range.css('border-right', '0px');
$range.find('.w2ui-selection-resizer').hide();
}
if (first.recid != null && last.recid != null && td1f.length > 0 && td2f.length > 0) {
var _left = (td1f.position().left - 0 + rec1.scrollLeft());
var _top = (td1f.position().top - 0 + rec1.scrollTop());
$range.show().css({
left : (_left > 0 ? _left : 0) + 'px',
top : (_top > 0 ? _top : 0) + 'px',
width : (td2f.position().left - td1f.position().left + td2f.width() + 2) + 'px',
height : (td2f.position().top - td1f.position().top + td2f.height() + 1) + 'px'
});
} else {
$range.hide();
}
} else {
$range.hide();
}
// regular columns range
var $range = $('#grid_'+ this.name +'_'+ rg.name);
if (td1.length > 0 || td2.length > 0) {
if ($range.length === 0) {
rec2.append('<div id="grid_'+ this.name +'_' + rg.name +'" class="w2ui-selection" style="'+ rg.style +'">'+
(rg.name == 'selection' ? '<div id="grid_'+ this.name +'_resizer" class="w2ui-selection-resizer"></div>' : '')+
'</div>');
$range = $('#grid_'+ this.name +'_'+ rg.name);
} else {
$range.attr('style', rg.style);
}
if (td1.length === 0) {
td1 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(first.recid) +' td:first-child');
if (td1.length === 0) td1 = $('#grid_'+ this.name +'_rec_top td:first-child');
}
if (td2f.length !== 0) {
$range.css('border-left', '0px');
}
if (first.recid != null && last.recid != null && td1.length > 0 && td2.length > 0) {
var _left = (td1.position().left - 0 + rec2.scrollLeft());
var _top = (td1.position().top - 0 + rec2.scrollTop());
$range.show().css({
left : (_left > 0 ? _left : 0) + 'px',
top : (_top > 0 ? _top : 0) + 'px',
width : (td2.position().left - td1.position().left + td2.width() + 2) + 'px',
height : (td2.position().top - td1.position().top + td2.height() + 1) + 'px'
});
} else {
$range.hide();
}
} else {
$range.hide();
}
}
// add resizer events
$(this.box).find('.w2ui-selection-resizer')
.off('mousedown').on('mousedown', mouseStart)
.off('dblclick').on('dblclick', function (event) {
var edata = obj.trigger({ phase: 'before', type: 'resizerDblClick', target: obj.name, originalEvent: event });
if (edata.isCancelled === true) return;
obj.trigger($.extend(edata, { phase: 'after' }));
});
var edata = { phase: 'before', type: 'selectionExtend', target: obj.name, originalRange: null, newRange: null };
return (new Date()).getTime() - time;
function mouseStart (event) {
var sel = obj.getSelection();
obj.last.move = {
type : 'expand',
x : event.screenX,
y : event.screenY,
divX : 0,
divY : 0,
recid : sel[0].recid,
column : sel[0].column,
originalRange : [{ recid: sel[0].recid, column: sel[0].column }, { recid: sel[sel.length-1].recid, column: sel[sel.length-1].column }],
newRange : [{ recid: sel[0].recid, column: sel[0].column }, { recid: sel[sel.length-1].recid, column: sel[sel.length-1].column }]
};
$(document)
.off('.w2ui-' + obj.name)
.on('mousemove.w2ui-' + obj.name, mouseMove)
.on('mouseup.w2ui-' + obj.name, mouseStop);
// do not blur grid
event.preventDefault();
}
function mouseMove (event) {
var mv = obj.last.move;
if (!mv || mv.type != 'expand') return;
mv.divX = (event.screenX - mv.x);
mv.divY = (event.screenY - mv.y);
// find new cell
var recid, column;
var tmp = event.originalEvent.target;
if (tmp.tagName.toUpperCase() != 'TD') tmp = $(tmp).parents('td')[0];
if ($(tmp).attr('col') != null) column = parseInt($(tmp).attr('col'));
tmp = $(tmp).parents('tr')[0];
recid = $(tmp).attr('recid');
// new range
if (mv.newRange[1].recid == recid && mv.newRange[1].column == column) return;
var prevNewRange = $.extend({}, mv.newRange);
mv.newRange = [{ recid: mv.recid, column: mv.column }, { recid: recid, column: column }];
// event before
edata = obj.trigger($.extend(edata, { originalRange: mv.originalRange, newRange : mv.newRange }));
if (edata.isCancelled === true) {
mv.newRange = prevNewRange;
edata.newRange = prevNewRange;
return;
} else {
// default behavior
obj.removeRange('grid-selection-expand');
obj.addRange({
name : 'grid-selection-expand',
range : edata.newRange,
style : 'background-color: rgba(100,100,100,0.1); border: 2px dotted rgba(100,100,100,0.5);'
});
}
}
function mouseStop (event) {
// default behavior
obj.removeRange('grid-selection-expand');
delete obj.last.move;
$(document).off('.w2ui-' + obj.name);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}
},
select: function () {
if (arguments.length === 0) return 0;
var time = (new Date).getTime();
var selected = 0;
var sel = this.last.selection;
if (!this.multiSelect) this.selectNone();
// if too manu arguments > 150k, then it errors off
var args = Array.prototype.slice.call(arguments)
if (Array.isArray(args[0])) args = args[0]
// event before
var tmp = { phase: 'before', type: 'select', target: this.name };
if (args.length == 1) {
tmp.multiple = false;
if ($.isPlainObject(args[0])) {
tmp.recid = args[0].recid;
tmp.column = args[0].column;
} else {
tmp.recid = args[0];
}
} else {
tmp.multiple = true;
tmp.recids = args;
}
var edata = this.trigger(tmp);
if (edata.isCancelled === true) return 0;
// default action
if (this.selectType == 'row') {
for (var a = 0; a < args.length; a++) {
var recid = typeof args[a] == 'object' ? args[a].recid : args[a];
var index = this.get(recid, true);
if (index == null) continue;
var recEl1 = null;
var recEl2 = null;
if (this.searchData.length !== 0 || (index + 1 >= this.last.range_start && index + 1 <= this.last.range_end)) {
recEl1 = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid));
recEl2 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
}
if (this.selectType == 'row') {
if (sel.indexes.indexOf(index) != -1) continue;
sel.indexes.push(index);
if (recEl1 && recEl2) {
recEl1.addClass('w2ui-selected').data('selected', 'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
recEl2.addClass('w2ui-selected').data('selected', 'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
recEl1.find('.w2ui-grid-select-check').prop("checked", true);
}
selected++;
}
}
} else {
// normalize for performance
var new_sel = {};
for (var a = 0; a < args.length; a++) {
var recid = typeof args[a] == 'object' ? args[a].recid : args[a];
var column = typeof args[a] == 'object' ? args[a].column : null;
new_sel[recid] = new_sel[recid] || [];
if ($.isArray(column)) {
new_sel[recid] = column;
} else if (w2utils.isInt(column)) {
new_sel[recid].push(column);
} else {
for (var i = 0; i < this.columns.length; i++) { if (this.columns[i].hidden) continue; new_sel[recid].push(parseInt(i)); }
}
}
// add all
var col_sel = [];
for (var recid in new_sel) {
var index = this.get(recid, true);
if (index == null) continue;
var recEl1 = null;
var recEl2 = null;
if (index + 1 >= this.last.range_start && index + 1 <= this.last.range_end) {
recEl1 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
recEl2 = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid));
}
var s = sel.columns[index] || [];
// default action
if (sel.indexes.indexOf(index) == -1) {
sel.indexes.push(index);
}
// anly only those that are new
for (var t = 0; t < new_sel[recid].length; t++) {
if (s.indexOf(new_sel[recid][t]) == -1) s.push(new_sel[recid][t]);
}
s.sort(function(a, b) { return a-b; }); // sort function must be for numerical sort
for (var t = 0; t < new_sel[recid].length; t++) {
var col = new_sel[recid][t];
if (col_sel.indexOf(col) == -1) col_sel.push(col);
if (recEl1) {
recEl1.find('#grid_'+ this.name +'_data_'+ index +'_'+ col).addClass('w2ui-selected');
recEl1.find('.w2ui-col-number').addClass('w2ui-row-selected');
recEl1.data('selected', 'yes');
recEl1.find('.w2ui-grid-select-check').prop("checked", true);
}
if (recEl2) {
recEl2.find('#grid_'+ this.name +'_data_'+ index +'_'+ col).addClass('w2ui-selected');
recEl2.find('.w2ui-col-number').addClass('w2ui-row-selected');
recEl2.data('selected', 'yes');
recEl2.find('.w2ui-grid-select-check').prop("checked", true);
}
selected++;
}
// save back to selection object
sel.columns[index] = s;
}
// select columns (need here for speed)
for (var c = 0; c < col_sel.length; c++) {
$(this.box).find('#grid_'+ this.name +'_column_'+ col_sel[c] +' .w2ui-col-header').addClass('w2ui-col-selected');
}
}
// need to sort new selection for speed
sel.indexes.sort(function(a, b) { return a-b; });
// all selected?
var areAllSelected = (this.records.length > 0 && sel.indexes.length == this.records.length),
areAllSearchedSelected = (sel.indexes.length > 0 && this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length);
if (areAllSelected || areAllSearchedSelected) {
$('#grid_'+ this.name +'_check_all').prop('checked', true);
} else {
$('#grid_'+ this.name +'_check_all').prop('checked', false);
}
this.status();
this.addRange('selection');
this.updateToolbar(sel, areAllSelected);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return selected;
},
unselect: function () {
var unselected = 0;
var sel = this.last.selection;
// if too manu arguments > 150k, then it errors off
var args = Array.prototype.slice.call(arguments)
if (Array.isArray(args[0])) args = args[0]
// event before
var tmp = { phase: 'before', type: 'unselect', target: this.name };
if (args.length == 1) {
tmp.multiple = false;
if ($.isPlainObject(args[0])) {
tmp.recid = args[0].recid;
tmp.column = args[0].column;
} else {
tmp.recid = args[0];
}
} else {
tmp.multiple = true;
tmp.recids = args
}
var edata = this.trigger(tmp);
if (edata.isCancelled === true) return 0;
for (var a = 0; a < args.length; a++) {
var recid = typeof args[a] == 'object' ? args[a].recid : args[a];
var record = this.get(recid);
if (record == null) continue;
var index = this.get(record.recid, true);
var recEl1 = $('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid));
var recEl2 = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
if (this.selectType == 'row') {
if (sel.indexes.indexOf(index) == -1) continue;
// default action
sel.indexes.splice(sel.indexes.indexOf(index), 1);
recEl1.removeClass('w2ui-selected w2ui-inactive').removeData('selected').find('.w2ui-col-number').removeClass('w2ui-row-selected');
recEl2.removeClass('w2ui-selected w2ui-inactive').removeData('selected').find('.w2ui-col-number').removeClass('w2ui-row-selected');
if (recEl1.length != 0) {
recEl1[0].style.cssText = 'height: '+ this.recordHeight +'px; ' + recEl1.attr('custom_style');
recEl2[0].style.cssText = 'height: '+ this.recordHeight +'px; ' + recEl2.attr('custom_style');
}
recEl1.find('.w2ui-grid-select-check').prop("checked", false);
unselected++;
} else {
var col = args[a].column;
if (!w2utils.isInt(col)) { // unselect all columns
var cols = [];
for (var i = 0; i < this.columns.length; i++) { if (this.columns[i].hidden) continue; cols.push({ recid: recid, column: i }); }
return this.unselect(cols);
}
var s = sel.columns[index];
if (!$.isArray(s) || s.indexOf(col) == -1) continue;
// default action
s.splice(s.indexOf(col), 1);
$('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid)).find(' > td[col='+ col +']').removeClass('w2ui-selected w2ui-inactive');
$('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid)).find(' > td[col='+ col +']').removeClass('w2ui-selected w2ui-inactive');
// check if any row/column still selected
var isColSelected = false;
var isRowSelected = false;
var tmp = this.getSelection();
for (var i = 0; i < tmp.length; i++) {
if (tmp[i].column == col) isColSelected = true;
if (tmp[i].recid == recid) isRowSelected = true;
}
if (!isColSelected) {
$(this.box).find('.w2ui-grid-columns td[col='+ col +'] .w2ui-col-header, .w2ui-grid-fcolumns td[col='+ col +'] .w2ui-col-header').removeClass('w2ui-col-selected');
}
if (!isRowSelected) {
$('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid)).find('.w2ui-col-number').removeClass('w2ui-row-selected');
}
unselected++;
if (s.length === 0) {
delete sel.columns[index];
sel.indexes.splice(sel.indexes.indexOf(index), 1);
recEl1.removeData('selected');
recEl1.find('.w2ui-grid-select-check').prop("checked", false);
recEl2.removeData('selected');
}
}
}
// all selected?
var areAllSelected = (this.records.length > 0 && sel.indexes.length == this.records.length),
areAllSearchedSelected = (sel.indexes.length > 0 && this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length);
if (areAllSelected || areAllSearchedSelected) {
$('#grid_'+ this.name +'_check_all').prop('checked', true);
} else {
$('#grid_'+ this.name +'_check_all').prop('checked', false);
}
// show number of selected
this.status();
this.addRange('selection');
this.updateToolbar(sel, areAllSelected);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return unselected;
},
selectAll: function () {
var time = (new Date()).getTime();
if (this.multiSelect === false) return;
// event before
var edata = this.trigger({ phase: 'before', type: 'select', target: this.name, all: true });
if (edata.isCancelled === true) return;
// default action
var url = (typeof this.url != 'object' ? this.url : this.url.get);
var sel = this.last.selection;
var cols = [];
for (var i = 0; i < this.columns.length; i++) cols.push(i);
// if local data source and searched
sel.indexes = [];
if (!url && this.searchData.length !== 0) {
// local search applied
for (var i = 0; i < this.last.searchIds.length; i++) {
sel.indexes.push(this.last.searchIds[i]);
if (this.selectType != 'row') sel.columns[this.last.searchIds[i]] = cols.slice(); // .slice makes copy of the array
}
} else {
var buffered = this.records.length;
if (this.searchData.length != 0 && !url) buffered = this.last.searchIds.length;
for (var i = 0; i < buffered; i++) {
sel.indexes.push(i);
if (this.selectType != 'row') sel.columns[i] = cols.slice(); // .slice makes copy of the array
}
}
// add selected class
if (this.selectType == 'row') {
$(this.box).find('.w2ui-grid-records tr').not('.w2ui-empty-record')
.addClass('w2ui-selected').data('selected', 'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-frecords tr').not('.w2ui-empty-record')
.addClass('w2ui-selected').data('selected', 'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
$(this.box).find('input.w2ui-grid-select-check').prop('checked', true);
} else {
$(this.box).find('.w2ui-grid-columns td .w2ui-col-header, .w2ui-grid-fcolumns td .w2ui-col-header').addClass('w2ui-col-selected');
$(this.box).find('.w2ui-grid-records tr .w2ui-col-number').addClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-records tr').not('.w2ui-empty-record')
.find('.w2ui-grid-data').not('.w2ui-col-select').addClass('w2ui-selected').data('selected', 'yes');
$(this.box).find('.w2ui-grid-frecords tr .w2ui-col-number').addClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-frecords tr').not('.w2ui-empty-record')
.find('.w2ui-grid-data').not('.w2ui-col-select').addClass('w2ui-selected').data('selected', 'yes');
$(this.box).find('input.w2ui-grid-select-check').prop('checked', true);
}
// enable/disable toolbar buttons
var sel = this.getSelection(true);
if (sel.length == 1) this.toolbar.enable('w2ui-edit'); else this.toolbar.disable('w2ui-edit');
if (sel.length >= 1) this.toolbar.enable('w2ui-delete'); else this.toolbar.disable('w2ui-delete');
this.addRange('selection');
$('#grid_'+ this.name +'_check_all').prop('checked', true);
this.status();
this.updateToolbar({ indexes: sel }, true);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
selectNone: function () {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'unselect', target: this.name, all: true });
if (edata.isCancelled === true) return;
// default action
var sel = this.last.selection;
// remove selected class
if (this.selectType == 'row') {
$(this.box).find('.w2ui-grid-records tr.w2ui-selected').removeClass('w2ui-selected w2ui-inactive').removeData('selected')
.find('.w2ui-col-number').removeClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-frecords tr.w2ui-selected').removeClass('w2ui-selected w2ui-inactive').removeData('selected')
.find('.w2ui-col-number').removeClass('w2ui-row-selected');
$(this.box).find('input.w2ui-grid-select-check').prop('checked', false);
} else {
$(this.box).find('.w2ui-grid-columns td .w2ui-col-header, .w2ui-grid-fcolumns td .w2ui-col-header').removeClass('w2ui-col-selected');
$(this.box).find('.w2ui-grid-records tr .w2ui-col-number').removeClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-frecords tr .w2ui-col-number').removeClass('w2ui-row-selected');
$(this.box).find('.w2ui-grid-data.w2ui-selected').removeClass('w2ui-selected w2ui-inactive').removeData('selected');
$(this.box).find('input.w2ui-grid-select-check').prop('checked', false);
}
sel.indexes = [];
sel.columns = {};
this.toolbar.disable('w2ui-edit', 'w2ui-delete');
this.removeRange('selection');
$('#grid_'+ this.name +'_check_all').prop('checked', false);
this.status();
this.updateToolbar(sel, false);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
updateToolbar: function (sel, areAllSelected) {
var obj = this
var cnt = sel && sel.indexes ? sel.indexes.length : 0
this.toolbar.items.forEach(function (item) {
_checkItem(item, '')
if (Array.isArray(item.items)) {
item.items.forEach(function (it) {
_checkItem(it, item.id + ':')
})
}
})
function _checkItem(item, prefix) {
if (item.batch === 0) {
if (cnt > 0) obj.toolbar.enable(prefix + item.id); else obj.toolbar.disable(prefix + item.id)
}
if (item.batch != null && !isNaN(item.batch) && item.batch > 0) {
if (cnt == item.batch) obj.toolbar.enable(prefix + item.id); else obj.toolbar.disable(prefix + item.id)
}
if (typeof item.batch == 'function') {
item.batch(obj.selectType == 'cell' ? sel : (sel ? sel.indexes : null))
}
}
},
getSelection: function (returnIndex) {
var ret = [];
var sel = this.last.selection;
if (this.selectType == 'row') {
for (var i = 0; i < sel.indexes.length; i++) {
if (!this.records[sel.indexes[i]]) continue;
if (returnIndex === true) ret.push(sel.indexes[i]); else ret.push(this.records[sel.indexes[i]].recid);
}
return ret;
} else {
for (var i = 0; i < sel.indexes.length; i++) {
var cols = sel.columns[sel.indexes[i]];
if (!this.records[sel.indexes[i]]) continue;
for (var j = 0; j < cols.length; j++) {
ret.push({ recid: this.records[sel.indexes[i]].recid, index: parseInt(sel.indexes[i]), column: cols[j] });
}
}
return ret;
}
},
search: function (field, value) {
var obj = this;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
var searchData = [];
var last_multi = this.last.multi;
var last_logic = this.last.logic;
var last_field = this.last.field;
var last_search = this.last.search;
var hasHiddenSearches = false;
// add hidden searches
for (var i = 0; i < this.searches.length; i++) {
if (!this.searches[i].hidden || this.searches[i].value == null) continue;
searchData.push({
field : this.searches[i].field,
operator : this.searches[i].operator || 'is',
type : this.searches[i].type,
value : this.searches[i].value || ''
});
hasHiddenSearches = true;
}
// 1: search() - advanced search (reads from popup)
if (arguments.length === 0) {
last_search = '';
// advanced search
for (var i = 0; i < this.searches.length; i++) {
var search = this.searches[i];
var operator = $('#grid_'+ this.name + '_operator_'+ i).val();
var field1 = $('#grid_'+ this.name + '_field_'+ i);
var field2 = $('#grid_'+ this.name + '_field2_'+ i);
var value1 = field1.val();
var value2 = field2.val();
var svalue = null;
var text = null;
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
var fld1 = field1.data('w2field');
var fld2 = field2.data('w2field');
if (fld1) value1 = fld1.clean(value1);
if (fld2) value2 = fld2.clean(value2);
}
if (['list', 'enum'].indexOf(search.type) != -1) {
value1 = field1.data('selected') || {};
if ($.isArray(value1)) {
svalue = [];
for (var j = 0; j < value1.length; j++) {
svalue.push(w2utils.isFloat(value1[j].id) ? parseFloat(value1[j].id) : String(value1[j].id).toLowerCase());
delete value1[j].hidden;
}
if ($.isEmptyObject(value1)) value1 = '';
} else {
text = value1.text || '';
value1 = value1.id || '';
}
}
if ((value1 !== '' && value1 != null) || (value2 != null && value2 !== '')) {
var tmp = {
field : search.field,
type : search.type,
operator : operator
};
if (operator == 'between') {
$.extend(tmp, { value: [value1, value2] });
} else if (operator == 'in' && typeof value1 == 'string') {
$.extend(tmp, { value: value1.split(',') });
} else if (operator == 'not in' && typeof value1 == 'string') {
$.extend(tmp, { value: value1.split(',') });
} else {
$.extend(tmp, { value: value1 });
}
if (svalue) $.extend(tmp, { svalue: svalue });
if (text) $.extend(tmp, { text: text });
// conver date to unix time
try {
if (search.type == 'date' && operator == 'between') {
tmp.value[0] = value1; // w2utils.isDate(value1, w2utils.settings.dateFormat, true).getTime();
tmp.value[1] = value2; // w2utils.isDate(value2, w2utils.settings.dateFormat, true).getTime();
}
if (search.type == 'date' && operator == 'is') {
tmp.value = value1; // w2utils.isDate(value1, w2utils.settings.dateFormat, true).getTime();
}
} catch (e) {
}
searchData.push(tmp);
last_multi = true; // if only hidden searches, then do not set
}
}
last_logic = 'AND';
}
// 2: search(field, value) - regular search
if (typeof field == 'string') {
// if only one argument - search all
if (arguments.length == 1) {
value = field;
field = 'all';
}
last_field = field;
last_search = value;
last_multi = false;
last_logic = (hasHiddenSearches ? 'AND' : 'OR');
// loop through all searches and see if it applies
if (value != null) {
if (field.toLowerCase() == 'all') {
// if there are search fields loop thru them
if (this.searches.length > 0) {
for (var i = 0; i < this.searches.length; i++) {
var search = this.searches[i];
if (search.type == 'text' || (search.type == 'alphanumeric' && w2utils.isAlphaNumeric(value))
|| (search.type == 'int' && w2utils.isInt(value)) || (search.type == 'float' && w2utils.isFloat(value))
|| (search.type == 'percent' && w2utils.isFloat(value)) || ((search.type == 'hex' || search.type == 'color') && w2utils.isHex(value))
|| (search.type == 'currency' && w2utils.isMoney(value)) || (search.type == 'money' && w2utils.isMoney(value))
|| (search.type == 'date' && w2utils.isDate(value)) || (search.type == 'time' && w2utils.isTime(value))
|| (search.type == 'datetime' && w2utils.isDateTime(value)) || (search.type == 'datetime' && w2utils.isDate(value))
|| (search.type == 'enum' && w2utils.isAlphaNumeric(value)) || (search.type == 'list' && w2utils.isAlphaNumeric(value))
) {
var tmp = {
field : search.field,
type : search.type,
operator : (search.operator != null ? search.operator : (search.type == 'text' ? this.textSearch : 'is')),
value : value
};
if ($.trim(value) != '') searchData.push(tmp);
}
// range in global search box
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1 && $.trim(String(value)).split('-').length == 2) {
var t = $.trim(String(value)).split('-');
var tmp = {
field : search.field,
type : search.type,
operator : (search.operator != null ? search.operator : 'between'),
value : [t[0], t[1]]
};
searchData.push(tmp);
}
// lists fiels
if (['list', 'enum'].indexOf(search.type) != -1) {
var new_values = [];
if (search.options == null) search.options = {};
if (!Array.isArray(search.options.items)) search.options.items = [];
for (var j = 0; j < search.options.items; j++) {
var tmp = search.options.items[j];
try {
var re = new RegExp(value, 'i');
if (re.test(tmp)) new_values.push(j);
if (tmp.text && re.test(tmp.text)) new_values.push(tmp.id);
} catch (e) {}
}
if (new_values.length > 0) {
var tmp = {
field : search.field,
type : search.type,
operator : (search.operator != null ? search.operator : 'in'),
value : new_values
};
searchData.push(tmp);
}
}
}
} else {
// no search fields, loop thru columns
for (var i = 0; i < this.columns.length; i++) {
var tmp = {
field : this.columns[i].field,
type : 'text',
operator : this.textSearch,
value : value
};
searchData.push(tmp);
}
}
} else {
var el = $('#grid_'+ this.name +'_search_all');
var search = this.getSearch(field);
if (search == null) search = { field: field, type: 'text' };
if (search.field == field) this.last.label = search.label;
if (value !== '') {
var op = this.textSearch;
var val = value;
if (['date', 'time', 'datetime'].indexOf(search.type) != -1) op = 'is';
if (['list', 'enum'].indexOf(search.type) != -1) {
op = 'is';
var tmp = el.data('selected');
if (tmp && !$.isEmptyObject(tmp)) val = tmp.id; else val = '';
}
if (search.type == 'int' && value !== '') {
op = 'is';
if (String(value).indexOf('-') != -1) {
var tmp = value.split('-');
if (tmp.length == 2) {
op = 'between';
val = [parseInt(tmp[0]), parseInt(tmp[1])];
}
}
if (String(value).indexOf(',') != -1) {
var tmp = value.split(',');
op = 'in';
val = [];
for (var i = 0; i < tmp.length; i++) val.push(tmp[i]);
}
}
if (search.operator != null) op = search.operator;
var tmp = {
field : search.field,
type : search.type,
operator : op,
value : val
};
searchData.push(tmp);
}
}
}
}
// 3: search([ { field, value, [operator,] [type] }, { field, value, [operator,] [type] } ], logic) - submit whole structure
if ($.isArray(field)) {
var logic = 'AND';
if (typeof value == 'string') {
logic = value.toUpperCase();
if (logic != 'OR' && logic != 'AND') logic = 'AND';
}
last_search = '';
last_multi = true;
last_logic = logic;
for (var i = 0; i < field.length; i++) {
var data = field[i];
if (typeof data.value == 'number') data.operator = 'is';
if (typeof data.value == 'string') data.operator = this.textSearch;
if ($.isArray(data.value)) data.operator = 'in'
// merge current field and search if any
searchData.push(data);
}
}
// event before
var edata = this.trigger({
phase: 'before', type: 'search', target: this.name, multi: (arguments.length === 0 ? true : false),
searchField: (field ? field : 'multi'), searchValue: (field ? value : 'multi'),
searchData: searchData, searchLogic: last_logic });
if (edata.isCancelled === true) return;
// default action
this.searchData = edata.searchData;
this.last.field = last_field;
this.last.search = last_search;
this.last.multi = last_multi;
this.last.logic = edata.searchLogic;
this.last.scrollTop = 0;
this.last.scrollLeft = 0;
this.last.selection.indexes = [];
this.last.selection.columns = {};
// -- clear all search field
this.searchClose();
// apply search
if (url) {
this.last.xhr_offset = 0;
this.reload();
} else {
// local search
this.localSearch();
this.refresh();
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
searchOpen: function () {
if (!this.box) return;
if (this.searches.length === 0) return;
var obj = this;
var it = obj.toolbar.get('w2ui-search-advanced');
var btn = '#tb_'+ obj.toolbar.name +'_item_'+ w2utils.escapeId(it.id) +' table.w2ui-button';
// event before
var edata = this.trigger({ phase: 'before', type: 'searchOpen', target: this.name });
if (edata.isCancelled === true) {
setTimeout(function () { obj.toolbar.uncheck('w2ui-search-advanced'); }, 1);
return;
}
// show search
$('#tb_'+ this.name +'_toolbar_item_w2ui-search-advanced').w2overlay({
html : this.getSearchesHTML(),
name : this.name + '-searchOverlay',
left : -10,
'class' : 'w2ui-grid-searches',
onShow : function () {
obj.initSearches();
$('#w2ui-overlay-'+ obj.name +'-searchOverlay .w2ui-grid-searches').data('grid-name', obj.name);
var sfields = $('#w2ui-overlay-'+ this.name +'-searchOverlay .w2ui-grid-searches *[rel=search]');
if (sfields.length > 0) sfields[0].focus();
if (!it.checked) {
it.checked = true;
$(btn).addClass('checked');
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
},
onHide: function () {
it.checked = false;
$(btn).removeClass('checked');
}
});
},
searchClose: function () {
var obj = this;
if (!this.box) return;
if (this.searches.length === 0) return;
if (this.toolbar) this.toolbar.uncheck('w2ui-search-advanced', 'w2ui-column-on-off');
// hide search
$().w2overlay({ name: this.name + '-searchOverlay' });
},
searchReset: function (noRefresh) {
var searchData = [];
var hasHiddenSearches = false;
// add hidden searches
for (var i = 0; i < this.searches.length; i++) {
if (!this.searches[i].hidden || this.searches[i].value == null) continue;
searchData.push({
field : this.searches[i].field,
operator : this.searches[i].operator || 'is',
type : this.searches[i].type,
value : this.searches[i].value || ''
});
hasHiddenSearches = true;
}
// event before
var edata = this.trigger({ phase: 'before', type: 'search', reset: true, target: this.name, searchData: searchData });
if (edata.isCancelled === true) return;
// default action
this.searchData = edata.searchData;
this.last.search = '';
this.last.logic = (hasHiddenSearches ? 'AND' : 'OR');
// --- do not reset to All Fields (I think)
if (this.searches.length > 0) {
if (!this.multiSearch || !this.show.searchAll) {
var tmp = 0;
while (tmp < this.searches.length && (this.searches[tmp].hidden || this.searches[tmp].simple === false)) tmp++;
if (tmp >= this.searches.length) {
// all searches are hidden
this.last.field = '';
this.last.label = '';
} else {
this.last.field = this.searches[tmp].field;
this.last.label = this.searches[tmp].label;
}
} else {
this.last.field = 'all';
this.last.label = w2utils.lang('All Fields');
}
}
this.last.multi = false;
this.last.xhr_offset = 0;
// reset scrolling position
this.last.scrollTop = 0;
this.last.scrollLeft = 0;
this.last.selection.indexes = [];
this.last.selection.columns = {};
// -- clear all search field
this.searchClose();
$('#grid_'+ this.name +'_search_all').val('').removeData('selected');
// apply search
if (!noRefresh) this.reload();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
searchShowFields: function (forceHide) {
var obj = this;
var el = $('#grid_'+ this.name +'_search_all');
if (forceHide === true) {
$(el).w2overlay({ name: obj.name + '-searchFields' });
return;
}
var html = '<div class="w2ui-select-field"><table><tbody>';
for (var s = -1; s < this.searches.length; s++) {
var search = this.searches[s];
var sField = (search ? search.field : null);
var column = this.getColumn(sField);
var disable = false;
var msg = 'This column is hidden';
if (this.show.searchHiddenMsg == true && s != -1 && (column == null || (column.hidden === true && column.hideable !== false))) {
disable = true;
if (column == null) {
msg = 'This column does not exist'
}
}
if (s == -1) { // -1 is All Fields search
if (!this.multiSearch || !this.show.searchAll) continue;
search = { field: 'all', label: w2utils.lang('All Fields') };
} else {
if (column != null && column.hideable === false) continue;
// don't show hidden searches
if (this.searches[s].hidden === true || this.searches[s].simple === false) continue;
}
if (search.label == null && search.caption != null) {
console.log('NOTICE: grid search.caption property is deprecated, please use search.label. Search ->', search);
search.label = search.caption;
}
html += '<tr style="'+ (disable ? 'color: silver' : '') +'" '
+ (disable
? 'onmouseenter="jQuery(this).w2tag({ top: 4, html: \''+ msg +'\' })" onmouseleave="jQuery(this).w2tag()"'
: ''
) +
(w2utils.isIOS ? 'onTouchStart' : 'onClick') +'="event.stopPropagation(); '+
' w2ui[\''+ this.name +'\'].initAllField(\''+ search.field +'\');'+
' jQuery(\'#grid_'+ this.name +'_search_all\').w2overlay({ name: \''+ this.name +'-searchFields\' });"'+
' jQuery(this).addClass(\'w2ui-selected\');'+
' onmousedown="jQuery(this).parent().find(\'tr\').removeClass(\'w2ui-selected\'); jQuery(this).addClass(\'w2ui-selected\')" ' +
' onmouseup="jQuery(this).removeClass(\'w2ui-selected\')" ' +
' >'+
' <td>'+
' <span class="w2ui-column-check w2ui-icon-'+ (search.field == this.last.field ? 'check' : 'empty') +'"></span>'+
' </td>'+
' <td>'+ search.label +'</td>'+
'</tr>';
}
html += "</tbody></table></div>";
var overName = obj.name + '-searchFields';
if ($('#w2ui-overlay-'+ overName).length == 1) html = ''; // hide if visible
// need timer otherwise does nto show with list type
setTimeout(function () {
$(el).w2overlay({ html: html, name: overName, left: -10 });
}, 1);
},
initAllField: function (field, value) {
var el = $('#grid_'+ this.name +'_search_all');
if (field == 'all') {
var search = { field: 'all', label: w2utils.lang('All Fields') };
el.w2field('clear');
// el.change(); // triggering change will cause grid calling remote url twice
} else {
var search = this.getSearch(field);
if (search == null) return;
var st = search.type;
if (['enum', 'select'].indexOf(st) != -1) st = 'list';
el.w2field(st, $.extend({}, search.options, { suffix: '', autoFormat: false, selected: value }));
if (['list', 'enum', 'date', 'time', 'datetime'].indexOf(search.type) != -1) {
this.last.search = '';
this.last.item = '';
el.val('');
$('#grid_'+ this.name +'_searchClear').hide()
}
}
// update field
if (this.last.search != '') {
this.last.label = search.label;
this.search(search.field, this.last.search);
} else {
this.last.field = search.field;
this.last.label = search.label;
}
el.attr('placeholder', w2utils.lang(search.label || search.caption || search.field));
$().w2overlay({ name: this.name + '-searchFields' });
},
// clears records and related params
clear: function (noRefresh) {
this.total = 0;
this.records = [];
this.summary = [];
this.last.xhr_offset = 0; // need this for reload button to work on remote data set
this.last.idCache = {}; // optimization to free memory
this.reset(true);
// refresh
if (!noRefresh) this.refresh();
},
// clears scroll position, selection, ranges
reset: function (noRefresh) {
// position
this.last.scrollTop = 0;
this.last.scrollLeft = 0;
this.last.selection = { indexes: [], columns: {} };
this.last.range_start = null;
this.last.range_end = null;
// additional
$('#grid_'+ this.name +'_records').prop('scrollTop', 0);
// refresh
if (!noRefresh) this.refresh();
},
skip: function (offset, callBack) {
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (url) {
this.offset = parseInt(offset);
if (this.offset > this.total) this.offset = this.total - this.limit;
if (this.offset < 0 || !w2utils.isInt(this.offset)) this.offset = 0;
this.clear(true);
this.reload(callBack);
} else {
console.log('ERROR: grid.skip() can only be called when you have remote data source.');
}
},
load: function (url, callBack) {
if (url == null) {
console.log('ERROR: You need to provide url argument when calling .load() method of "'+ this.name +'" object.');
return;
}
// default action
this.clear(true);
this.request('get', {}, url, callBack);
},
reload: function (callBack) {
var grid = this;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
grid.selectionSave();
if (url) {
// need to remember selection (not just last.selection object)
this.load(url, function () {
grid.selectionRestore();
if (typeof callBack == 'function') callBack();
});
} else {
this.reset(true);
this.localSearch();
this.selectionRestore();
if (typeof callBack == 'function') callBack({ status: 'success' });
}
},
request: function (cmd, add_params, url, callBack) {
if (add_params == null) add_params = {};
if (url == '' || url == null) url = this.url;
if (url == '' || url == null) return;
// build parameters list
if (!w2utils.isInt(this.offset)) this.offset = 0;
if (!w2utils.isInt(this.last.xhr_offset)) this.last.xhr_offset = 0;
// add list params
var params = {
limit : this.limit,
offset : parseInt(this.offset) + parseInt(this.last.xhr_offset),
searchLogic : this.last.logic,
search: this.searchData.map(function (search) {
var _search = $.extend({}, search);
if (this.searchMap && this.searchMap[_search.field]) _search.field = this.searchMap[_search.field];
return _search;
}.bind(this)),
sort: this.sortData.map(function (sort) {
var _sort = $.extend({}, sort);
if (this.sortMap && this.sortMap[_sort.field]) _sort.field = this.sortMap[_sort.field];
return _sort;
}.bind(this))
}
if (this.searchData.length === 0) {
delete params['search'];
delete params['searchLogic'];
}
if (this.sortData.length === 0) {
delete params['sort'];
}
// append other params
$.extend(params, this.postData);
$.extend(params, add_params);
// other actions
if (cmd == 'delete' || cmd == 'save') {
delete params['limit'];
delete params['offset'];
params['action'] = cmd
if (cmd == 'delete') {
params[this.recid || 'recid'] = this.getSelection();
}
}
// event before
if (cmd == 'get') {
var edata = this.trigger({ phase: 'before', type: 'request', target: this.name, url: url, postData: params, httpHeaders: this.httpHeaders });
if (edata.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
} else {
var edata = { url: url, postData: params, httpHeaders: this.httpHeaders };
}
// call server to get data
var obj = this;
if (this.last.xhr_offset === 0) {
this.lock(w2utils.lang(this.msgRefresh), true);
}
if (this.last.xhr) try { this.last.xhr.abort(); } catch (e) {}
// URL
url = (typeof edata.url != 'object' ? edata.url : edata.url.get);
if (cmd == 'save' && typeof edata.url == 'object') url = edata.url.save;
if (cmd == 'delete' && typeof edata.url == 'object') url = edata.url.remove;
// process url with routeData
if (!$.isEmptyObject(obj.routeData)) {
var info = w2utils.parseRoute(url);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (obj.routeData[info.keys[k].name] == null) continue;
url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
}
}
}
// ajax options
var ajaxOptions = {
type : 'GET',
url : url,
data : edata.postData,
headers : edata.httpHeaders,
dataType : 'json' // expected data type from server
};
var dataType = this.dataType || w2utils.settings.dataType
switch (dataType) {
case 'HTTP':
ajaxOptions.data = (typeof ajaxOptions.data == 'object' ? String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']') : ajaxOptions.data);
break;
case 'HTTPJSON':
ajaxOptions.data = { request: JSON.stringify(ajaxOptions.data) };
break;
case 'RESTFULL':
ajaxOptions.type = 'GET';
if (cmd == 'save') ajaxOptions.type = 'PUT'; // so far it is always update
if (cmd == 'delete') ajaxOptions.type = 'DELETE';
ajaxOptions.data = (typeof ajaxOptions.data == 'object' ? String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']') : ajaxOptions.data);
break;
case 'RESTFULLJSON':
ajaxOptions.type = 'GET';
if (cmd == 'save') ajaxOptions.type = 'PUT'; // so far it is always update
if (cmd == 'delete') ajaxOptions.type = 'DELETE';
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
ajaxOptions.contentType = 'application/json';
break;
case 'JSON':
ajaxOptions.type = 'POST';
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
ajaxOptions.contentType = 'application/json';
break;
}
if (this.method) ajaxOptions.type = this.method;
this.last.xhr_cmd = cmd;
this.last.xhr_start = (new Date()).getTime();
this.last.loaded = false;
this.last.xhr = $.ajax(ajaxOptions)
.done(function (data, status, xhr) {
obj.requestComplete(status, xhr, cmd, callBack);
})
.fail(function (xhr, status, error) {
// trigger event
var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
var edata2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
if (edata2.isCancelled === true) return;
// default behavior
if (status != 'abort') { // it can be aborted by the grid itself
var data;
try { data = typeof xhr.responseJSON === 'object' ? xhr.responseJSON : $.parseJSON(xhr.responseText); } catch (e) {}
console.log('ERROR: Server communication failed.',
'\n EXPECTED:', { status: 'success', total: 5, records: [{ recid: 1, field: 'value' }] },
'\n OR:', { status: 'error', message: 'error message' },
'\n RECEIVED:', typeof data == 'object' ? data : xhr.responseText);
obj.requestComplete('error', xhr, cmd, callBack);
}
// event after
obj.trigger($.extend(edata2, { phase: 'after' }));
});
if (cmd == 'get') {
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
},
requestComplete: function(status, xhr, cmd, callBack) {
var obj = this;
this.unlock();
setTimeout(function () {
if (obj.show.statusResponse) {
obj.status(w2utils.lang('Server Response') + ' ' + ((new Date()).getTime() - obj.last.xhr_start)/1000 +' ' + w2utils.lang('sec'));
}
}, 10);
this.last.pull_more = false;
this.last.pull_refresh = true;
// event before
var event_name = 'load';
if (this.last.xhr_cmd == 'save') event_name = 'save';
if (this.last.xhr_cmd == 'delete') event_name = 'delete';
var edata = this.trigger({ phase: 'before', target: this.name, type: event_name, xhr: xhr, status: status });
if (edata.isCancelled === true) {
if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
return;
}
// parse server response
var data;
if (status != 'error') {
// default action
if (typeof obj.parser == 'function') {
data = obj.parser(xhr.responseJSON);
if (typeof data != 'object') {
console.log('ERROR: Your parser did not return proper object');
}
} else {
data = xhr.responseJSON;
if (data == null) {
data = {
status : 'error',
message : w2utils.lang(this.msgNotJSON),
responseText : xhr.responseText
};
} else if (Array.isArray(data)) {
// if it is plain array, assume these are records
data = {
status : 'success',
records : data,
total : data.length
}
}
}
if (Array.isArray(data.records)) {
// make sure each record has recid
data.records.forEach(function (rec, ind) {
if (obj.recid) {
rec.recid = obj.parseField(rec, obj.recid);
}
if (rec.recid == null) {
rec.recid = 'recid-' + ind;
}
})
}
if (data['status'] == 'error') {
obj.error(data['message']);
} else {
if (cmd == 'get') {
if (data.total == null) data.total = -1;
if (data.records == null) {
data.records = [];
}
if (data.records.length == this.limit) {
var loaded = this.records.length + data.records.length
this.last.xhr_hasMore = (loaded == this.total ? false : true);
} else {
this.last.xhr_hasMore = false;
this.total = this.offset + this.last.xhr_offset + data.records.length;
}
if (!this.last.xhr_hasMore) {
// if no morerecords, then hide spinner
$('#grid_'+ this.name +'_rec_more, #grid_'+ this.name +'_frec_more').hide()
}
if (this.last.xhr_offset === 0) {
this.records = [];
this.summary = [];
if (w2utils.isInt(data.total)) this.total = parseInt(data.total);
} else {
if (data.total != -1 && parseInt(data.total) != parseInt(this.total)) {
this.message(w2utils.lang(this.msgNeedReload), function () {
delete this.last.xhr_offset;
this.reload();
}.bind(this));
return;
}
}
// records
if (data.records) {
for (var r = 0; r < data.records.length; r++) {
this.records.push(data.records[r]);
}
}
// summary records (if any)
if (data.summary) {
this.summary = [];
for (var r = 0; r < data.summary.length; r++) {
this.summary.push(data.summary[r]);
}
}
}
if (cmd == 'delete') {
this.reset(); // unselect old selections
this.reload();
return;
}
}
} else {
data = {
status : 'error',
message : w2utils.lang(this.msgAJAXerror),
responseText : xhr.responseText
};
obj.error(w2utils.lang(this.msgAJAXerror));
}
// event after
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
this.localSort();
this.localSearch();
}
this.total = parseInt(this.total);
// do not refresh if loading on infinite scroll
if (this.last.xhr_offset === 0) {
this.refresh();
} else {
this.scroll();
this.resize();
}
// call back
if (typeof callBack == 'function') callBack(data); // need to be befor event:after
// after event
this.trigger($.extend(edata, { phase: 'after' }));
this.last.loaded = true;
},
error: function (msg) {
var obj = this;
// let the management of the error outside of the grid
var edata = this.trigger({ target: this.name, type: 'error', message: msg , xhr: this.last.xhr });
if (edata.isCancelled === true) {
if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
return;
}
this.message(msg);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
getChanges: function (recordsBase) {
var changes = [];
if (typeof recordsBase == 'undefined') {
var recordsBase = this.records;
}
for (var r = 0; r < recordsBase.length; r++) {
var rec = recordsBase[r];
if (rec.w2ui) {
if (rec.w2ui.changes != null) {
var obj = {}
obj[this.recid || 'recid'] = rec.recid
changes.push($.extend(true, obj, rec.w2ui.changes));
}
// recursively look for changes in non-expanded children
if (rec.w2ui.expanded !== true && rec.w2ui.children && rec.w2ui.children.length) {
$.merge(changes, this.getChanges(rec.w2ui.children))
}
}
}
return changes;
},
mergeChanges: function () {
var changes = this.getChanges();
for (var c = 0; c < changes.length; c++) {
var record = this.get(changes[c].recid);
for (var s in changes[c]) {
if (s == 'recid') continue; // do not allow to change recid
if (typeof changes[c][s] === "object") changes[c][s] = changes[c][s].text;
try {
if (s.indexOf('.') != -1) {
eval("record['" + s.replace(/\./g, "']['") + "'] = changes[c][s]")
} else {
record[s] = changes[c][s];
}
} catch (e) {
console.log('ERROR: Cannot merge. ', e.message || '', e);
}
if (record.w2ui) delete record.w2ui.changes;
}
}
this.refresh();
},
// ===================================================
// -- Action Handlers
save: function (callBack) {
var obj = this;
var changes = this.getChanges();
var url = (typeof this.url != 'object' ? this.url : this.url.save);
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'save', changes: changes });
if (edata.isCancelled === true) {
if (url && typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
return;
}
if (url) {
this.request('save', { 'changes' : edata.changes }, null,
function (data) {
if (data.status !== 'error') {
// only merge changes, if save was successful
obj.mergeChanges();
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
// call back
if (typeof callBack == 'function') callBack(data);
}
);
} else {
this.mergeChanges();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
},
editField: function (recid, column, value, event) {
var obj = this;
if (this.last.inEditMode === true) { // already editing
if (event.keyCode == 13) {
var index = this.last._edit.index;
var column = this.last._edit.column;
var recid = this.last._edit.recid;
this.editChange({ type: 'custom', value: this.last._edit.value }, this.get(recid, true), column, event);
var next = event.shiftKey ? this.prevRow(index, column) : this.nextRow(index, column);
if (next != null && next != index) {
setTimeout(function () {
if (obj.selectType != 'row') {
obj.selectNone();
obj.select({ recid: obj.records[next].recid, column: column });
} else {
obj.editField(obj.records[next].recid, column, null, event);
}
}, 1);
}
this.last.inEditMode = false;
} else {
// when 2 chars entered fast
var $input = $(this.box).find('div.w2ui-edit-box .w2ui-input');
if ($input.length > 0 && $input[0].tagName == 'DIV') {
$input.text($input.text() + value);
w2utils.setCursorPosition($input[0], $input.text().length);
}
}
return;
}
var index = obj.get(recid, true);
var edit = obj.getCellEditable(index, column);
if (!edit) return;
var rec = obj.records[index];
var col = obj.columns[column];
var prefix = (col.frozen === true ? '_f' : '_');
if (['enum', 'file'].indexOf(edit.type) != -1) {
console.log('ERROR: input types "enum" and "file" are not supported in inline editing.');
return;
}
// event before
var edata = obj.trigger({ phase: 'before', type: 'editField', target: obj.name, recid: recid, column: column, value: value,
index: index, originalEvent: event });
if (edata.isCancelled === true) return;
value = edata.value;
// default behaviour
this.last.inEditMode = true;
this.last._edit = { value: value, index: index, column: column, recid: recid };
this.selectNone();
this.select({ recid: recid, column: column });
if (['checkbox', 'check'].indexOf(edit.type) != -1) return;
// create input element
var tr = $('#grid_'+ obj.name + prefix +'rec_' + w2utils.escapeId(recid));
var el = tr.find('[col='+ column +'] > div');
// clear previous if any
$(this.box).find('div.w2ui-edit-box').remove();
// for spreadsheet - insert into selection
if (this.selectType != 'row') {
$('#grid_'+ this.name + prefix + 'selection')
.attr('id', 'grid_'+ this.name + '_editable')
.removeClass('w2ui-selection')
.addClass('w2ui-edit-box')
.prepend('<div style="position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px;"></div>')
.find('.w2ui-selection-resizer')
.remove();
el = $('#grid_'+ this.name + '_editable >div:first-child');
}
if (edit.inTag == null) edit.inTag = '';
if (edit.outTag == null) edit.outTag = '';
if (edit.style == null) edit.style = '';
if (edit.items == null) edit.items = [];
var val = (rec.w2ui && rec.w2ui.changes && rec.w2ui.changes[col.field] != null ? w2utils.stripTags(rec.w2ui.changes[col.field]) : w2utils.stripTags(rec[col.field]));
if (val == null) val = '';
var old_value = (typeof val != 'object' ? val : '');
if (edata.old_value != null) old_value = edata.old_value;
if (value != null) val = value;
var addStyle = (col.style != null ? col.style + ';' : '');
if (typeof col.render == 'string' && ['number', 'int', 'float', 'money', 'percent', 'size'].indexOf(col.render.split(':')[0]) != -1) {
addStyle += 'text-align: right;';
}
// normalize items
if (edit.items.length > 0 && !$.isPlainObject(edit.items[0])) {
edit.items = w2obj.field.prototype.normMenu(edit.items);
}
switch (edit.type) {
case 'select':
var html = '';
for (var i = 0; i < edit.items.length; i++) {
html += '<option value="'+ edit.items[i].id +'"'+ (edit.items[i].id == val ? ' selected="selected"' : '') +'>'+ edit.items[i].text +'</option>';
}
el.addClass('w2ui-editable')
.html('<select id="grid_'+ obj.name +'_edit_'+ recid +'_'+ column +'" column="'+ column +'" class="w2ui-input"'+
' style="width: 100%; pointer-events: auto; padding: 0 0 0 3px; margin: 0px; border-left: 0; border-right: 0; border-radius: 0px; '+
' outline: none; font-family: inherit;'+ addStyle + edit.style +'" '+
' field="'+ col.field +'" recid="'+ recid +'" '+
' '+ edit.inTag +
'>'+ html +'</select>' + edit.outTag);
setTimeout(function () {
el.find('select')
.on('change', function (event) {
delete obj.last.move;
})
.on('blur', function (event) {
if ($(this).data('keep-open') == true) return;
obj.editChange.call(obj, this, index, column, event);
});
}, 10);
break;
case 'div':
var $tmp = tr.find('[col='+ column +'] > div');
var font = 'font-family: '+ $tmp.css('font-family') + '; font-size: '+ $tmp.css('font-size') + ';';
el.addClass('w2ui-editable')
.html('<div id="grid_'+ obj.name +'_edit_'+ recid +'_'+ column +'" class="w2ui-input"'+
' contenteditable style="'+ font + addStyle + edit.style +'" autocorrect="off" autocomplete="off" spellcheck="false" '+
' field="'+ col.field +'" recid="'+ recid +'" column="'+ column +'" '+ edit.inTag +
'></div>' + edit.outTag);
if (value == null) el.find('div.w2ui-input').text(typeof val != 'object' ? val : '');
// add blur listener
var input = el.find('div.w2ui-input').get(0);
setTimeout(function () {
var tmp = input;
$(tmp).on('blur', function (event) {
if ($(this).data('keep-open') == true) return;
obj.editChange.call(obj, tmp, index, column, event);
});
}, 10);
if (value != null) $(input).text(typeof val != 'object' ? val : '');
break;
default:
var $tmp = tr.find('[col='+ column +'] > div');
var font = 'font-family: '+ $tmp.css('font-family') + '; font-size: '+ $tmp.css('font-size');
el.addClass('w2ui-editable')
.html('<input id="grid_'+ obj.name +'_edit_'+ recid +'_'+ column +'" autocorrect="off" autocomplete="off" spellcheck="false" type="text" '+
' style="'+ font +'; width: 100%; height: 100%; padding: 3px; border-color: transparent; outline: none; border-radius: 0; '+
' pointer-events: auto; '+ addStyle + edit.style +'" '+
' field="'+ col.field +'" recid="'+ recid +'" column="'+ column +'" class="w2ui-input"'+ edit.inTag +
'/>' + edit.outTag);
// issue #499
if (edit.type == 'number') {
val = w2utils.formatNumber(val);
}
if (edit.type == 'date') {
val = w2utils.formatDate(w2utils.isDate(val, edit.format, true) || new Date(), edit.format);
}
if (value == null) el.find('input').val(typeof val != 'object' ? val : '');
// init w2field
var input = el.find('input').get(0);
$(input).w2field(edit.type, $.extend(edit, { selected: val }));
// add blur listener
setTimeout(function () {
var tmp = input;
if (edit.type == 'list') {
tmp = $($(input).data('w2field').helpers.focus).find('input');
if (typeof val != 'object' && val != '') tmp.val(val).css({ opacity: 1 }).prev().css({ opacity: 1 });
el.find('input').on('change', function (event) {
obj.editChange.call(obj, input, index, column, event);
});
}
$(tmp).on('blur', function (event) {
if ($(this).data('keep-open') == true) return;
obj.editChange.call(obj, input, index, column, event);
});
}, 10);
if (value != null) $(input).val(typeof val != 'object' ? val : '');
}
setTimeout(function () {
if (!obj.last.inEditMode) return;
el.find('input, select, div.w2ui-input')
.data('old_value', old_value)
.on('mousedown', function (event) {
event.stopPropagation();
})
.on('click', function (event) {
if (edit.type == 'div') {
expand.call(el.find('div.w2ui-input')[0], null);
} else {
expand.call(el.find('input, select')[0], null);
}
})
.on('paste', function (event) {
// clean paste to be plain text
var e = event.originalEvent;
event.preventDefault();
var text = e.clipboardData.getData("text/plain");
document.execCommand("insertHTML", false, text);
})
.on('keydown', function (event) {
var el = this;
var val = (el.tagName.toUpperCase() == 'DIV' ? $(el).text() : $(el).val());
switch (event.keyCode) {
case 8: // backspace;
if (edit.type == 'list' && !$(input).data('w2field')) { // cancel backspace when deleting element
event.preventDefault();
}
break;
case 9:
case 13:
event.preventDefault();
break;
case 37:
if (w2utils.getCursorPosition(el) === 0) {
event.preventDefault();
}
break;
case 39:
if (w2utils.getCursorPosition(el) == val.length) {
w2utils.setCursorPosition(el, val.length);
event.preventDefault();
}
break;
}
// need timeout so, this handler is executed last
setTimeout(function () {
switch (event.keyCode) {
case 9: // tab
var next_rec = recid;
var next_col = event.shiftKey ? obj.prevCell(index, column, true) : obj.nextCell(index, column, true);
// next or prev row
if (next_col == null) {
var tmp = event.shiftKey ? obj.prevRow(index, column) : obj.nextRow(index, column);
if (tmp != null && tmp != index) {
next_rec = obj.records[tmp].recid;
// find first editable row
for (var c = 0; c < obj.columns.length; c++) {
var edit = obj.getCellEditable(index, c);
if (edit != null && ['checkbox', 'check'].indexOf(edit.type) == -1) {
next_col = parseInt(c);
if (!event.shiftKey) break;
}
}
}
}
if (next_rec === false) next_rec = recid;
if (next_col == null) next_col = column;
// init new or same record
el.blur();
setTimeout(function () {
if (obj.selectType != 'row') {
obj.selectNone();
obj.select({ recid: next_rec, column: next_col });
} else {
obj.editField(next_rec, next_col, null, event);
}
}, 1);
if (event.preventDefault) event.preventDefault();
break;
case 13: // enter
el.blur();
var next = event.shiftKey ? obj.prevRow(index, column) : obj.nextRow(index, column);
if (next != null && next != index) {
setTimeout(function () {
if (obj.selectType != 'row') {
obj.selectNone();
obj.select({ recid: obj.records[next].recid, column: column });
} else {
obj.editField(obj.records[next].recid, column, null, event);
}
}, 1);
}
if (el.tagName.toUpperCase() == 'DIV') {
event.preventDefault();
}
break;
case 27: // escape
var old = obj.parseField(rec, col.field);
if (rec.w2ui && rec.w2ui.changes && rec.w2ui.changes[col.field] != null) old = rec.w2ui.changes[col.field];
if ($(el).data('old_value') != null) old = $(el).data('old_value');
if (el.tagName.toUpperCase() == 'DIV') {
$(el).text(old != null ? old : '');
} else {
el.value = old != null ? old : '';
}
el.blur();
setTimeout(function () { obj.select({ recid: recid, column: column }); }, 1);
break;
}
// if input too small - expand
expand.call(el, event);
}, 1);
})
.on('keyup', function (event) {
expand.call(this, event);
});
// focus and select
setTimeout(function () {
if (!obj.last.inEditMode) return;
var tmp = el.find('.w2ui-input');
var len = ($(tmp).val() != null ? $(tmp).val().length : 0);
if (edit.type == 'div') len = $(tmp).text().length;
if (tmp.length > 0) {
tmp.focus();
clearTimeout(obj.last.kbd_timer); // keep focus
if (tmp[0].tagName.toUpperCase() != 'SELECT') w2utils.setCursorPosition(tmp[0], len);
tmp[0].resize = expand;
expand.call(tmp[0], null);
}
}, 50);
// event after
obj.trigger($.extend(edata, { phase: 'after', input: el.find('input, select, div.w2ui-input') }));
}, 5); // needs to be 5-10
return;
function expand(event) {
try {
var val = (this.tagName.toUpperCase() == 'DIV' ? $(this).text() : this.value);
var $sel = $('#grid_'+ obj.name + '_editable');
var style = 'font-family: '+ $(this).css('font-family') + '; font-size: '+ $(this).css('font-size') + '; white-space: pre;';
var width = w2utils.getStrWidth(val, style);
if (width + 20 > $sel.width()) {
$sel.width(width + 20);
}
} catch (e) {
}
}
},
editChange: function (el, index, column, event) {
var obj = this;
// keep focus
setTimeout(function () {
var $input = $(obj.box).find('#grid_'+ obj.name + '_focus');
if (!$input.is(':focus')) $input.focus();
}, 10);
// all other fields
var summary = index < 0;
index = index < 0 ? -index - 1 : index;
var records = summary ? this.summary : this.records;
var rec = records[index];
var col = this.columns[column];
var tr = $('#grid_'+ this.name + (col.frozen === true ? '_frec_' : '_rec_') + w2utils.escapeId(rec.recid));
var new_val = (el.tagName && el.tagName.toUpperCase() == 'DIV' ? $(el).text() : el.value);
var old_val = this.parseField(rec, col.field);
var tmp = $(el).data('w2field');
if (tmp) {
if (tmp.type == 'list') new_val = $(el).data('selected');
if ($.isEmptyObject(new_val) || new_val == null) new_val = '';
if (!$.isPlainObject(new_val)) new_val = tmp.clean(new_val);
}
if (el.type == 'checkbox') {
if (rec.w2ui && rec.w2ui.editable === false) el.checked = !el.checked;
new_val = el.checked;
}
// change/restore event
var edata = {
phase: 'before', type: 'change', target: this.name, input_id: el.id, recid: rec.recid, index: index, column: column,
originalEvent: (event.originalEvent ? event.originalEvent : event),
value_new: new_val,
value_previous: (rec.w2ui && rec.w2ui.changes && rec.w2ui.changes.hasOwnProperty(col.field) ? rec.w2ui.changes[col.field]: old_val),
value_original: old_val
};
if ($(event.target).data('old_value') != null) edata.value_previous = $(event.target).data('old_value');
// if (old_val == null) old_val = ''; -- do not uncomment, error otherwise
while (true) {
new_val = edata.value_new;
if ((typeof new_val != 'object' && String(old_val) != String(new_val)) ||
(typeof new_val == 'object' && new_val && new_val.id != old_val && (typeof old_val != 'object' || old_val == null || new_val.id != old_val.id))) {
// change event
edata = this.trigger($.extend(edata, { type: 'change', phase: 'before' }));
if (edata.isCancelled !== true) {
if (new_val !== edata.value_new) {
// re-evaluate the type of change to be made
continue;
}
// default action
rec.w2ui = rec.w2ui || {};
rec.w2ui.changes = rec.w2ui.changes || {};
rec.w2ui.changes[col.field] = edata.value_new;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
} else {
// restore event
edata = this.trigger($.extend(edata, { type: 'restore', phase: 'before' }));
if (edata.isCancelled !== true) {
if (new_val !== edata.value_new) {
// re-evaluate the type of change to be made
continue;
}
// default action
if (rec.w2ui && rec.w2ui.changes) delete rec.w2ui.changes[col.field];
if (rec.w2ui && $.isEmptyObject(rec.w2ui.changes)) delete rec.w2ui.changes;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
}
break;
}
// refresh cell
var cell = $(tr).find('[col='+ column +']');
if (!summary) {
if (rec.w2ui && rec.w2ui.changes && rec.w2ui.changes[col.field] != null) {
cell.addClass('w2ui-changed');
} else {
cell.removeClass('w2ui-changed');
}
// update cell data
cell.replaceWith(this.getCellHTML(index, column, summary));
}
// remove
$(this.box).find('div.w2ui-edit-box').remove();
// enable/disable toolbar search button
if (this.show.toolbarSave) {
if (this.getChanges().length > 0) this.toolbar.enable('w2ui-save'); else this.toolbar.disable('w2ui-save');
}
obj.last.inEditMode = false;
},
"delete": function (force) {
var time = (new Date()).getTime();
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'delete', force: force });
if (force) this.message(); // close message
if (edata.isCancelled === true) return;
force = edata.force;
// hide all tooltips
setTimeout(function () { $().w2tag(); }, 20);
// default action
var recs = this.getSelection();
if (recs.length === 0) return;
if (this.msgDelete != '' && !force) {
this.message({
width : 380,
height : 170,
body : '<div class="w2ui-centered">' +
w2utils.lang(obj.msgDelete).replace('NN', recs.length).replace('records', (recs.length == 1 ? 'record' : 'records')) +
'</div>',
buttons : (w2utils.settings.macButtonOrder
? '<button type="button" class="w2ui-btn btn-default" onclick="w2ui[\''+ this.name +'\'].message()">' + w2utils.lang('Cancel') + '</button>' +
'<button type="button" class="w2ui-btn" onclick="w2ui[\''+ this.name +'\'].delete(true)">' + w2utils.lang(this.msgDeleteBtn) + '</button>'
: '<button type="button" class="w2ui-btn" onclick="w2ui[\''+ this.name +'\'].delete(true)">' + w2utils.lang(this.msgDeleteBtn) + '</button>' +
'<button type="button" class="w2ui-btn btn-default" onclick="w2ui[\''+ this.name +'\'].message()">' + w2utils.lang('Cancel') + '</button>'
),
onOpen: function (event) {
var inputs = $(this.box).find('input, textarea, select, button');
inputs.off('.message')
.on('blur.message', function (evt) {
// last input
if (inputs.index(evt.target) + 1 === inputs.length) {
inputs.get(0).focus();
evt.preventDefault();
}
})
.on('keydown.message', function (evt) {
if (evt.keyCode == 27) obj.message(); // esc
});
setTimeout(function () {
$(this.box).find('.w2ui-btn.btn-default').focus();
clearTimeout(obj.last.kbd_timer);
}.bind(this), 50);
}
});
return;
}
// call delete script
var url = (typeof this.url != 'object' ? this.url : this.url.remove);
if (url) {
this.request('delete');
} else {
if (typeof recs[0] != 'object') {
this.selectNone();
this.remove.apply(this, recs);
} else {
// clear cells
for (var r = 0; r < recs.length; r++) {
var fld = this.columns[recs[r].column].field;
var ind = this.get(recs[r].recid, true);
var rec = this.records[ind];
if (ind != null && fld != 'recid') {
this.records[ind][fld] = '';
if (rec.w2ui && rec.w2ui.changes) delete rec.w2ui.changes[fld];
// -- style should not be deleted
// if (rec.style != null && $.isPlainObject(rec.style) && rec.style[recs[r].column]) {
// delete rec.style[recs[r].column];
// }
}
}
this.update();
}
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
click: function (recid, event) {
var time = (new Date()).getTime();
var column = null;
var obj = this;
if (this.last.cancelClick == true || (event && event.altKey)) return;
if ((typeof recid == 'object') && (recid !== null)) {
column = recid.column;
recid = recid.recid;
}
if (event == null) event = {};
// check for double click
if (time - parseInt(this.last.click_time) < 350 && this.last.click_recid == recid && event.type == 'click') {
this.dblClick(recid, event);
return;
}
// hide bubble
if (this.last.bubbleEl) {
$(this.last.bubbleEl).w2tag();
this.last.bubbleEl = null;
}
this.last.click_time = time;
var last_recid = this.last.click_recid;
this.last.click_recid = recid;
// column user clicked on
if (column == null && event.target) {
var tmp = event.target;
if (tmp.tagName.toUpperCase() != 'TD') tmp = $(tmp).parents('td')[0];
if ($(tmp).attr('col') != null) column = parseInt($(tmp).attr('col'));
}
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'click', recid: recid, column: column, originalEvent: event });
if (edata.isCancelled === true) return;
// default action
var obj = this;
var sel = this.getSelection();
$('#grid_'+ this.name +'_check_all').prop("checked", false);
var ind = this.get(recid, true);
var record = this.records[ind];
var selectColumns = [];
obj.last.sel_ind = ind;
obj.last.sel_col = column;
obj.last.sel_recid = recid;
obj.last.sel_type = 'click';
// multi select with shif key
if (event.shiftKey && sel.length > 0 && obj.multiSelect) {
if (sel[0].recid) {
var start = this.get(sel[0].recid, true);
var end = this.get(recid, true);
if (column > sel[0].column) {
var t1 = sel[0].column;
var t2 = column;
} else {
var t1 = column;
var t2 = sel[0].column;
}
for (var c = t1; c <= t2; c++) selectColumns.push(c);
} else {
var start = this.get(last_recid, true);
var end = this.get(recid, true);
}
var sel_add = [];
if (start > end) { var tmp = start; start = end; end = tmp; }
var url = (typeof this.url != 'object' ? this.url : this.url.get);
for (var i = start; i <= end; i++) {
if (this.searchData.length > 0 && !url && $.inArray(i, this.last.searchIds) == -1) continue;
if (this.selectType == 'row') {
sel_add.push(this.records[i].recid);
} else {
for (var sc = 0; sc < selectColumns.length; sc++) {
sel_add.push({ recid: this.records[i].recid, column: selectColumns[sc] });
}
}
//sel.push(this.records[i].recid);
}
this.select(sel_add);
} else {
var last = this.last.selection;
var flag = (last.indexes.indexOf(ind) != -1 ? true : false);
var fselect = false;
// if clicked on the checkbox
if ($(event.target).parents('td').hasClass('w2ui-col-select')) fselect = true;
// clear other if necessary
if (((!event.ctrlKey && !event.shiftKey && !event.metaKey && !fselect) || !this.multiSelect) && !this.showSelectColumn) {
if (this.selectType != 'row' && $.inArray(column, last.columns[ind]) == -1) flag = false;
if (sel.length > 300) this.selectNone(); else this.unselect(sel);
if (flag === true && sel.length == 1) {
this.unselect({ recid: recid, column: column });
} else {
this.select({ recid: recid, column: column });
}
} else {
var isChecked = $(event.target).parents('tr').find('.w2ui-grid-select-check').is(':checked');
if (this.selectType != 'row' && $.inArray(column, last.columns[ind]) == -1 && !isChecked) flag = false;
if (flag === true) {
this.unselect({ recid: recid, column: column });
} else {
this.select({ recid: recid, column: column });
}
}
}
this.status();
obj.initResize();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
columnClick: function (field, event) {
// event before
var edata = this.trigger({ phase: 'before', type: 'columnClick', target: this.name, field: field, originalEvent: event });
if (edata.isCancelled === true) return;
// default behaviour
if (this.selectType == 'row') {
var column = this.getColumn(field);
if (column && column.sortable) this.sort(field, null, (event && (event.ctrlKey || event.metaKey) ? true : false) );
if (edata.field == 'line-number') {
if (this.getSelection().length >= this.records.length) {
this.selectNone();
} else {
this.selectAll();
}
}
} else {
if (event.altKey){
var column = this.getColumn(field);
if (column && column.sortable) this.sort(field, null, (event && (event.ctrlKey || event.metaKey) ? true : false) );
}
// select entire column
if (edata.field == 'line-number') {
if (this.getSelection().length >= this.records.length) {
this.selectNone();
} else {
this.selectAll();
}
} else {
if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {
this.selectNone();
}
var tmp = this.getSelection();
var column = this.getColumn(edata.field, true);
var sel = [];
var cols = [];
// check if there was a selection before
if (tmp.length != 0 && event.shiftKey) {
var start = column;
var end = tmp[0].column;
if (start > end) {
start = tmp[0].column;
end = column;
}
for (var i=start; i<=end; i++) cols.push(i);
} else {
cols.push(column);
}
var edata = this.trigger({ phase: 'before', type: 'columnSelect', target: this.name, columns: cols });
if (edata.isCancelled !== true) {
for (var i = 0; i < this.records.length; i++) {
sel.push({ recid: this.records[i].recid, column: cols });
}
this.select(sel);
}
this.trigger($.extend(edata, { phase: 'after' }));
}
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
columnDblClick: function (field, event) {
// event before
var edata = this.trigger({ phase: 'before', type: 'columnDblClick', target: this.name, field: field, originalEvent: event });
if (edata.isCancelled === true) return;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
focus: function (event) {
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', type: 'focus', target: this.name, originalEvent: event });
if (edata.isCancelled === true) return false;
// default behaviour
this.hasFocus = true;
$(this.box).removeClass('w2ui-inactive').find('.w2ui-inactive').removeClass('w2ui-inactive');
setTimeout(function () {
var $input = $(obj.box).find('#grid_'+ obj.name + '_focus');
if (!$input.is(':focus')) $input.focus();
}, 10);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
blur: function (event) {
// event before
var edata = this.trigger({ phase: 'before', type: 'blur', target: this.name, originalEvent: event });
if (edata.isCancelled === true) return false;
// default behaviour
this.hasFocus = false;
$(this.box).addClass('w2ui-inactive').find('.w2ui-selected').addClass('w2ui-inactive');
$(this.box).find('.w2ui-selection').addClass('w2ui-inactive');
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
keydown: function (event) {
// this method is called from w2utils
var obj = this;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (obj.keyboard !== true) return;
// trigger event
var edata = obj.trigger({ phase: 'before', type: 'keydown', target: obj.name, originalEvent: event });
if (edata.isCancelled === true) return;
// default behavior
if ($(this.box).find('>.w2ui-message').length > 0) {
// if there are messages
if (event.keyCode == 27) this.message();
return
}
var empty = false;
var records = $('#grid_'+ obj.name +'_records');
var sel = obj.getSelection();
if (sel.length === 0) empty = true;
var recid = sel[0] || null;
var columns = [];
var recid2 = sel[sel.length-1];
if (typeof recid == 'object' && recid != null) {
recid = sel[0].recid;
columns = [];
var ii = 0;
while (true) {
if (!sel[ii] || sel[ii].recid != recid) break;
columns.push(sel[ii].column);
ii++;
}
recid2 = sel[sel.length-1].recid;
}
var ind = obj.get(recid, true);
var ind2 = obj.get(recid2, true);
var rec = obj.get(recid);
var recEL = $('#grid_'+ obj.name +'_rec_'+ (ind != null ? w2utils.escapeId(obj.records[ind].recid) : 'none'));
var cancel = false;
var key = event.keyCode;
var shiftKey = event.shiftKey;
switch (key) {
case 8: // backspace
case 46: // delete
// delete if button is visible
obj["delete"]();
cancel = true;
event.stopPropagation();
break;
case 27: // escape
obj.selectNone();
cancel = true;
break;
case 65: // cmd + A
if (!event.metaKey && !event.ctrlKey) break;
obj.selectAll();
cancel = true;
break;
case 13: // enter
// if expandable columns - expand it
if (this.selectType == 'row' && obj.show.expandColumn === true) {
if (recEL.length <= 0) break;
obj.toggle(recid, event);
cancel = true;
} else { // or enter edit
for (var c = 0; c < this.columns.length; c++) {
var edit = this.getCellEditable(ind, c);
if (edit) {
columns.push(parseInt(c));
break;
}
}
// edit last column that was edited
if (this.selectType == 'row' && this.last._edit && this.last._edit.column) {
columns = [this.last._edit.column];
}
if (columns.length > 0) {
obj.editField(recid, columns[0], null, event);
cancel = true;
}
}
break;
case 37: // left
if (empty) { // no selection
selectTopRecord();
break;
}
if (this.selectType == 'row') {
if (recEL.length <= 0) break;
var tmp = this.records[ind].w2ui || {};
if (tmp && tmp.parent_recid != null && (!Array.isArray(tmp.children) || tmp.children.length === 0 || !tmp.expanded)) {
obj.unselect(recid);
obj.collapse(tmp.parent_recid, event);
obj.select(tmp.parent_recid);
} else {
obj.collapse(recid, event);
}
} else {
var prev = obj.prevCell(ind, columns[0]);
if (!shiftKey && prev == null) {
this.selectNone();
prev = 0;
}
if (prev != null) {
if (shiftKey && obj.multiSelect) {
if (tmpUnselect()) return;
var tmp = [];
var newSel = [];
var unSel = [];
if (columns.indexOf(this.last.sel_col) === 0 && columns.length > 1) {
for (var i = 0; i < sel.length; i++) {
if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
unSel.push({ recid: sel[i].recid, column: columns[columns.length-1] });
}
obj.unselect(unSel);
obj.scrollIntoView(ind, columns[columns.length-1], true);
} else {
for (var i = 0; i < sel.length; i++) {
if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
newSel.push({ recid: sel[i].recid, column: prev });
}
obj.select(newSel);
obj.scrollIntoView(ind, prev, true);
}
} else {
event.metaKey = false;
obj.click({ recid: recid, column: prev }, event);
obj.scrollIntoView(ind, prev, true);
}
} else {
// if selected more then one, then select first
if (!shiftKey) {
if (sel.length > 1) {
obj.selectNone();
} else {
for (var s = 1; s < sel.length; s++) obj.unselect(sel[s]);
}
}
}
}
cancel = true;
break;
case 39: // right
if (empty) {
selectTopRecord();
break;
}
if (this.selectType == 'row') {
if (recEL.length <= 0) break;
obj.expand(recid, event);
} else {
var next = obj.nextCell(ind, columns[columns.length-1]); // columns is an array of selected columns
if (!shiftKey && next == null) {
this.selectNone();
next = this.columns.length-1;
}
if (next != null) {
if (shiftKey && key == 39 && obj.multiSelect) {
if (tmpUnselect()) return;
var tmp = [];
var newSel = [];
var unSel = [];
if (columns.indexOf(this.last.sel_col) == columns.length-1 && columns.length > 1) {
for (var i = 0; i < sel.length; i++) {
if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
unSel.push({ recid: sel[i].recid, column: columns[0] });
}
obj.unselect(unSel);
obj.scrollIntoView(ind, columns[0], true);
} else {
for (var i = 0; i < sel.length; i++) {
if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
newSel.push({ recid: sel[i].recid, column: next });
}
obj.select(newSel);
obj.scrollIntoView(ind, next, true);
}
} else {
event.metaKey = false;
obj.click({ recid: recid, column: next }, event);
obj.scrollIntoView(ind, next, true);
}
} else {
// if selected more then one, then select first
if (!shiftKey) {
if (sel.length > 1) {
obj.selectNone();
} else {
for (var s = 0; s < sel.length-1; s++) obj.unselect(sel[s]);
}
}
}
}
cancel = true;
break;
case 38: // up
if (empty) selectTopRecord();
if (recEL.length <= 0) break;
// move to the previous record
var prev = obj.prevRow(ind, columns[0]);
if (!shiftKey && prev == null) {
if (this.searchData.length != 0 && !url) {
prev = this.last.searchIds[0];
} else {
prev = 0;
}
}
if (prev != null) {
if (shiftKey && obj.multiSelect) { // expand selection
if (tmpUnselect()) return;
if (obj.selectType == 'row') {
if (obj.last.sel_ind > prev && obj.last.sel_ind != ind2) {
obj.unselect(obj.records[ind2].recid);
} else {
obj.select(obj.records[prev].recid);
}
} else {
if (obj.last.sel_ind > prev && obj.last.sel_ind != ind2) {
prev = ind2;
var tmp = [];
for (var c = 0; c < columns.length; c++) tmp.push({ recid: obj.records[prev].recid, column: columns[c] });
obj.unselect(tmp);
} else {
var tmp = [];
for (var c = 0; c < columns.length; c++) tmp.push({ recid: obj.records[prev].recid, column: columns[c] });
obj.select(tmp);
}
}
} else { // move selected record
if (sel.length > 300) this.selectNone(); else this.unselect(sel);
obj.click({ recid: obj.records[prev].recid, column: columns[0] }, event);
}
obj.scrollIntoView(prev);
if (event.preventDefault) event.preventDefault();
} else {
// if selected more then one, then select first
if (!shiftKey) {
if (sel.length > 1) {
obj.selectNone();
} else {
for (var s = 1; s < sel.length; s++) obj.unselect(sel[s]);
}
}
}
break;
case 40: // down
if (empty) selectTopRecord();
if (recEL.length <= 0) break;
// move to the next record
var next = obj.nextRow(ind2, columns[0]);
if (!shiftKey && next == null) {
if (this.searchData.length != 0 && !url) {
next = this.last.searchIds[this.last.searchIds.length - 1];
} else {
next = this.records.length - 1;
}
}
if (next != null) {
if (shiftKey && obj.multiSelect) { // expand selection
if (tmpUnselect()) return;
if (obj.selectType == 'row') {
if (this.last.sel_ind < next && this.last.sel_ind != ind) {
obj.unselect(obj.records[ind].recid);
} else {
obj.select(obj.records[next].recid);
}
} else {
if (this.last.sel_ind < next && this.last.sel_ind != ind) {
next = ind;
var tmp = [];
for (var c = 0; c < columns.length; c++) tmp.push({ recid: obj.records[next].recid, column: columns[c] });
obj.unselect(tmp);
} else {
var tmp = [];
for (var c = 0; c < columns.length; c++) tmp.push({ recid: obj.records[next].recid, column: columns[c] });
obj.select(tmp);
}
}
} else { // move selected record
if (sel.length > 300) this.selectNone(); else this.unselect(sel);
obj.click({ recid: obj.records[next].recid, column: columns[0] }, event);
}
obj.scrollIntoView(next);
cancel = true;
} else {
// if selected more then one, then select first
if (!shiftKey) {
if (sel.length > 1) {
obj.selectNone();
} else {
for (var s = 0; s < sel.length-1; s++) obj.unselect(sel[s]);
}
}
}
break;
// copy & paste
case 17: // ctrl key
case 91: // cmd key
// SLOW: 10k records take 7.0
if (empty) break;
// in Safari need to copy to buffer on cmd or ctrl key (otherwise does not work)
if (obj.last.isSafari) {
obj.last.copy_event = obj.copy(false, event);
$('#grid_'+ obj.name + '_focus').val(obj.last.copy_event.text).select();
}
break;
case 67: // - c
// this fill trigger event.onComplete
if (event.metaKey || event.ctrlKey) {
if (obj.last.isSafari) {
obj.copy(obj.last.copy_event, event);
} else {
obj.last.copy_event = obj.copy(false, event);
$('#grid_'+ obj.name + '_focus').val(obj.last.copy_event.text).select();
obj.copy(obj.last.copy_event, event);
}
}
break;
case 88: // x - cut
if (empty) break;
if (event.ctrlKey || event.metaKey) {
if (obj.last.isSafari) {
obj.copy(obj.last.copy_event, event);
} else {
obj.last.copy_event = obj.copy(false, event);
$('#grid_'+ obj.name + '_focus').val(obj.last.copy_event.text).select();
obj.copy(obj.last.copy_event, event);
}
}
break;
}
var tmp = [32, 187, 189, 192, 219, 220, 221, 186, 222, 188, 190, 191]; // other typable chars
for (var i=48; i<=111; i++) tmp.push(i); // 0-9,a-z,A-Z,numpad
if (tmp.indexOf(key) != -1 && !event.ctrlKey && !event.metaKey && !cancel) {
if (columns.length === 0) columns.push(0);
cancel = false;
// move typed key into edit
setTimeout(function () {
var focus = $('#grid_'+ obj.name + '_focus');
var key = focus.val();
focus.val('');
obj.editField(recid, columns[0], key, event);
}, 1);
}
if (cancel) { // cancel default behaviour
if (event.preventDefault) event.preventDefault();
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
function selectTopRecord() {
var ind = Math.floor(records[0].scrollTop / obj.recordHeight) + 1;
if (!obj.records[ind] || ind < 2) ind = 0;
obj.select({ recid: obj.records[ind].recid, column: 0});
}
function tmpUnselect () {
if (obj.last.sel_type != 'click') return false;
if (obj.selectType != 'row') {
obj.last.sel_type = 'key';
if (sel.length > 1) {
for (var s = 0; s < sel.length; s++) {
if (sel[s].recid == obj.last.sel_recid && sel[s].column == obj.last.sel_col) {
sel.splice(s, 1);
break;
}
}
obj.unselect(sel);
return true;
}
return false;
} else {
obj.last.sel_type = 'key';
if (sel.length > 1) {
sel.splice(sel.indexOf(obj.records[obj.last.sel_ind].recid), 1);
obj.unselect(sel);
return true;
}
return false;
}
}
},
scrollIntoView: function (ind, column, instant) {
var buffered = this.records.length;
if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
if (buffered === 0) return;
if (ind == null) {
var sel = this.getSelection();
if (sel.length === 0) return;
if ($.isPlainObject(sel[0])) {
ind = sel[0].index;
column = sel[0].column;
} else {
ind = this.get(sel[0], true);
}
}
var records = $('#grid_'+ this.name +'_records');
// if all records in view
var len = this.last.searchIds.length;
if (len > 0) ind = this.last.searchIds.indexOf(ind); // if search is applied
// vertical
if (records.height() < this.recordHeight * (len > 0 ? len : buffered) && records.length > 0) {
// scroll to correct one
var t1 = Math.floor(records[0].scrollTop / this.recordHeight);
var t2 = t1 + Math.floor(records.height() / this.recordHeight);
if (ind == t1) {
if (instant === true) {
records.prop({ 'scrollTop': records.scrollTop() - records.height() / 1.3 });
} else {
records.stop();
records.animate({ 'scrollTop': records.scrollTop() - records.height() / 1.3 }, 250, 'linear');
}
}
if (ind == t2) {
if (instant === true) {
records.prop({ 'scrollTop': records.scrollTop() + records.height() / 1.3 });
} else {
records.stop();
records.animate({ 'scrollTop': records.scrollTop() + records.height() / 1.3 }, 250, 'linear');
}
}
if (ind < t1 || ind > t2) {
if (instant === true) {
records.prop({ 'scrollTop': (ind - 1) * this.recordHeight });
} else {
records.stop();
records.animate({ 'scrollTop': (ind - 1) * this.recordHeight }, 250, 'linear');
}
}
}
// horizontal
if (column != null) {
var x1 = 0;
var x2 = 0;
var sb = w2utils.scrollBarSize();
for (var i = 0; i <= column; i++) {
var col = this.columns[i];
if (col.frozen || col.hidden) continue;
x1 = x2;
x2 += parseInt(col.sizeCalculated);
}
if (records.width() < x2 - records.scrollLeft()) { // right
if (instant === true) {
records.prop({ 'scrollLeft': x1 - sb });
} else {
records.animate({ 'scrollLeft': x1 - sb }, 250, 'linear');
}
} else if (x1 < records.scrollLeft()) { // left
if (instant === true) {
records.prop({ 'scrollLeft': x2 - records.width() + sb * 2 });
} else {
records.animate({ 'scrollLeft': x2 - records.width() + sb * 2 }, 250, 'linear');
}
}
}
},
dblClick: function (recid, event) {
// find columns
var column = null;
if ((typeof recid == 'object') && (recid !== null)) {
column = recid.column;
recid = recid.recid;
}
if (event == null) event = {};
// column user clicked on
if (column == null && event.target) {
var tmp = event.target;
if (tmp.tagName.toUpperCase() != 'TD') tmp = $(tmp).parents('td')[0];
column = parseInt($(tmp).attr('col'));
}
var index = this.get(recid, true);
var rec = this.records[index];
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'dblClick', recid: recid, column: column, originalEvent: event });
if (edata.isCancelled === true) return;
// default action
this.selectNone();
var edit = this.getCellEditable(index, column);
if (edit) {
this.editField(recid, column, null, event);
} else {
this.select({ recid: recid, column: column });
if (this.show.expandColumn || (rec.w2ui && Array.isArray(rec.w2ui.children))) this.toggle(recid);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
contextMenu: function (recid, column, event) {
var obj = this;
if (obj.last.userSelect == 'text') return;
if (event == null) event = { offsetX: 0, offsetY: 0, target: $('#grid_'+ obj.name +'_rec_'+ recid)[0] };
if (event.offsetX == null) {
event.offsetX = event.layerX - event.target.offsetLeft;
event.offsetY = event.layerY - event.target.offsetTop;
}
if (w2utils.isFloat(recid)) recid = parseFloat(recid);
var sel = this.getSelection();
if (this.selectType == 'row') {
if (sel.indexOf(recid) == -1) obj.click(recid);
} else {
var $tmp = $(event.target);
if ($tmp[0].tagName.toUpperCase() != 'TD') $tmp = $(event.target).parents('td');
var selected = false;
column = $tmp.attr('col');
// check if any selected sel in the right row/column
for (var i=0; i<sel.length; i++) {
if (sel[i].recid == recid || sel[i].column == column) selected = true;
}
if (!selected && recid != null) obj.click({ recid: recid, column: column });
if (!selected && column != null) obj.columnClick(this.columns[column].field, event);
}
// event before
var edata = obj.trigger({ phase: 'before', type: 'contextMenu', target: obj.name, originalEvent: event, recid: recid, column: column });
if (edata.isCancelled === true) return;
// default action
if (obj.menu.length > 0) {
$(obj.box).find(event.target)
.w2menu(obj.menu, {
originalEvent: event,
contextMenu: true,
onSelect: function (event) {
obj.menuClick(recid, event);
}
}
);
}
// cancel event
if (event.preventDefault) event.preventDefault();
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
},
menuClick: function (recid, event) {
var obj = this;
// event before
var edata = obj.trigger({
phase: 'before', type: 'menuClick', target: obj.name,
originalEvent: event.originalEvent, menuEvent: event,
recid: recid, menuIndex: event.index, menuItem: event.item
});
if (edata.isCancelled === true) return;
// default action
// -- empty
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
},
toggle: function (recid) {
var rec = this.get(recid);
rec.w2ui = rec.w2ui || {};
if (rec.w2ui.expanded === true) return this.collapse(recid); else return this.expand(recid);
},
expand: function (recid) {
var obj = this;
var ind = this.get(recid, true);
var rec = this.records[ind];
rec.w2ui = rec.w2ui || {};
var id = w2utils.escapeId(recid);
var children = rec.w2ui.children;
if (Array.isArray(children)) {
if (rec.w2ui.expanded === true || children.length === 0) return false; // already shown
var edata = this.trigger({ phase: 'before', type: 'expand', target: this.name, recid: recid });
if (edata.isCancelled === true) return false;
rec.w2ui.expanded = true;
children.forEach(function (child) {
child.w2ui = child.w2ui || {};
child.w2ui.parent_recid = rec.recid;
if (child.w2ui.children == null) child.w2ui.children = [];
});
this.records.splice.apply(this.records, [ind + 1, 0].concat(children));
this.total += children.length;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
this.localSort(true, true);
if (this.searchData.length > 0) {
this.localSearch(true);
}
}
this.refresh();
this.trigger($.extend(edata, { phase: 'after' }));
} else {
if ($('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').length > 0 || this.show.expandColumn !== true) return false;
if (rec.w2ui.expanded == 'none') return false;
// insert expand row
$('#grid_'+ this.name +'_rec_'+ id).after(
'<tr id="grid_'+ this.name +'_rec_'+ recid +'_expanded_row" class="w2ui-expanded-row">'+
' <td colspan="100" class="w2ui-expanded2">'+
' <div id="grid_'+ this.name +'_rec_'+ recid +'_expanded"></div>'+
' </td>'+
' <td class="w2ui-grid-data-last"></td>'+
'</tr>');
$('#grid_'+ this.name +'_frec_'+ id).after(
'<tr id="grid_'+ this.name +'_frec_'+ recid +'_expanded_row" class="w2ui-expanded-row">'+
(this.show.lineNumbers ? '<td class="w2ui-col-number"></td>' : '') +
' <td class="w2ui-grid-data w2ui-expanded1" colspan="100">'+
' <div id="grid_'+ this.name +'_frec_'+ recid +'_expanded"></div>'+
' </td>'+
'</tr>');
// event before
var edata = this.trigger({ phase: 'before', type: 'expand', target: this.name, recid: recid,
box_id: 'grid_'+ this.name +'_rec_'+ recid +'_expanded', fbox_id: 'grid_'+ this.name +'_frec_'+ id +'_expanded' });
if (edata.isCancelled === true) {
$('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').remove();
$('#grid_'+ this.name +'_frec_'+ id +'_expanded_row').remove();
return false;
}
// expand column
var row1 = $(this.box).find('#grid_'+ this.name +'_rec_'+ recid +'_expanded');
var row2 = $(this.box).find('#grid_'+ this.name +'_frec_'+ recid +'_expanded');
var innerHeight = row1.find('> div:first-child').height();
if (row1.height() < innerHeight) {
row1.css({ height: innerHeight + 'px' });
}
if (row2.height() < innerHeight) {
row2.css({ height: innerHeight + 'px' });
}
// default action
$('#grid_'+ this.name +'_rec_'+ id).attr('expanded', 'yes').addClass('w2ui-expanded');
$('#grid_'+ this.name +'_frec_'+ id).attr('expanded', 'yes').addClass('w2ui-expanded');
// $('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').show();
$('#grid_'+ this.name +'_cell_'+ this.get(recid, true) +'_expand div').html('-');
rec.w2ui.expanded = true;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
this.resizeRecords();
}
return true;
},
collapse: function (recid) {
var obj = this;
var ind = this.get(recid, true);
var rec = this.records[ind];
rec.w2ui = rec.w2ui || {};
var id = w2utils.escapeId(recid);
var children = rec.w2ui.children;
if (Array.isArray(children)) {
if (rec.w2ui.expanded !== true) return false; // already hidden
var edata = this.trigger({ phase: 'before', type: 'collapse', target: this.name, recid: recid });
if (edata.isCancelled === true) return false;
clearExpanded(rec);
var stops = [];
for (var r = rec; r != null; r = this.get(r.w2ui.parent_recid))
stops.push(r.w2ui.parent_recid);
// stops contains 'undefined' plus the ID of all nodes in the path from 'rec' to the tree root
var start = ind + 1;
var end = start;
while (true) {
if (this.records.length <= end + 1 || this.records[end+1].w2ui == null ||
stops.indexOf(this.records[end+1].w2ui.parent_recid) >= 0) {
break;
}
end++;
}
this.records.splice(start, end - start + 1);
this.total -= end - start + 1;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
if (this.searchData.length > 0) {
this.localSearch(true);
}
}
this.refresh();
obj.trigger($.extend(edata, { phase: 'after' }));
} else {
if ($('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').length === 0 || this.show.expandColumn !== true) return false;
// event before
var edata = this.trigger({ phase: 'before', type: 'collapse', target: this.name, recid: recid,
box_id: 'grid_'+ this.name +'_rec_'+ id +'_expanded', fbox_id: 'grid_'+ this.name +'_frec_'+ id +'_expanded' });
if (edata.isCancelled === true) return false;
// default action
$('#grid_'+ this.name +'_rec_'+ id).removeAttr('expanded').removeClass('w2ui-expanded');
$('#grid_'+ this.name +'_frec_'+ id).removeAttr('expanded').removeClass('w2ui-expanded');
$('#grid_'+ this.name +'_cell_'+ this.get(recid, true) +'_expand div').html('+');
$('#grid_'+ obj.name +'_rec_'+ id +'_expanded').css('height', '0px');
$('#grid_'+ obj.name +'_frec_'+ id +'_expanded').css('height', '0px');
setTimeout(function () {
$('#grid_'+ obj.name +'_rec_'+ id +'_expanded_row').remove();
$('#grid_'+ obj.name +'_frec_'+ id +'_expanded_row').remove();
rec.w2ui.expanded = false;
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
obj.resizeRecords();
}, 300);
}
return true;
function clearExpanded(rec) {
rec.w2ui.expanded = false;
for (var i = 0; i < rec.w2ui.children.length; i++) {
var subRec = rec.w2ui.children[i];
if (subRec.w2ui.expanded) {
clearExpanded(subRec);
}
}
}
},
sort: function (field, direction, multiField) { // if no params - clears sort
// event before
var edata = this.trigger({ phase: 'before', type: 'sort', target: this.name, field: field, direction: direction, multiField: multiField });
if (edata.isCancelled === true) return;
// check if needed to quit
if (field != null) {
// default action
var sortIndex = this.sortData.length;
for (var s = 0; s < this.sortData.length; s++) {
if (this.sortData[s].field == field) { sortIndex = s; break; }
}
if (direction == null) {
if (this.sortData[sortIndex] == null) {
direction = 'asc';
} else {
if(this.sortData[sortIndex].direction == null) {
this.sortData[sortIndex].direction = '';
}
switch (this.sortData[sortIndex].direction.toLowerCase()) {
case 'asc' : direction = 'desc'; break;
case 'desc' : direction = 'asc'; break;
default : direction = 'asc'; break;
}
}
}
if (this.multiSort === false) { this.sortData = []; sortIndex = 0; }
if (multiField != true) { this.sortData = []; sortIndex = 0; }
// set new sort
if (this.sortData[sortIndex] == null) this.sortData[sortIndex] = {};
this.sortData[sortIndex].field = field;
this.sortData[sortIndex].direction = direction;
} else {
this.sortData = [];
}
// if local
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!url) {
this.localSort(true, true);
if (this.searchData.length > 0) this.localSearch(true);
// reset vertical scroll
this.last.scrollTop = 0;
$('#grid_'+ this.name +'_records').prop('scrollTop', 0);
// event after
this.trigger($.extend(edata, { phase: 'after', direction: direction }));
this.refresh();
} else {
// event after
this.trigger($.extend(edata, { phase: 'after', direction: direction }));
this.last.xhr_offset = 0;
this.reload();
}
},
copy: function (flag, oEvent) {
if ($.isPlainObject(flag)) {
// event after
this.trigger($.extend(flag, { phase: 'after' }));
return flag.text;
}
// generate text to copy
var sel = this.getSelection();
if (sel.length === 0) return '';
var text = '';
if (typeof sel[0] == 'object') { // cell copy
// find min/max column
var minCol = sel[0].column;
var maxCol = sel[0].column;
var recs = [];
for (var s = 0; s < sel.length; s++) {
if (sel[s].column < minCol) minCol = sel[s].column;
if (sel[s].column > maxCol) maxCol = sel[s].column;
if (recs.indexOf(sel[s].index) == -1) recs.push(sel[s].index);
}
recs.sort(function(a, b) { return a-b; }); // sort function must be for numerical sort
for (var r = 0 ; r < recs.length; r++) {
var ind = recs[r];
for (var c = minCol; c <= maxCol; c++) {
var col = this.columns[c];
if (col.hidden === true) continue;
text += this.getCellCopy(ind, c) + '\t';
}
text = text.substr(0, text.length-1); // remove last \t
text += '\n';
}
} else { // row copy
// copy headers
for (var c = 0; c < this.columns.length; c++) {
var col = this.columns[c];
if (col.hidden === true) continue;
var colName = (col.text ? col.text : col.field);
if (col.text && col.text.length < 3 && col.tooltip) colName = col.tooltip; // if column name is less then 3 char and there is tooltip - use it
text += '"' + w2utils.stripTags(colName) + '"\t';
}
text = text.substr(0, text.length-1); // remove last \t
text += '\n';
// copy selected text
for (var s = 0; s < sel.length; s++) {
var ind = this.get(sel[s], true);
for (var c = 0; c < this.columns.length; c++) {
var col = this.columns[c];
if (col.hidden === true) continue;
text += '"' + this.getCellCopy(ind, c) + '"\t';
}
text = text.substr(0, text.length-1); // remove last \t
text += '\n';
}
}
text = text.substr(0, text.length - 1);
// if called without params
if (flag == null) {
// before event
var edata = this.trigger({ phase: 'before', type: 'copy', target: this.name, text: text,
cut: (oEvent.keyCode == 88 ? true : false), originalEvent: oEvent });
if (edata.isCancelled === true) return '';
text = edata.text;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return text;
} else if (flag === false) { // only before event
// before event
var edata = this.trigger({ phase: 'before', type: 'copy', target: this.name, text: text,
cut: (oEvent.keyCode == 88 ? true : false), originalEvent: oEvent });
if (edata.isCancelled === true) return '';
text = edata.text;
return edata;
}
},
/**
* Gets value to be copied to the clipboard
* @param ind index of the record
* @param col_ind index of the column
* @returns the displayed value of the field's record associated with the cell
*/
getCellCopy: function(ind, col_ind) {
return w2utils.stripTags(this.getCellHTML(ind, col_ind));
},
paste: function (text) {
var sel = this.getSelection();
var ind = this.get(sel[0].recid, true);
var col = sel[0].column;
// before event
var edata = this.trigger({ phase: 'before', type: 'paste', target: this.name, text: text, index: ind, column: col });
if (edata.isCancelled === true) return;
text = edata.text;
// default action
if (this.selectType == 'row' || sel.length === 0) {
console.log('ERROR: You can paste only if grid.selectType = \'cell\' and when at least one cell selected.');
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return;
}
var newSel = [];
var text = text.split('\n');
for (var t = 0; t < text.length; t++) {
var tmp = text[t].split('\t');
var cnt = 0;
var rec = this.records[ind];
var cols = [];
if (rec == null) continue;
for (var dt = 0; dt < tmp.length; dt++) {
if (!this.columns[col + cnt]) continue;
this.setCellPaste(rec, col + cnt, tmp[dt]);
cols.push(col + cnt);
cnt++;
}
for (var c = 0; c < cols.length; c++) newSel.push({ recid: rec.recid, column: cols[c] });
ind++;
}
this.selectNone();
this.select(newSel);
this.refresh();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
/**
* Sets record field using clipboard text
* @param rec record
* @param col_ind column index
* @param paste sub part of the pasted text
*/
setCellPaste: function(rec, col_ind, paste) {
var field = this.columns[col_ind].field;
rec.w2ui = rec.w2ui || {};
rec.w2ui.changes = rec.w2ui.changes || {};
rec.w2ui.changes[field] = paste;
},
// ==================================================
// --- Common functions
resize: function () {
var obj = this;
var time = (new Date()).getTime();
// make sure the box is right
if (!this.box || $(this.box).attr('name') != this.name) return;
// determine new width and height
$(this.box).find('> div.w2ui-grid-box')
.css('width', $(this.box).width())
.css('height', $(this.box).height());
// event before
var edata = this.trigger({ phase: 'before', type: 'resize', target: this.name });
if (edata.isCancelled === true) return;
// resize
obj.resizeBoxes();
obj.resizeRecords();
if (obj.toolbar && obj.toolbar.resize) obj.toolbar.resize();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
update: function (cells) {
var time = (new Date()).getTime();
if (this.box == null) return 0;
if (cells == null) {
for (var index = this.last.range_start - 1; index <= this.last.range_end - 1; index++) {
if (index < 0) continue;
var rec = this.records[index] || {};
if (!rec.w2ui) rec.w2ui = {};
for (var column = 0; column < this.columns.length; column++) {
var row = $(this.box).find('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(rec.recid));
var cell = $(this.box).find('#grid_'+ this.name + '_data_'+ index +'_'+ column);
cell.replaceWith(this.getCellHTML(index, column, false));
cell = $(this.box).find('#grid_'+ this.name + '_data_'+ index +'_'+ column); // need to reselect as it was replaced
// assign style
if (rec.w2ui.style != null && !$.isEmptyObject(rec.w2ui.style)) {
if (typeof rec.w2ui.style == 'string') {
row.attr('style', rec.w2ui.style);
}
if ($.isPlainObject(rec.w2ui.style) && typeof rec.w2ui.style[column] == 'string') {
cell.attr('style', rec.w2ui.style[column]);
}
} else {
cell.attr('style', '');
}
// assign class
if (rec.w2ui.class != null && !$.isEmptyObject(rec.w2ui.class)) {
if (typeof rec.w2ui.class == 'string') {
row.addClass(rec.w2ui.class);
}
if ($.isPlainObject(rec.w2ui.class) && typeof rec.w2ui.class[column] == 'string') {
cell.addClass(rec.w2ui.class[column]);
}
}
}
}
} else {
for (var i = 0; i < cells.length; i++) {
var index = cells[i].index;
var column = cells[i].column;
if (index < 0) continue;
if (index == null || column == null) {
console.log('ERROR: Wrong argument for grid.update(cells), cells should be [{ index: X, column: Y }, ...]');
continue;
}
var rec = this.records[index] || {};
var row = $(this.box).find('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(rec.recid));
var cell = $(this.box).find('#grid_'+ this.name + '_data_'+ index +'_'+ column);
if (!rec.w2ui) rec.w2ui = {};
cell.replaceWith(this.getCellHTML(index, column, false));
cell = $(this.box).find('#grid_'+ this.name + '_data_'+ index +'_'+ column); // need to reselect as it was replaced
// assign style
if (rec.w2ui.style != null && !$.isEmptyObject(rec.w2ui.style)) {
if (typeof rec.w2ui.style == 'string') {
row.attr('style', rec.w2ui.style);
}
if ($.isPlainObject(rec.w2ui.style) && typeof rec.w2ui.style[column] == 'string') {
cell.attr('style', rec.w2ui.style[column]);
}
} else {
cell.attr('style', '');
}
// assign class
if (rec.w2ui.class != null && !$.isEmptyObject(rec.w2ui.class)) {
if (typeof rec.w2ui.class == 'string') {
row.addClass(rec.w2ui.class);
}
if ($.isPlainObject(rec.w2ui.class) && typeof rec.w2ui.class[column] == 'string') {
cell.addClass(rec.w2ui.class[column]);
}
}
}
}
return (new Date()).getTime() - time;
},
refreshCell: function (recid, field) {
var index = this.get(recid, true);
var col_ind = this.getColumn(field, true);
var isSummary = (this.records[index] && this.records[index].recid == recid ? false : true);
var cell = $(this.box).find((isSummary ? '.w2ui-grid-summary ' : '') + '#grid_'+ this.name + '_data_'+ index +'_'+ col_ind);
if (cell.length == 0) return false;
// set cell html and changed flag
cell.replaceWith(this.getCellHTML(index, col_ind, isSummary));
},
refreshRow: function (recid, ind) {
var tr1 = $(this.box).find('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid));
var tr2 = $(this.box).find('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
if (tr1.length > 0) {
if (ind == null) ind = this.get(recid, true);
var line = tr1.attr('line');
var isSummary = (this.records[ind] && this.records[ind].recid == recid ? false : true);
// if it is searched, find index in search array
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (this.searchData.length > 0 && !url) for (var s = 0; s < this.last.searchIds.length; s++) if (this.last.searchIds[s] == ind) ind = s;
var rec_html = this.getRecordHTML(ind, line, isSummary);
$(tr1).replaceWith(rec_html[0]);
$(tr2).replaceWith(rec_html[1]);
// apply style to row if it was changed in render functions
var st = (this.records[ind].w2ui ? this.records[ind].w2ui.style : '');
if (typeof st == 'string') {
var tr1 = $(this.box).find('#grid_'+ this.name +'_frec_'+ w2utils.escapeId(recid));
var tr2 = $(this.box).find('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
tr1.attr('custom_style', st);
tr2.attr('custom_style', st);
if (tr1.hasClass('w2ui-selected')) {
st = st.replace('background-color', 'none');
}
tr1[0].style.cssText = 'height: '+ this.recordHeight + 'px;' + st;
tr2[0].style.cssText = 'height: '+ this.recordHeight + 'px;' + st;
}
if (isSummary) {
this.resize();
}
}
},
refresh: function () {
var obj = this;
var time = (new Date()).getTime();
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (this.total <= 0 && !url && this.searchData.length === 0) {
this.total = this.records.length;
}
this.toolbar.disable('w2ui-edit', 'w2ui-delete');
if (!this.box) return;
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'refresh' });
if (edata.isCancelled === true) return;
// -- header
if (this.show.header) {
$('#grid_'+ this.name +'_header').html(this.header +'&#160;').show();
} else {
$('#grid_'+ this.name +'_header').hide();
}
// -- toolbar
if (this.show.toolbar) {
// if select-collumn is checked - no toolbar refresh
if (this.toolbar && this.toolbar.get('w2ui-column-on-off') && this.toolbar.get('w2ui-column-on-off').checked) {
// no action
} else {
$('#grid_'+ this.name +'_toolbar').show();
// refresh toolbar all but search field
if (typeof this.toolbar == 'object') {
var tmp = this.toolbar.items;
for (var t = 0; t < tmp.length; t++) {
if (tmp[t].id == 'w2ui-search' || tmp[t].type == 'break') continue;
this.toolbar.refresh(tmp[t].id);
}
}
}
} else {
$('#grid_'+ this.name +'_toolbar').hide();
}
// -- make sure search is closed
this.searchClose();
// search placeholder
var el = $('#grid_'+ obj.name +'_search_all');
if (!this.multiSearch && this.last.field == 'all' && this.searches.length > 0) {
this.last.field = this.searches[0].field;
this.last.label = this.searches[0].label;
}
for (var s = 0; s < this.searches.length; s++) {
if (this.searches[s].field == this.last.field) this.last.label = this.searches[s].label;
}
if (this.last.multi) {
el.attr('placeholder', '[' + w2utils.lang('Multiple Fields') + ']');
el.w2field('clear');
} else {
el.attr('placeholder', w2utils.lang(this.last.label));
}
if (el.val() != this.last.search) {
var val = this.last.search;
var tmp = el.data('w2field');
if (tmp) val = tmp.format(val);
el.val(val);
}
// -- body
obj.refreshBody();
// -- footer
if (this.show.footer) {
$('#grid_'+ this.name +'_footer').html(this.getFooterHTML()).show();
} else {
$('#grid_'+ this.name +'_footer').hide();
}
// show/hide clear search link
var $clear = $('#grid_'+ this.name +'_searchClear');
$clear.hide();
this.searchData.some(function (item) {
var tmp = obj.getSearch(item.field);
if (obj.last.multi || (tmp && !tmp.hidden && ['list', 'enum'].indexOf(tmp.type) == -1)) {
$clear.show();
return true;
}
});
// all selected?
var sel = this.last.selection,
areAllSelected = (this.records.length > 0 && sel.indexes.length == this.records.length),
areAllSearchedSelected = (sel.indexes.length > 0 && this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length);
if (areAllSelected || areAllSearchedSelected) {
$('#grid_'+ this.name +'_check_all').prop('checked', true);
} else {
$('#grid_'+ this.name +'_check_all').prop('checked', false);
}
// show number of selected
this.status();
// collapse all records
var rows = obj.find({ 'w2ui.expanded': true }, true);
for (var r = 0; r < rows.length; r++) {
var tmp = obj.records[rows[r]].w2ui;
if (tmp && !Array.isArray(tmp.children)) {
tmp.expanded = false;
}
}
// mark selection
if (obj.markSearch) {
setTimeout(function () {
// mark all search strings
var search = [];
for (var s = 0; s < obj.searchData.length; s++) {
var sdata = obj.searchData[s];
var fld = obj.getSearch(sdata.field);
if (!fld || fld.hidden) continue;
var ind = obj.getColumn(sdata.field, true);
search.push({ field: sdata.field, search: sdata.value, col: ind });
}
if (search.length > 0) {
search.forEach(function (item) {
$(obj.box).find('td[col="'+ item.col +'"]').not('.w2ui-head').w2marker(item.search);
});
}
}, 50);
}
// enable/disable toolbar search button
if (this.show.toolbarSave) {
if (this.getChanges().length > 0) this.toolbar.enable('w2ui-save'); else this.toolbar.disable('w2ui-save');
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
obj.resize();
obj.addRange('selection');
setTimeout(function () { // allow to render first
obj.resize(); // needed for horizontal scroll to show (do not remove)
obj.scroll();
}, 1);
if ( obj.reorderColumns && !obj.last.columnDrag ) {
obj.last.columnDrag = obj.initColumnDrag();
} else if ( !obj.reorderColumns && obj.last.columnDrag ) {
obj.last.columnDrag.remove();
}
return (new Date()).getTime() - time;
},
refreshBody: function () {
// -- separate summary
var obj = this,
tmp = this.find({ 'w2ui.summary': true }, true);
if (tmp.length > 0) {
for (var t = 0; t < tmp.length; t++) this.summary.push(this.records[tmp[t]]);
for (var t = tmp.length-1; t >= 0; t--) this.records.splice(tmp[t], 1);
}
// -- body
this.scroll(); // need to calculate virtual scolling for columns
var recHTML = this.getRecordsHTML();
var colHTML = this.getColumnsHTML();
var bodyHTML =
'<div id="grid_'+ this.name +'_frecords" class="w2ui-grid-frecords" style="margin-bottom: '+ (w2utils.scrollBarSize() - 1) +'px;">'+
recHTML[0] +
'</div>'+
'<div id="grid_'+ this.name +'_records" class="w2ui-grid-records" onscroll="w2ui[\''+ this.name +'\'].scroll(event);">' +
recHTML[1] +
'</div>'+
'<div id="grid_'+ this.name +'_scroll1" class="w2ui-grid-scroll1" style="height: '+ w2utils.scrollBarSize() +'px"></div>'+
// Columns need to be after to be able to overlap
'<div id="grid_'+ this.name +'_fcolumns" class="w2ui-grid-fcolumns">'+
' <table><tbody>'+ colHTML[0] +'</tbody></table>'+
'</div>'+
'<div id="grid_'+ this.name +'_columns" class="w2ui-grid-columns">'+
' <table><tbody>'+ colHTML[1] +'</tbody></table>'+
'</div>';
var gridBody = $('#grid_'+ this.name +'_body', obj.box).html(bodyHTML),
records = $('#grid_'+ this.name +'_records', obj.box);
var frecords = $('#grid_'+ this.name +'_frecords', obj.box);
var self = this;
if (this.selectType == 'row') {
records
.on('mouseover mouseout', 'tr', function(event) {
$('#grid_'+ self.name +'_frec_' + w2utils.escapeId($(this).attr('recid'))).toggleClass('w2ui-record-hover', event.type == 'mouseover')
})
frecords
.on('mouseover mouseout', 'tr', function(event) {
$('#grid_'+ self.name +'_rec_' + w2utils.escapeId($(this).attr('recid'))).toggleClass('w2ui-record-hover', event.type == 'mouseover')
})
}
if(w2utils.isIOS)
records.add(frecords)
.on('click', 'tr', function(ev) {
self.dblClick($(this).attr('recid'), ev);
})
else
records.add(frecords)
.on('click', 'tr', function(ev) {
self.click($(this).attr('recid'), ev);
})
.on('contextmenu', 'tr', function(ev) {
self.contextMenu($(this).attr('recid'), null, ev);
})
// enable scrolling on frozen records,
gridBody.data('scrolldata', { lastTime: 0, lastDelta: 0, time: 0 })
.find('.w2ui-grid-frecords')
.on("mousewheel DOMMouseScroll ", function(event) {
event.preventDefault();
var e = event.originalEvent,
scrolldata = gridBody.data('scrolldata'),
recordsContainer = $(this).siblings('.w2ui-grid-records').addBack().filter('.w2ui-grid-records'),
amount = typeof e.wheelDelta != null ? e.wheelDelta * -1 / 120 : (e.detail || e.deltaY) / 3, // normalizing scroll speed
newScrollTop = recordsContainer.scrollTop();
scrolldata.time = +new Date();
if (scrolldata.lastTime < scrolldata.time - 150) {
scrolldata.lastDelta = 0;
}
scrolldata.lastTime = scrolldata.time;
scrolldata.lastDelta += amount;
if (Math.abs(scrolldata.lastDelta) < 1) {
amount = 0;
} else {
amount = Math.round(scrolldata.lastDelta);
}
gridBody.data('scrolldata', scrolldata);
// make scroll amount dependent on visible rows
amount *= (Math.round(records.height() / obj.recordHeight) - 1) * obj.recordHeight / 4;
recordsContainer.stop().animate({ 'scrollTop': newScrollTop + amount }, 250, 'linear');
});
if (this.records.length === 0 && this.msgEmpty) {
$('#grid_'+ this.name +'_body')
.append('<div id="grid_'+ this.name + '_empty_msg" class="w2ui-grid-empty-msg"><div>'+ this.msgEmpty +'</div></div>');
} else if ($('#grid_'+ this.name +'_empty_msg').length > 0) {
$('#grid_'+ this.name +'_empty_msg').remove();
}
// show summary records
if (this.summary.length > 0) {
var sumHTML = this.getSummaryHTML();
$('#grid_'+ this.name +'_fsummary').html(sumHTML[0]).show();
$('#grid_'+ this.name +'_summary').html(sumHTML[1]).show();
} else {
$('#grid_'+ this.name +'_fsummary').hide();
$('#grid_'+ this.name +'_summary').hide();
}
},
render: function (box) {
var obj = this;
var time = (new Date()).getTime();
if (box != null) {
if ($(this.box).find('#grid_'+ this.name +'_body').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-grid w2ui-inactive')
.html('');
}
this.box = box;
}
if (!this.box) return;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'render', box: box });
if (edata.isCancelled === true) return;
// reset needed if grid existed
this.reset(true);
// --- default search field
if (!this.last.field) {
if (!this.multiSearch || !this.show.searchAll) {
var tmp = 0;
while (tmp < this.searches.length && (this.searches[tmp].hidden || this.searches[tmp].simple === false)) tmp++;
if (tmp >= this.searches.length) {
// all searches are hidden
this.last.field = '';
this.last.label = '';
} else {
this.last.field = this.searches[tmp].field;
this.last.label = this.searches[tmp].label;
}
} else {
this.last.field = 'all';
this.last.label = w2utils.lang('All Fields');
}
}
// insert elements
$(this.box)
.attr('name', this.name)
.addClass('w2ui-reset w2ui-grid w2ui-inactive')
.html('<div class="w2ui-grid-box">'+
' <div id="grid_'+ this.name +'_header" class="w2ui-grid-header"></div>'+
' <div id="grid_'+ this.name +'_toolbar" class="w2ui-grid-toolbar"></div>'+
' <div id="grid_'+ this.name +'_body" class="w2ui-grid-body"></div>'+
' <div id="grid_'+ this.name +'_fsummary" class="w2ui-grid-body w2ui-grid-summary"></div>'+
' <div id="grid_'+ this.name +'_summary" class="w2ui-grid-body w2ui-grid-summary"></div>'+
' <div id="grid_'+ this.name +'_footer" class="w2ui-grid-footer"></div>'+
' <textarea id="grid_'+ this.name +'_focus" class="w2ui-grid-focus-input" '+
(w2utils.isIOS ? 'readonly' : '') +'></textarea>'+ // readonly needed on android not to open keyboard
'</div>');
if (this.selectType != 'row') $(this.box).addClass('w2ui-ss');
if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
// init toolbar
this.initToolbar();
if (this.toolbar != null) this.toolbar.render($('#grid_'+ this.name +'_toolbar')[0]);
// reinit search_all
if (this.last.field && this.last.field != 'all') {
var sd = this.searchData;
setTimeout(function () { obj.initAllField(obj.last.field, (sd.length == 1 ? sd[0].value : null)); }, 1);
}
// init footer
$('#grid_'+ this.name +'_footer').html(this.getFooterHTML());
// refresh
if (!this.last.state) this.last.state = this.stateSave(true); // initial default state
this.stateRestore();
if (url) { this.clear(); this.refresh(); } // show empty grid (need it) - should it be only for remote data source
// if hidden searches - apply it
var hasHiddenSearches = false;
for (var i = 0; i < this.searches.length; i++) {
if (this.searches[i].hidden) { hasHiddenSearches = true; break; }
}
if (hasHiddenSearches) {
this.searchReset(false); // will call reload
if (!url) setTimeout(function () { obj.searchReset(); }, 1);
} else {
this.reload();
}
// focus
$(this.box).find('#grid_'+ this.name + '_focus')
.on('focus', function (event) {
clearTimeout(obj.last.kbd_timer);
if (!obj.hasFocus) obj.focus();
})
.on('blur', function (event) {
clearTimeout(obj.last.kbd_timer);
obj.last.kbd_timer = setTimeout(function () {
if (obj.hasFocus) { obj.blur(); }
}, 100); // need this timer to be 100 ms
})
.on('paste', function (event) {
var cd = (event.originalEvent.clipboardData ? event.originalEvent.clipboardData : null)
if (cd && cd.types && cd.types.indexOf('text/plain') != -1) {
event.preventDefault()
var text = cd.getData('text/plain');
if (text.indexOf('\r') != -1 && text.indexOf('\n') == -1) {
text = text.replace(/\r/g, '\n');
}
w2ui[obj.name].paste(text);
} else {
// for older browsers
var el = this;
setTimeout(function () { w2ui[obj.name].paste(el.value); el.value = ''; }, 1)
}
})
.on('keydown', function (event) {
w2ui[obj.name].keydown.call(w2ui[obj.name], event);
});
// init mouse events for mouse selection
var edataCol; // event for column select
$(this.box).off('mousedown').on('mousedown', mouseStart);
this.updateToolbar()
// event after
this.trigger($.extend(edata, { phase: 'after' }));
// attach to resize event
if ($('.w2ui-layout').length === 0) { // if there is layout, it will send a resize event
$(window)
.off('resize.w2ui-'+ obj.name)
.on('resize.w2ui-'+ obj.name, function (event) {
if (w2ui[obj.name] == null) {
$(window).off('resize.w2ui-'+ obj.name)
} else {
w2ui[obj.name].resize();
}
});
}
return (new Date()).getTime() - time;
function mouseStart (event) {
if (event.which != 1) return; // if not left mouse button
// restore css user-select
if (obj.last.userSelect == 'text') {
delete obj.last.userSelect;
$(obj.box).find('.w2ui-grid-body').css(w2utils.cssPrefix('user-select', 'none'));
}
// regular record select
if (obj.selectType == 'row' && ($(event.target).parents().hasClass('w2ui-head') || $(event.target).hasClass('w2ui-head'))) return;
if (obj.last.move && obj.last.move.type == 'expand') return;
// if altKey - alow text selection
if (event.altKey) {
$(obj.box).find('.w2ui-grid-body').css(w2utils.cssPrefix('user-select', 'text'));
obj.selectNone();
obj.last.move = { type: 'text-select' };
obj.last.userSelect = 'text';
} else {
var tmp = event.target;
var pos = {
x: event.offsetX - 10,
y: event.offsetY - 10
}
var tmps = false;
while (tmp) {
if (tmp.classList && tmp.classList.contains('w2ui-grid')) break;
if (tmp.tagName && tmp.tagName.toUpperCase() == 'TD') tmps = true;
if (tmp.tagName && tmp.tagName.toUpperCase() != 'TR' && tmps == true) {
pos.x += tmp.offsetLeft;
pos.y += tmp.offsetTop;
}
tmp = tmp.parentNode;
}
obj.last.move = {
x : event.screenX,
y : event.screenY,
divX : 0,
divY : 0,
focusX : pos.x,
focusY : pos.y,
recid : $(event.target).parents('tr').attr('recid'),
column : parseInt(event.target.tagName.toUpperCase() == 'TD' ? $(event.target).attr('col') : $(event.target).parents('td').attr('col')),
type : 'select',
ghost : false,
start : true
};
if (obj.last.move.recid == null) obj.last.move.type = 'select-column';
// set focus to grid
var target = event.target;
var $input = $(obj.box).find('#grid_'+ obj.name + '_focus');
// move input next to cursor so screen does not jump
if (obj.last.move) {
var sLeft = obj.last.move.focusX;
var sTop = obj.last.move.focusY;
var $owner = $(target).parents('table').parent();
if ($owner.hasClass('w2ui-grid-records') || $owner.hasClass('w2ui-grid-frecords')
|| $owner.hasClass('w2ui-grid-columns') || $owner.hasClass('w2ui-grid-fcolumns')
|| $owner.hasClass('w2ui-grid-summary')) {
sLeft = obj.last.move.focusX - $(obj.box).find('#grid_'+ obj.name +'_records').scrollLeft();
sTop = obj.last.move.focusY - $(obj.box).find('#grid_'+ obj.name +'_records').scrollTop();
}
if ($(target).hasClass('w2ui-grid-footer') || $(target).parents('div.w2ui-grid-footer').length > 0) {
sTop = $(obj.box).find('#grid_'+ obj.name +'_footer').position().top;
}
// if clicked on toolbar
if ($owner.hasClass('w2ui-scroll-wrapper') && $owner.parent().hasClass('w2ui-toolbar')) {
sLeft = obj.last.move.focusX - $owner.scrollLeft();
}
$input.css({
left: sLeft - 10,
top : sTop
});
}
// if toolbar input is clicked
setTimeout(function () {
if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(target.tagName.toUpperCase()) != -1) {
$(target).focus();
} else {
if (!$input.is(':focus')) $input.focus();
}
}, 50);
// disable click select for this condition
if (!obj.multiSelect && !obj.reorderRows && obj.last.move.type == 'drag') {
delete obj.last.move;
}
}
if (obj.reorderRows == true) {
var el = event.target;
if (el.tagName.toUpperCase() != 'TD') el = $(el).parents('td')[0];
if ($(el).hasClass('w2ui-col-number') || $(el).hasClass('w2ui-col-order')) {
obj.selectNone();
obj.last.move.reorder = true;
// supress hover
var eColor = $(obj.box).find('.w2ui-even.w2ui-empty-record').css('background-color');
var oColor = $(obj.box).find('.w2ui-odd.w2ui-empty-record').css('background-color');
$(obj.box).find('.w2ui-even td').not('.w2ui-col-number').css('background-color', eColor);
$(obj.box).find('.w2ui-odd td').not('.w2ui-col-number').css('background-color', oColor);
// display empty record and ghost record
var mv = obj.last.move;
if (!mv.ghost) {
var row = $('#grid_'+ obj.name + '_rec_'+ mv.recid);
var tmp = row.parents('table').find('tr:first-child').clone();
mv.offsetY = event.offsetY;
mv.from = mv.recid;
mv.pos = row.position();
mv.ghost = $(row).clone(true);
mv.ghost.removeAttr('id');
row.find('td').remove();
row.append('<td colspan="1000"><div style="height: '+ obj.recordHeight +'px; background-color: #eee; border-bottom: 1px dashed #aaa; border-top: 1px dashed #aaa;"></div></td>');
var recs = $(obj.box).find('.w2ui-grid-records');
recs.append('<div id="grid_'+ obj.name + '_ghost_line" style="position: absolute; z-index: 999999; pointer-events: none; width: 100%;"></div>');
recs.append('<table id="grid_'+ obj.name + '_ghost" style="position: absolute; z-index: 999998; opacity: 0.9; pointer-events: none;"></table>');
$('#grid_'+ obj.name + '_ghost').append(tmp).append(mv.ghost);
}
var ghost = $('#grid_'+ obj.name + '_ghost');
var recs = $(obj.box).find('.w2ui-grid-records');
ghost.css({
top : mv.pos.top + recs.scrollTop(),
left : mv.pos.left,
"border-top" : '1px solid #aaa',
"border-bottom" : '1px solid #aaa'
});
} else {
obj.last.move.reorder = false;
}
}
$(document)
.on('mousemove.w2ui-' + obj.name, mouseMove)
.on('mouseup.w2ui-' + obj.name, mouseStop);
// needed when grid grids are nested, see issue #1275
event.stopPropagation();
}
function mouseMove (event) {
var mv = obj.last.move;
if (!mv || ['select', 'select-column'].indexOf(mv.type) == -1) return;
mv.divX = (event.screenX - mv.x);
mv.divY = (event.screenY - mv.y);
if (Math.abs(mv.divX) <= 1 && Math.abs(mv.divY) <= 1) return; // only if moved more then 1px
obj.last.cancelClick = true;
if (obj.reorderRows == true && obj.last.move.reorder) {
var recs = $(obj.box).find('.w2ui-grid-records');
var tmp = $(event.target).parents('tr');
var recid = tmp.attr('recid');
if (recid == '-none-') recid = 'bottom';
if (recid != mv.from) {
var row1 = $('#grid_'+ obj.name + '_rec_'+ mv.recid);
var row2 = $('#grid_'+ obj.name + '_rec_'+ recid);
$(obj.box).find('.insert-before');
row2.addClass('insert-before');
// MOVABLE GHOST
// if (event.screenY - mv.lastY < 0) row1.after(row2); else row2.after(row1);
mv.lastY = event.screenY;
mv.to = recid;
// line to insert before
var pos = row2.position()
var ghost_line = $('#grid_'+ obj.name + '_ghost_line');
if (pos) {
ghost_line.css({
top : pos.top + recs.scrollTop(),
left : mv.pos.left,
'border-top': '2px solid #769EFC'
});
} else {
ghost_line.css({
'border-top': '2px solid transparent'
});
}
}
var ghost = $('#grid_'+ obj.name + '_ghost');
ghost.css({
top : mv.pos.top + mv.divY + recs.scrollTop(),
left : mv.pos.left
});
return;
}
if (mv.start && mv.recid) {
obj.selectNone();
mv.start = false;
}
var newSel= [];
var recid = (event.target.tagName.toUpperCase() == 'TR' ? $(event.target).attr('recid') : $(event.target).parents('tr').attr('recid'));
if (recid == null) {
// select by dragging columns
if (obj.selectType == 'row') return;
if (obj.last.move && obj.last.move.type == 'select') return;
var col = parseInt($(event.target).parents('td').attr('col'));
if (isNaN(col)) {
obj.removeRange('column-selection');
$(obj.box).find('.w2ui-grid-columns .w2ui-col-header, .w2ui-grid-fcolumns .w2ui-col-header').removeClass('w2ui-col-selected');
$(obj.box).find('.w2ui-col-number').removeClass('w2ui-row-selected');
delete mv.colRange;
} else {
// add all columns in between
var newRange = col + '-' + col;
if (mv.column < col) newRange = mv.column + '-' + col;
if (mv.column > col) newRange = col + '-' + mv.column;
// array of selected columns
var cols = [];
var tmp = newRange.split('-');
for (var ii = parseInt(tmp[0]); ii <= parseInt(tmp[1]); ii++) {
cols.push(ii)
}
if (mv.colRange != newRange) {
edataCol = obj.trigger({ phase: 'before', type: 'columnSelect', target: obj.name, columns: cols, isCancelled: false }); // initial isCancelled
if (edataCol.isCancelled !== true) {
if (mv.colRange == null) obj.selectNone();
// highlight columns
var tmp = newRange.split('-');
$(obj.box).find('.w2ui-grid-columns .w2ui-col-header, .w2ui-grid-fcolumns .w2ui-col-header').removeClass('w2ui-col-selected');
for (var j = parseInt(tmp[0]); j <= parseInt(tmp[1]); j++) {
$(obj.box).find('#grid_'+ obj.name +'_column_' + j + ' .w2ui-col-header').addClass('w2ui-col-selected');
}
$(obj.box).find('.w2ui-col-number').not('.w2ui-head').addClass('w2ui-row-selected');
// show new range
mv.colRange = newRange;
obj.removeRange('column-selection');
obj.addRange({
name : 'column-selection',
range : [{ recid: obj.records[0].recid, column: tmp[0] }, { recid: obj.records[obj.records.length-1].recid, column: tmp[1] }],
style : 'background-color: rgba(90, 145, 234, 0.1)'
});
}
}
}
} else { // regular selection
var ind1 = obj.get(mv.recid, true);
// this happens when selection is started on summary row
if (ind1 == null || (obj.records[ind1] && obj.records[ind1].recid != mv.recid)) return;
var ind2 = obj.get(recid, true);
// this happens when selection is extended into summary row (a good place to implement scrolling)
if (ind2 == null) return;
var col1 = parseInt(mv.column);
var col2 = parseInt(event.target.tagName.toUpperCase() == 'TD' ? $(event.target).attr('col') : $(event.target).parents('td').attr('col'));
if (isNaN(col1) && isNaN(col2)) { // line number select entire record
col1 = 0;
col2 = obj.columns.length-1;
}
if (ind1 > ind2) { var tmp = ind1; ind1 = ind2; ind2 = tmp; }
// check if need to refresh
var tmp = 'ind1:'+ ind1 +',ind2;'+ ind2 +',col1:'+ col1 +',col2:'+ col2;
if (mv.range == tmp) return;
mv.range = tmp;
for (var i = ind1; i <= ind2; i++) {
if (obj.last.searchIds.length > 0 && obj.last.searchIds.indexOf(i) == -1) continue;
if (obj.selectType != 'row') {
if (col1 > col2) { var tmp = col1; col1 = col2; col2 = tmp; }
var tmp = [];
for (var c = col1; c <= col2; c++) {
if (obj.columns[c].hidden) continue;
newSel.push({ recid: obj.records[i].recid, column: parseInt(c) });
}
} else {
newSel.push(obj.records[i].recid);
}
}
if (obj.selectType != 'row') {
var sel = obj.getSelection();
// add more items
var tmp = [];
for (var ns = 0; ns < newSel.length; ns++) {
var flag = false;
for (var s = 0; s < sel.length; s++) if (newSel[ns].recid == sel[s].recid && newSel[ns].column == sel[s].column) flag = true;
if (!flag) tmp.push({ recid: newSel[ns].recid, column: newSel[ns].column });
}
obj.select(tmp);
// remove items
var tmp = [];
for (var s = 0; s < sel.length; s++) {
var flag = false;
for (var ns = 0; ns < newSel.length; ns++) if (newSel[ns].recid == sel[s].recid && newSel[ns].column == sel[s].column) flag = true;
if (!flag) tmp.push({ recid: sel[s].recid, column: sel[s].column });
}
obj.unselect(tmp);
} else {
if (obj.multiSelect) {
var sel = obj.getSelection();
for (var ns = 0; ns < newSel.length; ns++) {
if (sel.indexOf(newSel[ns]) == -1) obj.select(newSel[ns]); // add more items
}
for (var s = 0; s < sel.length; s++) {
if (newSel.indexOf(sel[s]) == -1) obj.unselect(sel[s]); // remove items
}
}
}
}
}
function mouseStop (event) {
var mv = obj.last.move;
setTimeout(function () { delete obj.last.cancelClick; }, 1);
if ($(event.target).parents().hasClass('.w2ui-head') || $(event.target).hasClass('.w2ui-head')) return;
if (mv && ['select', 'select-column'].indexOf(mv.type) != -1) {
if (mv.colRange != null && edataCol.isCancelled !== true) {
var tmp = mv.colRange.split('-');
var sel = [];
for (var i = 0; i < obj.records.length; i++) {
var cols = []
for (var j = parseInt(tmp[0]); j <= parseInt(tmp[1]); j++) cols.push(j);
sel.push({ recid: obj.records[i].recid, column: cols });
}
obj.removeRange('column-selection');
obj.trigger($.extend(edataCol, { phase: 'after' }));
obj.select(sel);
}
if (obj.reorderRows == true && obj.last.move.reorder) {
// event
var edata = obj.trigger({ phase: 'before', target: obj.name, type: 'reorderRow', recid: mv.from, moveAfter: mv.to });
if (edata.isCancelled === true) {
$('#grid_'+ obj.name + '_ghost').remove();
$('#grid_'+ obj.name + '_ghost_line').remove();
obj.refresh();
delete obj.last.move;
return;
}
// default behavior
var ind1 = obj.get(mv.from, true);
var ind2 = obj.get(mv.to, true);
if (mv.to == 'bottom') ind2 = obj.records.length; // end of list
var tmp = obj.records[ind1];
// swap records
if (ind1 != null && ind2 != null) {
obj.records.splice(ind1, 1);
if (ind1 > ind2) {
obj.records.splice(ind2, 0, tmp);
} else {
obj.records.splice(ind2 - 1, 0, tmp);
}
}
$('#grid_'+ obj.name + '_ghost').remove();
$('#grid_'+ obj.name + '_ghost_line').remove();
obj.refresh();
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}
}
delete obj.last.move;
$(document).off('.w2ui-' + obj.name);
}
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'destroy' });
if (edata.isCancelled === true) return;
// remove all events
$(this.box).off();
// clean up
if (typeof this.toolbar == 'object' && this.toolbar.destroy) this.toolbar.destroy();
if ($(this.box).find('#grid_'+ this.name +'_body').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-grid w2ui-inactive')
.html('');
}
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
// ===========================================
// --- Internal Functions
initColumnOnOff: function () {
if (!this.show.toolbarColumns) return;
var obj = this;
// line number
var col_html = '<div class="w2ui-col-on-off">'+
'<table><tbody>'+
'<tr id="grid_'+ this.name +'_column_ln_check" onclick="w2ui[\''+ obj.name +'\'].columnOnOff(event, \'line-numbers\'); event.stopPropagation();">'+
' <td style="width: 30px; text-align: center; padding-right: 3px; color: #888;">'+
' <span class="w2ui-column-check w2ui-icon-'+ (!obj.show.lineNumbers ? 'empty' : 'check') +'"></span>'+
' </td>'+
' <td>'+
' <label>'+ w2utils.lang('Line #') +'</label>'+
' </td>'+
'</tr>';
// columns
for (var c = 0; c < this.columns.length; c++) {
var col = this.columns[c];
var tmp = this.columns[c].text;
if (col.hideable === false) continue;
if (!tmp && this.columns[c].tooltip) tmp = this.columns[c].tooltip;
if (!tmp) tmp = '- column '+ (parseInt(c) + 1) +' -';
col_html +=
'<tr id="grid_'+ this.name +'_column_'+ c +'_check" '+
' onclick="w2ui[\''+ obj.name +'\'].columnOnOff(event, \''+ col.field +'\'); event.stopPropagation();">'+
' <td style="width: 30px; text-align: center; padding-right: 3px; color: #888;">'+
' <span class="w2ui-column-check w2ui-icon-'+ (col.hidden ? 'empty' : 'check') +'"></span>'+
' </td>'+
' <td>'+
' <label>'+ w2utils.stripTags(tmp) +'</label>'+
' </td>'+
'</tr>';
}
var url = (typeof this.url != 'object' ? this.url : this.url.get);
// devider
if ((url && obj.show.skipRecords) || obj.show.saveRestoreState) {
col_html += '<tr style="pointer-events: none"><td colspan="2"><div style="border-top: 1px solid #ddd;"></div></td></tr>';
}
// skip records
if (url && obj.show.skipRecords) {
col_html +=
'<tr><td colspan="2" style="padding: 0px">'+
' <div style="cursor: pointer; padding: 2px 8px; cursor: default">'+ w2utils.lang('Skip') +
' <input type="text" style="width: 60px" value="'+ this.offset +'" '+
' onkeydown="if ([48,49,50,51,52,53,54,55,56,57,58,13,8,46,37,39].indexOf(event.keyCode) == -1) { event.preventDefault() }"'+
' onkeypress="if (event.keyCode == 13) { '+
' w2ui[\''+ obj.name +'\'].skip(this.value); '+
' jQuery(\'.w2ui-overlay\')[0].hide(); '+
' }"/> '+ w2utils.lang('Records')+
' </div>'+
'</td></tr>';
}
// save/restore state
if (obj.show.saveRestoreState) {
col_html += '<tr><td colspan="2" onclick="var obj = w2ui[\''+ obj.name +'\']; obj.toolbar.uncheck(\'w2ui-column-on-off\'); obj.stateSave();">'+
' <div style="cursor: pointer; padding: 4px 8px; cursor: default">'+ w2utils.lang('Save Grid State') + '</div>'+
'</td></tr>'+
'<tr><td colspan="2" onclick="var obj = w2ui[\''+ obj.name +'\']; obj.toolbar.uncheck(\'w2ui-column-on-off\'); obj.stateReset();">'+
' <div style="cursor: pointer; padding: 4px 8px; cursor: default">'+ w2utils.lang('Restore Default State') + '</div>'+
'</td></tr>';
}
col_html += "</tbody></table></div>";
this.toolbar.get('w2ui-column-on-off').html = col_html;
},
/**
*
* @param box, grid object
* @returns {{remove: Function}} contains a closure around all events to ensure they are removed from the dom
*/
initColumnDrag: function ( box ) {
//throw error if using column groups
if ( this.columnGroups && this.columnGroups.length ) throw 'Draggable columns are not currently supported with column groups.';
var obj = this,
_dragData = {};
_dragData.lastInt = null;
_dragData.pressed = false;
_dragData.timeout = null;_dragData.columnHead = null;
//attach original event listener
$(obj.box).on('mousedown', dragColStart);
$(obj.box).on('mouseup', catchMouseup);
function catchMouseup(){
_dragData.pressed = false;
clearTimeout( _dragData.timeout );
}
/**
*
* @param event, mousedown
* @returns {boolean} false, preventsDefault
*/
function dragColStart ( event ) {
if ( _dragData.timeout ) clearTimeout( _dragData.timeout );
var self = this;
_dragData.pressed = true;
_dragData.timeout = setTimeout(function(){
// When dragging a column for reordering, a quick release and a secondary
// click may result in a bug where the column is ghosted to the screen,
// but can no longer be docked back into the header. It simply floats and you
// can no longer interact with it.
// The erronius event thats fired will have _dragData.numberPreColumnsPresent === 0
// populated, wheras a valid event will not.
// if we see the erronius event, dont allow that second click to register, which results
// in the floating column remaining under the mouse's control.
if ( !_dragData.pressed || _dragData.numberPreColumnsPresent === 0 ) return;
var edata,
columns,
selectedCol,
origColumn,
origColumnNumber,
invalidPreColumns = [ 'w2ui-col-number', 'w2ui-col-expand', 'w2ui-col-select' ],
invalidPostColumns = [ 'w2ui-head-last' ],
invalidColumns = invalidPreColumns.concat( invalidPostColumns ),
preColumnsSelector = '.w2ui-col-number, .w2ui-col-expand, .w2ui-col-select',
preColHeadersSelector = '.w2ui-head.w2ui-col-number, .w2ui-head.w2ui-col-expand, .w2ui-head.w2ui-col-select';
// do nothing if it is not a header
if ( !$( event.originalEvent.target ).parents().hasClass( 'w2ui-head' ) ) return;
// do nothing if it is an invalid column
for ( var i = 0, l = invalidColumns.length; i < l; i++ ){
if ( $( event.originalEvent.target ).parents().hasClass( invalidColumns[ i ] ) ) return;
}
_dragData.numberPreColumnsPresent = $( obj.box ).find( preColHeadersSelector ).length;
//start event for drag start
_dragData.columnHead = origColumn = $( event.originalEvent.target ).parents( '.w2ui-head' );
_dragData.originalPos = origColumnNumber = parseInt( origColumn.attr( 'col' ), 10);
edata = obj.trigger({ type: 'columnDragStart', phase: 'before', originalEvent: event, origColumnNumber: origColumnNumber, target: origColumn[0] });
if ( edata.isCancelled === true ) return false;
columns = _dragData.columns = $( obj.box ).find( '.w2ui-head:not(.w2ui-head-last)' );
//add events
$( document ).on( 'mouseup', dragColEnd );
$( document ).on( 'mousemove', dragColOver );
//_dragData.columns.css({ overflow: 'visible' }).children( 'div' ).css({ overflow: 'visible' });
//configure and style ghost image
_dragData.ghost = $( self ).clone( true );
//hide other elements on ghost except the grid body
$( _dragData.ghost ).find( '[col]:not([col="' + _dragData.originalPos + '"]), .w2ui-toolbar, .w2ui-grid-header' ).remove();
$( _dragData.ghost ).find( preColumnsSelector ).remove();
$( _dragData.ghost ).find( '.w2ui-grid-body' ).css({ top: 0 });
selectedCol = $( _dragData.ghost ).find( '[col="' + _dragData.originalPos + '"]' );
$( document.body ).append( _dragData.ghost );
$( _dragData.ghost ).css({
width: 0,
height: 0,
margin: 0,
position: 'fixed',
zIndex: 999999,
opacity: 0
}).addClass( '.w2ui-grid-ghost' ).animate({
width: selectedCol.width(),
height: $(obj.box).find('.w2ui-grid-body:first').height(),
left : event.pageX,
top : event.pageY,
opacity: 0.8
}, 0 );
//establish current offsets
_dragData.offsets = [];
for ( var i = 0, l = columns.length; i < l; i++ ) {
_dragData.offsets.push( $( columns[ i ] ).offset().left );
}
//conclude event
obj.trigger( $.extend( edata, { phase: 'after' } ) );
}, 150 );//end timeout wrapper
}
function dragColOver ( event ) {
if ( !_dragData.pressed ) return;
var cursorX = event.originalEvent.pageX,
cursorY = event.originalEvent.pageY,
offsets = _dragData.offsets,
lastWidth = $( '.w2ui-head:not(.w2ui-head-last)' ).width();
_dragData.targetInt = Math.max(_dragData.numberPreColumnsPresent,targetIntersection( cursorX, offsets, lastWidth ));
markIntersection( _dragData.targetInt );
trackGhost( cursorX, cursorY );
}
function dragColEnd ( event ) {
_dragData.pressed = false;
var edata,
target,
selected,
columnConfig,
targetColumn,
ghosts = $( '.w2ui-grid-ghost' );
//start event for drag start
edata = obj.trigger({ type: 'columnDragEnd', phase: 'before', originalEvent: event, target: _dragData.columnHead[0] });
if ( edata.isCancelled === true ) return false;
selected = obj.columns[ _dragData.originalPos ];
columnConfig = obj.columns;
targetColumn = $( _dragData.columns[ Math.min(_dragData.lastInt, _dragData.columns.length - 1) ] );
target = (_dragData.lastInt < _dragData.columns.length) ? parseInt(targetColumn.attr('col')) : columnConfig.length;
if ( target !== _dragData.originalPos + 1 && target !== _dragData.originalPos && targetColumn && targetColumn.length ) {
$( _dragData.ghost ).animate({
top: $( obj.box ).offset().top,
left: targetColumn.offset().left,
width: 0,
height: 0,
opacity: 0.2
}, 300, function(){
$( this ).remove();
ghosts.remove();
});
columnConfig.splice( target, 0, $.extend( {}, selected ) );
columnConfig.splice( columnConfig.indexOf( selected ), 1);
} else {
$( _dragData.ghost ).remove();
ghosts.remove();
}
//_dragData.columns.css({ overflow: '' }).children( 'div' ).css({ overflow: '' });
$( document ).off( 'mouseup', dragColEnd );
$( document ).off( 'mousemove', dragColOver );
if ( _dragData.marker ) _dragData.marker.remove();
_dragData = {};
obj.refresh();
//conclude event
obj.trigger( $.extend( edata, { phase: 'after', targetColumnNumber: target - 1 } ) );
}
function markIntersection( intersection ){
if ( !_dragData.marker && !_dragData.markerLeft ) {
_dragData.marker = $('<div class="col-intersection-marker">' +
'<div class="top-marker"></div>' +
'<div class="bottom-marker"></div>' +
'</div>');
_dragData.markerLeft = $('<div class="col-intersection-marker">' +
'<div class="top-marker"></div>' +
'<div class="bottom-marker"></div>' +
'</div>');
}
if ( !_dragData.lastInt || _dragData.lastInt !== intersection ){
_dragData.lastInt = intersection;
_dragData.marker.remove();
_dragData.markerLeft.remove();
$('.w2ui-head').removeClass('w2ui-col-intersection');
//if the current intersection is greater than the number of columns add the marker to the end of the last column only
if ( intersection >= _dragData.columns.length ) {
$( _dragData.columns[ _dragData.columns.length - 1 ] ).children( 'div:last' ).append( _dragData.marker.addClass( 'right' ).removeClass( 'left' ) );
$( _dragData.columns[ _dragData.columns.length - 1 ] ).addClass('w2ui-col-intersection');
} else if ( intersection <= _dragData.numberPreColumnsPresent ) {
//if the current intersection is on the column numbers place marker on first available column only
$( _dragData.columns[ _dragData.numberPreColumnsPresent ] ).prepend( _dragData.marker.addClass( 'left' ).removeClass( 'right' ) ).css({ position: 'relative' });
$( _dragData.columns[ _dragData.numberPreColumnsPresent ] ).prev().addClass('w2ui-col-intersection');
} else {
//otherwise prepend the marker to the targeted column and append it to the previous column
$( _dragData.columns[intersection] ).children( 'div:last' ).prepend( _dragData.marker.addClass( 'left' ).removeClass( 'right' ) );
$( _dragData.columns[intersection] ).prev().children( 'div:last' ).append( _dragData.markerLeft.addClass( 'right' ).removeClass( 'left' ) ).css({ position: 'relative' });
$( _dragData.columns[intersection - 1] ).addClass('w2ui-col-intersection');
}
}
}
function targetIntersection( cursorX, offsets, lastWidth ){
if ( cursorX <= offsets[0] ) {
return 0;
} else if ( cursorX >= offsets[offsets.length - 1] + lastWidth ) {
return offsets.length;
} else {
for ( var i = 0, l = offsets.length; i < l; i++ ) {
var thisOffset = offsets[ i ];
var nextOffset = offsets[ i + 1 ] || offsets[ i ] + lastWidth;
var midpoint = ( nextOffset - offsets[ i ]) / 2 + offsets[ i ];
if ( cursorX > thisOffset && cursorX <= midpoint ) {
return i;
} else if ( cursorX > midpoint && cursorX <= nextOffset ) {
return i + 1;
}
}
return intersection;
}
}
function trackGhost( cursorX, cursorY ){
$( _dragData.ghost ).css({
left: cursorX - 10,
top: cursorY - 10
});
}
//return an object to remove drag if it has ever been enabled
return {
remove: function(){
$( obj.box ).off( 'mousedown', dragColStart );
$( obj.box ).off( 'mouseup', catchMouseup );
$( obj.box ).find( '.w2ui-head' ).removeAttr( 'draggable' );
obj.last.columnDrag = false;
}
};
},
columnOnOff: function (event, field) {
var $el = $(event.target).parents('tr').find('.w2ui-column-check');
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'columnOnOff', field: field, originalEvent: event });
if (edata.isCancelled === true) return;
// regular processing
var obj = this;
var hide = (!event.shiftKey && !event.metaKey && !event.ctrlKey && !$(event.target).hasClass('w2ui-column-check'));
// collapse expanded rows
var rows = obj.find({ 'w2ui.expanded': true }, true);
for (var r = 0; r < rows.length; r++) {
var tmp = this.records[r].w2ui;
if (tmp && !Array.isArray(tmp.children)) {
this.records[r].w2ui.expanded = false;
}
}
// show/hide
if (field == 'line-numbers') {
this.show.lineNumbers = !this.show.lineNumbers;
if (this.show.lineNumbers) {
$el.addClass('w2ui-icon-check').removeClass('w2ui-icon-empty');
} else {
$el.addClass('w2ui-icon-empty').removeClass('w2ui-icon-check');
}
this.refreshBody();
this.resizeRecords();
} else {
var col = this.getColumn(field);
if (col.hidden) {
$el.addClass('w2ui-icon-check').removeClass('w2ui-icon-empty');
setTimeout(function () {
obj.showColumn(col.field);
}, hide ? 0 : 50);
} else {
$el.addClass('w2ui-icon-empty').removeClass('w2ui-icon-check');
setTimeout(function () {
obj.hideColumn(col.field);
}, hide ? 0 : 50);
}
}
if (hide) {
setTimeout(function () {
$().w2overlay({ name: obj.name + '_toolbar' });
}, 40);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
scrollToColumn: function (field) {
if (field == null)
return;
var sWidth = 0;
var found = false;
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.field == field) {
found = true;
break;
}
if (col.frozen || col.hidden)
continue;
var cSize = parseInt(col.sizeCalculated ? col.sizeCalculated : col.size);
sWidth += cSize;
}
if (!found)
return;
this.last.scrollLeft = sWidth+1;
this.scroll();
},
initToolbar: function () {
var obj = this;
// -- if toolbar is true
if (this.toolbar['render'] == null) {
var tmp_items = this.toolbar.items || [];
this.toolbar.items = [];
this.toolbar = $().w2toolbar($.extend(true, {}, this.toolbar, { name: this.name +'_toolbar', owner: this }));
// =============================================
// ------ Toolbar Generic buttons
if (this.show.toolbarReload) {
this.toolbar.items.push($.extend(true, {}, this.buttons['reload']));
}
if (this.show.toolbarColumns) {
this.toolbar.items.push($.extend(true, {}, this.buttons['columns']));
}
if (this.show.toolbarReload || this.show.toolbarColumns) {
this.toolbar.items.push({ type: 'break', id: 'w2ui-break0' });
}
if (this.show.toolbarInput) {
var html =
'<div class="w2ui-toolbar-search">'+
'<table cellpadding="0" cellspacing="0"><tbody><tr>'+
' <td>'+ this.buttons['search'].html +'</td>'+
' <td>'+
' <input type="text" id="grid_'+ this.name +'_search_all" class="w2ui-search-all" tabindex="-1" '+
' autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"'+
' placeholder="'+ w2utils.lang(this.last.label) +'" value="'+ this.last.search +'"'+
' onfocus="var grid = w2ui[\''+ this.name +'\']; clearTimeout(grid.last.kbd_timer); grid.searchShowFields(true); grid.searchClose()"'+
' onkeydown="if (event.keyCode == 13 &amp;&amp; w2utils.isIE) this.onchange();"'+
' onchange="'+
' var grid = w2ui[\''+ this.name +'\']; '+
' var val = this.value; '+
' var sel = jQuery(this).data(\'selected\');'+
' var fld = jQuery(this).data(\'w2field\'); '+
' if (fld) val = fld.clean(val);'+
' if (fld &amp;&amp; fld.type == \'list\' &amp;&amp; sel &amp;&amp; typeof sel.id == \'undefined\') {'+
' grid.searchReset();'+
' } else {'+
' grid.search(grid.last.field, val);'+
' }'+
' "/>'+
' </td>'+
' <td>'+
' <div class="w2ui-search-clear" id="grid_'+ this.name +'_searchClear" '+
' onclick="var obj = w2ui[\''+ this.name +'\']; obj.searchReset();" style="display: none"'+
' >&#160;&#160;</div>'+
' </td>'+
'</tr></tbody></table>'+
'</div>';
this.toolbar.items.push({ type: 'html', id: 'w2ui-search', html: html });
}
if (this.show.toolbarSearch && this.multiSearch && this.searches.length > 0) {
this.toolbar.items.push($.extend(true, {}, this.buttons['search-go']));
}
if ((this.show.toolbarSearch || this.show.toolbarInput)
&& (this.show.toolbarAdd || this.show.toolbarEdit || this.show.toolbarDelete || this.show.toolbarSave)) {
this.toolbar.items.push({ type: 'break', id: 'w2ui-break1' });
}
if (this.show.toolbarAdd && Array.isArray(tmp_items)
&& tmp_items.map(function (item) { return item.id }).indexOf(this.buttons['add'].id) == -1) {
this.toolbar.items.push($.extend(true, {}, this.buttons['add']));
}
if (this.show.toolbarEdit && Array.isArray(tmp_items)
&& tmp_items.map(function (item) { return item.id }).indexOf(this.buttons['edit'].id) == -1) {
this.toolbar.items.push($.extend(true, {}, this.buttons['edit']));
}
if (this.show.toolbarDelete && Array.isArray(tmp_items)
&& tmp_items.map(function (item) { return item.id }).indexOf(this.buttons['delete'].id) == -1) {
this.toolbar.items.push($.extend(true, {}, this.buttons['delete']));
}
if (this.show.toolbarSave && Array.isArray(tmp_items)
&& tmp_items.map(function (item) { return item.id }).indexOf(this.buttons['save'].id) == -1) {
if (this.show.toolbarAdd || this.show.toolbarDelete || this.show.toolbarEdit) {
this.toolbar.items.push({ type: 'break', id: 'w2ui-break2' });
}
this.toolbar.items.push($.extend(true, {}, this.buttons['save']));
}
// add original buttons
if (tmp_items) for (var i = 0; i < tmp_items.length; i++) this.toolbar.items.push(tmp_items[i]);
// =============================================
// ------ Toolbar onClick processing
var obj = this;
this.toolbar.on('click', function (event) {
var edata = obj.trigger({ phase: 'before', type: 'toolbar', target: event.target, originalEvent: event });
if (edata.isCancelled === true) return;
var id = event.target;
switch (id) {
case 'w2ui-reload':
var edata2 = obj.trigger({ phase: 'before', type: 'reload', target: obj.name });
if (edata2.isCancelled === true) return false;
obj.reload();
obj.trigger($.extend(edata2, { phase: 'after' }));
break;
case 'w2ui-column-on-off':
obj.initColumnOnOff();
obj.initResize();
obj.resize();
break;
case 'w2ui-search-advanced':
var it = this.get(id);
if (it.checked) {
obj.searchClose();
} else {
obj.searchOpen();
}
// need to cancel event in order to user custom searchOpen/close functions
obj.toolbar.tooltipHide('w2ui-search-advanced');
event.preventDefault();
break;
case 'w2ui-add':
// events
var edata2 = obj.trigger({ phase: 'before', target: obj.name, type: 'add', recid: null });
if (edata2.isCancelled === true) return false;
obj.trigger($.extend(edata2, { phase: 'after' }));
// hide all tooltips
setTimeout(function () { $().w2tag(); }, 20);
break;
case 'w2ui-edit':
var sel = obj.getSelection();
var recid = null;
if (sel.length == 1) recid = sel[0];
// events
var edata2 = obj.trigger({ phase: 'before', target: obj.name, type: 'edit', recid: recid });
if (edata2.isCancelled === true) return false;
obj.trigger($.extend(edata2, { phase: 'after' }));
// hide all tooltips
setTimeout(function () { $().w2tag(); }, 20);
break;
case 'w2ui-delete':
obj["delete"]();
break;
case 'w2ui-save':
obj.save();
break;
}
// no default action
obj.trigger($.extend(edata, { phase: 'after' }));
})
this.toolbar.on('refresh', function (event) {
if (event.target == 'w2ui-search') {
var sd = obj.searchData;
setTimeout(function () {
obj.initAllField(obj.last.field, (sd.length == 1 ? sd[0].value : null));
}, 1);
}
})
}
},
initResize: function () {
var obj = this;
//if (obj.resizing === true) return;
$(this.box).find('.w2ui-resizer')
.off('.grid-col-resize')
.on('click.grid-col-resize', function (event) {
if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
if (event.preventDefault) event.preventDefault();
})
.on('mousedown.grid-col-resize', function (event) {
if (!event) event = window.event;
obj.resizing = true;
obj.last.tmp = {
x : event.screenX,
y : event.screenY,
gx : event.screenX,
gy : event.screenY,
col : parseInt($(this).attr('name'))
};
// find tds that will be resized
obj.last.tmp.tds = $('#grid_'+ obj.name +'_body table tr:first-child td[col='+ obj.last.tmp.col +']')
if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
if (event.preventDefault) event.preventDefault();
// fix sizes
for (var c = 0; c < obj.columns.length; c++) {
if (obj.columns[c].hidden) continue;
if (obj.columns[c].sizeOriginal == null) obj.columns[c].sizeOriginal = obj.columns[c].size;
obj.columns[c].size = obj.columns[c].sizeCalculated;
}
var edata = { phase: 'before', type: 'columnResize', target: obj.name, column: obj.last.tmp.col, field: obj.columns[obj.last.tmp.col].field };
edata = obj.trigger($.extend(edata, { resizeBy: 0, originalEvent: event }));
// set move event
var timer;
var mouseMove = function (event) {
if (obj.resizing != true) return;
if (!event) event = window.event;
// event before
edata = obj.trigger($.extend(edata, { resizeBy: (event.screenX - obj.last.tmp.gx), originalEvent: event }));
if (edata.isCancelled === true) { edata.isCancelled = false; return; }
// default action
obj.last.tmp.x = (event.screenX - obj.last.tmp.x);
obj.last.tmp.y = (event.screenY - obj.last.tmp.y);
var newWidth =(parseInt(obj.columns[obj.last.tmp.col].size) + obj.last.tmp.x) + 'px'
obj.columns[obj.last.tmp.col].size = newWidth;
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
obj.resizeRecords()
obj.scroll()
}, 100)
// quick resize
obj.last.tmp.tds.css({ width: newWidth })
// reset
obj.last.tmp.x = event.screenX;
obj.last.tmp.y = event.screenY;
};
var mouseUp = function (event) {
delete obj.resizing;
$(document).off('.grid-col-resize');
obj.resizeRecords();
obj.scroll();
// event after
obj.trigger($.extend(edata, { phase: 'after', originalEvent: event }));
};
$(document)
.off('.grid-col-resize')
.on('mousemove.grid-col-resize', mouseMove)
.on('mouseup.grid-col-resize', mouseUp);
})
.on('dblclick.grid-col-resize', function(event) {
var colId = parseInt($(this).attr('name')),
col = obj.columns[colId],
maxDiff = 0;
if (col.autoResize === false) {
return true;
}
if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
if (event.preventDefault) event.preventDefault();
$('.w2ui-grid-records td[col="' + colId + '"] > div', obj.box).each(function() {
var thisDiff = this.offsetWidth - this.scrollWidth;
if (thisDiff < maxDiff) {
maxDiff = thisDiff - 3; // 3px buffer needed for Firefox
}
});
// event before
var edata = { phase: 'before', type: 'columnAutoResize', target: obj.name, column: col, field: col.field };
edata = obj.trigger($.extend(edata, { resizeBy: Math.abs(maxDiff), originalEvent: event }));
if (edata.isCancelled === true) { edata.isCancelled = false; return; }
if (maxDiff < 0) {
col.size = Math.min(parseInt(col.size) + Math.abs(maxDiff), col.max || Infinity) + 'px';
obj.resizeRecords();
obj.resizeRecords(); // Why do we have to call it twice in order to show the scrollbar?
obj.scroll();
}
// event after
obj.trigger($.extend(edata, { phase: 'after', originalEvent: event }));
})
.each(function (index, el) {
var td = $(el).parent();
$(el).css({
"height" : td.height(),
"margin-left" : (td.width() - 3) + 'px'
});
});
},
resizeBoxes: function () {
// elements
var header = $('#grid_'+ this.name +'_header');
var toolbar = $('#grid_'+ this.name +'_toolbar');
var fsummary = $('#grid_'+ this.name +'_fsummary');
var summary = $('#grid_'+ this.name +'_summary');
var footer = $('#grid_'+ this.name +'_footer');
var body = $('#grid_'+ this.name +'_body');
if (this.show.header) {
header.css({
top: '0px',
left: '0px',
right: '0px'
});
}
if (this.show.toolbar) {
toolbar.css({
top: ( 0 + (this.show.header ? w2utils.getSize(header, 'height') : 0) ) + 'px',
left: '0px',
right: '0px'
});
}
if (this.summary.length > 0) {
fsummary.css({
bottom: ( 0 + (this.show.footer ? w2utils.getSize(footer, 'height') : 0) ) + 'px'
});
summary.css({
bottom: ( 0 + (this.show.footer ? w2utils.getSize(footer, 'height') : 0) ) + 'px',
right: '0px'
});
}
if (this.show.footer) {
footer.css({
bottom: '0px',
left: '0px',
right: '0px'
});
}
body.css({
top: ( 0 + (this.show.header ? w2utils.getSize(header, 'height') : 0) + (this.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0) ) + 'px',
bottom: ( 0 + (this.show.footer ? w2utils.getSize(footer, 'height') : 0) + (this.summary.length > 0 ? w2utils.getSize(summary, 'height') : 0) ) + 'px',
left: '0px',
right: '0px'
});
},
resizeRecords: function () {
var obj = this;
// remove empty records
$(this.box).find('.w2ui-empty-record').remove();
// -- Calculate Column size in PX
var box = $(this.box);
var grid = $(this.box).find('> div.w2ui-grid-box');
var header = $('#grid_'+ this.name +'_header');
var toolbar = $('#grid_'+ this.name +'_toolbar');
var summary = $('#grid_'+ this.name +'_summary');
var fsummary = $('#grid_'+ this.name +'_fsummary');
var footer = $('#grid_'+ this.name +'_footer');
var body = $('#grid_'+ this.name +'_body');
var columns = $('#grid_'+ this.name +'_columns');
var fcolumns = $('#grid_'+ this.name +'_fcolumns');
var records = $('#grid_'+ this.name +'_records');
var frecords = $('#grid_'+ this.name +'_frecords');
var scroll1 = $('#grid_'+ this.name +'_scroll1');
var lineNumberWidth = String(this.total).length * 8 + 10;
if (lineNumberWidth < 34) lineNumberWidth = 34; // 3 digit width
if (this.lineNumberWidth != null) lineNumberWidth = this.lineNumberWidth;
var bodyOverflowX = false;
var bodyOverflowY = false;
var sWidth = 0;
for (var i = 0; i < obj.columns.length; i++) {
if (obj.columns[i].frozen || obj.columns[i].hidden) continue;
var cSize = parseInt(obj.columns[i].sizeCalculated ? obj.columns[i].sizeCalculated : obj.columns[i].size);
sWidth += cSize;
}
if (records.width() < sWidth) bodyOverflowX = true;
if (body.height() - columns.height() < $(records).find('>table').height() + (bodyOverflowX ? w2utils.scrollBarSize() : 0)) bodyOverflowY = true;
// body might be expanded by data
if (!this.fixedBody) {
// allow it to render records, then resize
var calculatedHeight = w2utils.getSize(columns, 'height')
+ w2utils.getSize($('#grid_'+ obj.name +'_records table'), 'height')
+ (bodyOverflowX ? w2utils.scrollBarSize() : 0);
obj.height = calculatedHeight
+ w2utils.getSize(grid, '+height')
+ (obj.show.header ? w2utils.getSize(header, 'height') : 0)
+ (obj.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0)
+ (summary.css('display') != 'none' ? w2utils.getSize(summary, 'height') : 0)
+ (obj.show.footer ? w2utils.getSize(footer, 'height') : 0);
grid.css('height', obj.height);
body.css('height', calculatedHeight);
box.css('height', w2utils.getSize(grid, 'height') + w2utils.getSize(box, '+height'));
} else {
// fixed body height
var calculatedHeight = grid.height()
- (this.show.header ? w2utils.getSize(header, 'height') : 0)
- (this.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0)
- (summary.css('display') != 'none' ? w2utils.getSize(summary, 'height') : 0)
- (this.show.footer ? w2utils.getSize(footer, 'height') : 0);
body.css('height', calculatedHeight);
}
var buffered = this.records.length;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (this.searchData.length != 0 && !url) buffered = this.last.searchIds.length;
// apply overflow
if (!this.fixedBody) { bodyOverflowY = false; }
if (bodyOverflowX || bodyOverflowY) {
columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').css('width', w2utils.scrollBarSize()).show();
records.css({
top: ((this.columnGroups.length > 0 && this.show.columns ? 1 : 0) + w2utils.getSize(columns, 'height')) +'px',
"-webkit-overflow-scrolling": "touch",
"overflow-x": (bodyOverflowX ? 'auto' : 'hidden'),
"overflow-y": (bodyOverflowY ? 'auto' : 'hidden')
});
} else {
columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').hide();
records.css({
top: ((this.columnGroups.length > 0 && this.show.columns ? 1 : 0) + w2utils.getSize(columns, 'height')) +'px',
overflow: 'hidden'
});
if (records.length > 0) { this.last.scrollTop = 0; this.last.scrollLeft = 0; } // if no scrollbars, always show top
}
if (bodyOverflowX) {
frecords.css('margin-bottom', w2utils.scrollBarSize());
scroll1.show();
} else {
frecords.css('margin-bottom', 0);
scroll1.hide();
}
frecords.css({ overflow: 'hidden', top: records.css('top') });
if (this.show.emptyRecords && !bodyOverflowY) {
var max = Math.floor(records.height() / this.recordHeight) - 1;
var leftover = 0;
if (records[0]) leftover = records[0].scrollHeight - max * this.recordHeight;
if (leftover >= this.recordHeight) {
leftover -= this.recordHeight;
max++;
}
if (this.fixedBody) {
for (var di = buffered; di < max; di++) {
addEmptyRow(di, this.recordHeight, this);
}
addEmptyRow(max, leftover, this);
}
}
function addEmptyRow(row, height, grid) {
var html1 = '';
var html2 = '';
var htmlp = '';
html1 += '<tr class="'+ (row % 2 ? 'w2ui-even' : 'w2ui-odd') + ' w2ui-empty-record" recid="-none-" style="height: '+ height +'px">';
html2 += '<tr class="'+ (row % 2 ? 'w2ui-even' : 'w2ui-odd') + ' w2ui-empty-record" recid="-none-" style="height: '+ height +'px">';
if (grid.show.lineNumbers) html1 += '<td class="w2ui-col-number"></td>';
if (grid.show.selectColumn) html1 += '<td class="w2ui-grid-data w2ui-col-select"></td>';
if (grid.show.expandColumn) html1 += '<td class="w2ui-grid-data w2ui-col-expand"></td>';
html2 += '<td class="w2ui-grid-data-spacer" col="start" style="border-right: 0"></td>';
if (grid.show.orderColumn) html2 += '<td class="w2ui-grid-data w2ui-col-order" col="order"></td>';
for (var j = 0; j < grid.columns.length; j++) {
var col = grid.columns[j];
if ((col.hidden || j < grid.last.colStart || j > grid.last.colEnd) && !col.frozen) continue;
htmlp = '<td class="w2ui-grid-data" '+ (col.attr != null ? col.attr : '') +' col="'+ j +'"></td>';
if (col.frozen) html1 += htmlp; else html2 += htmlp;
}
html1 += '<td class="w2ui-grid-data-last"></td> </tr>';
html2 += '<td class="w2ui-grid-data-last" col="end"></td> </tr>';
$('#grid_'+ grid.name +'_frecords > table').append(html1);
$('#grid_'+ grid.name +'_records > table').append(html2);
}
if (body.length > 0) {
var width_max = parseInt(body.width())
- (bodyOverflowY ? w2utils.scrollBarSize() : 0)
- (this.show.lineNumbers ? lineNumberWidth : 0)
// - (this.show.orderColumn ? 26 : 0)
- (this.show.selectColumn ? 26 : 0)
- (this.show.expandColumn ? 26 : 0)
- 1; // left is 1xp due to border width
var width_box = width_max;
var percent = 0;
// gridMinWidth processing
var restart = false;
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.gridMinWidth > 0) {
if (col.gridMinWidth > width_box && col.hidden !== true) {
col.hidden = true;
restart = true;
}
if (col.gridMinWidth < width_box && col.hidden === true) {
col.hidden = false;
restart = true;
}
}
}
if (restart === true) {
this.refresh();
return;
}
// assign PX column s
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.hidden) continue;
if (String(col.size).substr(String(col.size).length-2).toLowerCase() == 'px') {
width_max -= parseFloat(col.size);
this.columns[i].sizeCalculated = col.size;
this.columns[i].sizeType = 'px';
} else {
percent += parseFloat(col.size);
this.columns[i].sizeType = '%';
delete col.sizeCorrected;
}
}
// if sum != 100% -- reassign proportionally
if (percent != 100 && percent > 0) {
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.hidden) continue;
if (col.sizeType == '%') {
col.sizeCorrected = Math.round(parseFloat(col.size) * 100 * 100 / percent) / 100 + '%';
}
}
}
// calculate % columns
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.hidden) continue;
if (col.sizeType == '%') {
if (this.columns[i].sizeCorrected != null) {
// make it 1px smaller, so margin of error can be calculated correctly
this.columns[i].sizeCalculated = Math.floor(width_max * parseFloat(col.sizeCorrected) / 100) - 1 + 'px';
} else {
// make it 1px smaller, so margin of error can be calculated correctly
this.columns[i].sizeCalculated = Math.floor(width_max * parseFloat(col.size) / 100) - 1 + 'px';
}
}
}
}
// fix margin of error that is due percentage calculations
var width_cols = 0;
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
if (col.hidden) continue;
if (col.min == null) col.min = 20;
if (parseInt(col.sizeCalculated) < parseInt(col.min)) col.sizeCalculated = col.min + 'px';
if (parseInt(col.sizeCalculated) > parseInt(col.max)) col.sizeCalculated = col.max + 'px';
width_cols += parseInt(col.sizeCalculated);
}
var width_diff = parseInt(width_box) - parseInt(width_cols);
if (width_diff > 0 && percent > 0) {
var i = 0;
while (true) {
var col = this.columns[i];
if (col == null) { i = 0; continue; }
if (col.hidden || col.sizeType == 'px') { i++; continue; }
col.sizeCalculated = (parseInt(col.sizeCalculated) + 1) + 'px';
width_diff--;
if (width_diff === 0) break;
i++;
}
} else if (width_diff > 0) {
columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').css('width', w2utils.scrollBarSize()).show();
}
// find width of frozen columns
var fwidth = 1;
if (this.show.lineNumbers) fwidth += lineNumberWidth;
if (this.show.selectColumn) fwidth += 26;
// if (this.show.orderColumn) fwidth += 26;
if (this.show.expandColumn) fwidth += 26;
for (var i = 0; i < this.columns.length; i++) {
if (this.columns[i].hidden) continue;
if (this.columns[i].frozen) fwidth += parseInt(this.columns[i].sizeCalculated);
}
fcolumns.css('width', fwidth);
frecords.css('width', fwidth);
fsummary.css('width', fwidth);
scroll1.css('width', fwidth);
columns.css('left', fwidth);
records.css('left', fwidth);
summary.css('left', fwidth);
// resize columns
columns.find('> table > tbody > tr:nth-child(1) td')
.add(fcolumns.find('> table > tbody > tr:nth-child(1) td'))
.each(function (index, el) {
// line numbers
if ($(el).hasClass('w2ui-col-number')) {
$(el).css('width', lineNumberWidth);
}
// records
var ind = $(el).attr('col');
if (ind != null) {
if (ind == 'start') {
var width = 0;
for (var i = 0; i < obj.last.colStart; i++) {
if (!obj.columns[i] || obj.columns[i].frozen || obj.columns[i].hidden) continue;
width += parseInt(obj.columns[i].sizeCalculated);
}
$(el).css('width', width + 'px');
}
if (obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
}
// last column
if ($(el).hasClass('w2ui-head-last')) {
if (obj.last.colEnd + 1 < obj.columns.length) {
var width = 0;
for (var i = obj.last.colEnd + 1; i < obj.columns.length; i++) {
if (!obj.columns[i] || obj.columns[i].frozen || obj.columns[i].hidden) continue;
width += parseInt(obj.columns[i].sizeCalculated);
}
$(el).css('width', width + 'px');
} else {
$(el).css('width', w2utils.scrollBarSize() + (width_diff > 0 && percent === 0 ? width_diff : 0) + 'px');
}
}
});
// if there are column groups - hide first row (needed for sizing)
if (columns.find('> table > tbody > tr').length == 3) {
columns.find('> table > tbody > tr:nth-child(1) td')
.add(fcolumns.find('> table > tbody > tr:nth-child(1) td'))
.html('').css({
'height' : '0px',
'border' : '0px',
'padding': '0px',
'margin' : '0px'
});
}
// resize records
records.find('> table > tbody > tr:nth-child(1) td')
.add(frecords.find('> table > tbody > tr:nth-child(1) td'))
.each(function (index, el) {
// line numbers
if ($(el).hasClass('w2ui-col-number')) {
$(el).css('width', lineNumberWidth);
}
// records
var ind = $(el).attr('col');
if (ind != null) {
if (ind == 'start') {
var width = 0;
for (var i = 0; i < obj.last.colStart; i++) {
if (!obj.columns[i] || obj.columns[i].frozen || obj.columns[i].hidden) continue;
width += parseInt(obj.columns[i].sizeCalculated);
}
$(el).css('width', width + 'px');
}
if (obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
}
// last column
if ($(el).hasClass('w2ui-grid-data-last') && $(el).parents('.w2ui-grid-frecords').length === 0) { // not in frecords
if (obj.last.colEnd + 1 < obj.columns.length) {
var width = 0;
for (var i = obj.last.colEnd + 1; i < obj.columns.length; i++) {
if (!obj.columns[i] || obj.columns[i].frozen || obj.columns[i].hidden) continue;
width += parseInt(obj.columns[i].sizeCalculated);
}
$(el).css('width', width + 'px');
} else {
$(el).css('width', (width_diff > 0 && percent === 0 ? width_diff : 0) + 'px');
}
}
});
// resize summary
summary.find('> table > tbody > tr:nth-child(1) td')
.add(fsummary.find('> table > tbody > tr:nth-child(1) td'))
.each(function (index, el) {
// line numbers
if ($(el).hasClass('w2ui-col-number')) {
$(el).css('width', lineNumberWidth);
}
// records
var ind = $(el).attr('col');
if (ind != null) {
if (ind == 'start') {
var width = 0;
for (var i = 0; i < obj.last.colStart; i++) {
if (!obj.columns[i] || obj.columns[i].frozen || obj.columns[i].hidden) continue;
width += parseInt(obj.columns[i].sizeCalculated);
}
$(el).css('width', width + 'px');
}
if (obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
}
// last column
if ($(el).hasClass('w2ui-grid-data-last') && $(el).parents('.w2ui-grid-frecords').length === 0) { // not in frecords
$(el).css('width', w2utils.scrollBarSize() + (width_diff > 0 && percent === 0 ? width_diff : 0) + 'px');
}
});
this.initResize();
this.refreshRanges();
// apply last scroll if any
if ((this.last.scrollTop || this.last.scrollLeft) && records.length > 0) {
columns.prop('scrollLeft', this.last.scrollLeft);
records.prop('scrollTop', this.last.scrollTop);
records.prop('scrollLeft', this.last.scrollLeft);
}
},
getSearchesHTML: function () {
var obj = this;
var html = '<table cellspacing="0"><tbody>';
var showBtn = false;
for (var i = 0; i < this.searches.length; i++) {
var s = this.searches[i];
s.type = String(s.type).toLowerCase();
if (s.hidden) continue;
var btn = '';
if (showBtn == false) {
btn = '<button type="button" class="w2ui-btn close-btn" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) obj.searchClose()">X</button>';
showBtn = true;
}
if (s.inTag == null) s.inTag = '';
if (s.outTag == null) s.outTag = '';
if (s.style == null) s.style = '';
if (s.type == null) s.type = 'text';
if (s.label == null && s.caption != null) {
console.log('NOTICE: grid search.caption property is deprecated, please use search.label. Search ->', s)
s.label = s.caption;
}
var operator =
'<select id="grid_'+ this.name +'_operator_'+ i +'" class="w2ui-input" ' +
' onchange="w2ui[\''+ this.name + '\'].initOperator(this, '+ i +')">' +
getOperators(s.type, s.operators) +
'</select>';
html += '<tr>'+
' <td class="close-btn">'+ btn +'</td>' +
' <td class="caption">'+ (s.label || '') +'</td>' +
' <td class="operator">'+ operator +'</td>'+
' <td class="value">';
switch (s.type) {
case 'text':
case 'alphanumeric':
case 'hex':
case 'color':
case 'list':
case 'combo':
case 'enum':
var tmpStyle = 'width: 250px;';
if (['hex', 'color'].indexOf(s.type) != -1) tmpStyle = 'width: 90px;';
html += '<input rel="search" type="text" id="grid_'+ this.name +'_field_'+ i +'" name="'+ s.field +'" '+
' class="w2ui-input" style="'+ tmpStyle + s.style +'" '+ s.inTag +'/>';
break;
case 'int':
case 'float':
case 'money':
case 'currency':
case 'percent':
case 'date':
case 'time':
case 'datetime':
var tmpStyle = 'width: 90px;';
if (s.type == 'datetime') tmpStyle = 'width: 140px;';
html += '<input rel="search" type="text" class="w2ui-input" style="'+ tmpStyle + s.style +'" id="grid_'+ this.name +'_field_'+ i +'" name="'+ s.field +'" '+ s.inTag +'/>'+
'<span id="grid_'+ this.name +'_range_'+ i +'" style="display: none">&#160;-&#160;&#160;'+
'<input rel="search" type="text" class="w2ui-input" style="'+ tmpStyle + s.style +'" id="grid_'+ this.name +'_field2_'+ i +'" name="'+ s.field +'" '+ s.inTag +'/>'+
'</span>';
break;
case 'select':
html += '<select rel="search" class="w2ui-input" style="'+ s.style +'" id="grid_'+ this.name +'_field_'+ i +'" '+
' name="'+ s.field +'" '+ s.inTag +'></select>';
break;
}
html += s.outTag +
' </td>' +
'</tr>';
}
html += '<tr>'+
' <td colspan="4" class="actions">'+
' <div>'+
' <button type="button" class="w2ui-btn" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) { obj.searchReset(); }">'+ w2utils.lang('Reset') + '</button>'+
' <button type="button" class="w2ui-btn w2ui-btn-blue" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) { obj.search(); }">'+ w2utils.lang('Search') + '</button>'+
' </div>'+
' </td>'+
'</tr></tbody></table>';
return html;
function getOperators(type, fieldOperators) {
var html = '';
var operators = obj.operators[obj.operatorsMap[type]];
if (fieldOperators != null) operators = fieldOperators;
for (var i = 0; i < operators.length; i++) {
var oper = operators[i];
var text = oper;
if (Array.isArray(oper)) {
text = oper[1];
oper = oper[0];
if (text == null) text = oper;
} else if ($.isPlainObject(oper)) {
text = oper.text;
oper = oper.oper;
}
html += '<option value="'+ oper +'">'+ w2utils.lang(text) +'</option>\n';
}
return html;
}
},
initOperator: function (el, search_ind) {
var obj = this;
var search = obj.searches[search_ind];
var range = $('#grid_'+ obj.name + '_range_'+ search_ind);
var fld1 = $('#grid_'+ obj.name +'_field_'+ search_ind);
var fld2 = fld1.parent().find('span input');
fld1.show();
range.hide();
switch ($(el).val()) {
case 'between':
range.show();
fld2.w2field(search.type, search.options);
break;
case 'not null':
case 'null':
fld1.hide();
fld1.val('1'); // need to insert something for search to activate
fld1.change();
break;
}
},
initSearches: function () {
var obj = this;
// init searches
for (var s = 0; s < this.searches.length; s++) {
var search = this.searches[s];
var sdata = this.getSearchData(search.field);
search.type = String(search.type).toLowerCase();
var operators = obj.operators[obj.operatorsMap[search.type]];
if (search.operators) operators = search.operators;
var operator = operators[0]; // default operator
if ($.isPlainObject(operator)) operator = operator.oper;
if (typeof search.options != 'object') search.options = {};
if (search.type == 'text') operator = this.textSearch;
// only accept search.operator if it is valid
for (var i = 0; i < operators.length; i++) {
var oper = operators[i];
if ($.isPlainObject(oper)) oper = oper.oper;
if (search.operator == oper) {
operator = search.operator;
break;
}
}
// init types
switch (search.type) {
case 'text':
case 'alphanumeric':
$('#grid_'+ this.name +'_field_' + s).w2field(search.type, search.options);
break;
case 'int':
case 'float':
case 'hex':
case 'color':
case 'money':
case 'currency':
case 'percent':
case 'date':
case 'time':
case 'datetime':
$('#grid_'+ this.name +'_field_'+s).w2field(search.type, search.options);
$('#grid_'+ this.name +'_field2_'+s).w2field(search.type, search.options);
setTimeout(function () { // convert to date if it is number
$('#grid_'+ obj.name +'_field_'+s).keydown();
$('#grid_'+ obj.name +'_field2_'+s).keydown();
}, 1);
break;
case 'list':
case 'combo':
case 'enum':
var options = search.options;
if (search.type == 'list') options.selected = {};
if (search.type == 'enum') options.selected = [];
if (sdata) options.selected = sdata.value;
$('#grid_'+ this.name +'_field_'+s).w2field(search.type, $.extend({ openOnFocus: true }, options));
if (sdata && sdata.text != null) $('#grid_'+ this.name +'_field_'+s).data('selected', {id: sdata.value, text: sdata.text});
break;
case 'select':
// build options
var options = '<option value="">--</option>';
for (var i = 0; i < search.options.items.length; i++) {
var si = search.options.items[i];
if ($.isPlainObject(search.options.items[i])) {
var val = si.id;
var txt = si.text;
if (val == null && si.value != null) val = si.value;
if (txt == null && si.text != null) txt = si.text;
if (val == null) val = '';
options += '<option value="'+ val +'">'+ txt +'</option>';
} else {
options += '<option value="'+ si +'">'+ si +'</option>';
}
}
$('#grid_'+ this.name +'_field_'+s).html(options);
break;
}
if (sdata != null) {
if (sdata.type == 'int' && ['in', 'not in'].indexOf(sdata.operator) != -1) {
$('#grid_'+ this.name +'_field_'+ s).w2field('clear').val(sdata.value);
}
$('#grid_'+ this.name +'_operator_'+ s).val(sdata.operator).trigger('change');
if (!$.isArray(sdata.value)) {
if (sdata.value != null) $('#grid_'+ this.name +'_field_'+ s).val(sdata.value).trigger('change');
} else {
if (['in', 'not in'].indexOf(sdata.operator) != -1) {
$('#grid_'+ this.name +'_field_'+ s).val(sdata.value).trigger('change');
} else {
$('#grid_'+ this.name +'_field_'+ s).val(sdata.value[0]).trigger('change');
$('#grid_'+ this.name +'_field2_'+ s).val(sdata.value[1]).trigger('change');
}
}
} else {
$('#grid_'+ this.name +'_operator_'+s).val(operator).trigger('change');
}
}
// add on change event
$('#w2ui-overlay-'+ this.name +'-searchOverlay .w2ui-grid-searches *[rel=search]').on('keypress', function (evnt) {
if (evnt.keyCode == 13) {
obj.search();
$().w2overlay({ name: obj.name + '-searchOverlay' });
}
});
},
getColumnsHTML: function () {
var obj = this;
var html1 = '';
var html2 = '';
if (this.show.columnHeaders) {
if (this.columnGroups.length > 0) {
var tmp1 = getColumns(true);
var tmp2 = getGroups();
var tmp3 = getColumns(false);
html1 = tmp1[0] + tmp2[0] + tmp3[0];
html2 = tmp1[1] + tmp2[1] + tmp3[1];
} else {
var tmp = getColumns(true);
html1 = tmp[0];
html2 = tmp[1];
}
}
return [html1, html2];
function getGroups () {
var html1 = '<tr>';
var html2 = '<tr>';
var tmpf = '';
// add empty group at the end
var tmp = obj.columnGroups.length - 1;
if (obj.columnGroups[tmp].text == null && obj.columnGroups[tmp].caption != null) {
console.log('NOTICE: grid columnGroup.caption property is deprecated, please use columnGroup.text. Group -> ', obj.columnGroups[tmp]);
obj.columnGroups[tmp].text = obj.columnGroups[tmp].caption;
}
if (obj.columnGroups[obj.columnGroups.length-1].text != '') obj.columnGroups.push({ text: '' });
if (obj.show.lineNumbers) {
html1 += '<td class="w2ui-head w2ui-col-number">'+
' <div style="height: '+ (obj.recordHeight+1) +'px">&#160;</div>'+
'</td>';
}
if (obj.show.selectColumn) {
html1 += '<td class="w2ui-head w2ui-col-select">'+
' <div style="height: 25px">&#160;</div>'+
'</td>';
}
if (obj.show.expandColumn) {
html1 += '<td class="w2ui-head w2ui-col-expand">'+
' <div style="height: 25px">&#160;</div>'+
'</td>';
}
var ii = 0;
html2 += '<td id="grid_'+ obj.name + '_column_start" class="w2ui-head" col="start" style="border-right: 0"></td>';
if (obj.show.orderColumn) {
html2 += '<td class="w2ui-head w2ui-col-order" col="order">'+
' <div style="height: 25px">&#160;</div>'+
'</td>';
}
for (var i=0; i<obj.columnGroups.length; i++) {
var colg = obj.columnGroups[i];
var col = obj.columns[ii];
if (colg.colspan != null) colg.span = colg.colspan;
if (colg.span == null || colg.span != parseInt(colg.span)) colg.span = 1;
if (col.text == null && col.caption != null) {
console.log('NOTICE: grid column.caption property is deprecated, please use column.text. Column ->', col);
col.text = col.caption;
}
var colspan = 0;
for (var jj = ii; jj < ii + colg.span; jj++) {
if (obj.columns[jj] && !obj.columns[jj].hidden) {
colspan++;
}
}
if (i == obj.columnGroups.length-1) {
colspan = 100; // last column
}
if (colspan <= 0) {
// do nothing here, all columns in the group are hidden.
} else if (colg.master === true) {
var sortStyle = '';
for (var si = 0; si < obj.sortData.length; si++) {
if (obj.sortData[si].field == col.field) {
if ((obj.sortData[si].direction || '').toLowerCase() === 'asc') sortStyle = 'w2ui-sort-up';
if ((obj.sortData[si].direction || '').toLowerCase() === 'desc') sortStyle = 'w2ui-sort-down';
}
}
var resizer = "";
if (col.resizable !== false) {
resizer = '<div class="w2ui-resizer" name="'+ ii +'"></div>';
}
var text = (typeof col.text == 'function' ? col.text(col) : col.text);
tmpf = '<td id="grid_'+ obj.name + '_column_' + ii +'" class="w2ui-head '+ sortStyle +'" col="'+ ii + '" '+
' rowspan="2" colspan="'+ colspan +'" '+
' oncontextmenu = "w2ui[\''+ obj.name +'\'].contextMenu(null, '+ ii +', event);"'+
' onclick="w2ui[\''+ obj.name +'\'].columnClick(\''+ col.field +'\', event);"'+
' ondblclick="w2ui[\''+ obj.name +'\'].columnDblClick(\''+ col.field +'\', event);">'+
resizer +
' <div class="w2ui-col-group w2ui-col-header '+ (sortStyle ? 'w2ui-col-sorted' : '') +'">'+
' <div class="'+ sortStyle +'"></div>'+
(!text ? '&#160;' : text) +
' </div>'+
'</td>';
if (col && col.frozen) html1 += tmpf; else html2 += tmpf;
} else {
var gText = (typeof colg.text == 'function' ? colg.text(colg) : colg.text);
tmpf = '<td id="grid_'+ obj.name + '_column_' + ii +'" class="w2ui-head" col="'+ ii + '" '+
' colspan="'+ colspan +'">'+
' <div class="w2ui-col-group">'+
(!gText ? '&#160;' : gText) +
' </div>'+
'</td>';
if (col && col.frozen) html1 += tmpf; else html2 += tmpf;
}
ii += colg.span;
}
html1 += '<td></td></tr>'; // need empty column for border-right
html2 += '<td id="grid_'+ obj.name + '_column_end" class="w2ui-head" col="end"></td></tr>';
return [html1, html2];
}
function getColumns (master) {
var html1 = '<tr>';
var html2 = '<tr>';
if (obj.show.lineNumbers) {
html1 += '<td class="w2ui-head w2ui-col-number" '+
' onclick="w2ui[\''+ obj.name +'\'].columnClick(\'line-number\', event);"'+
' ondblclick="w2ui[\''+ obj.name +'\'].columnDblClick(\'line-number\', event);">'+
' <div>#</div>'+
'</td>';
}
if (obj.show.selectColumn) {
html1 += '<td class="w2ui-head w2ui-col-select"'+
' onclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
' <div>'+
' <input type="checkbox" id="grid_'+ obj.name +'_check_all" tabindex="-1"'+
' style="' + (obj.multiSelect == false ? 'display: none;' : '') + '"'+
' onmousedown="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;"'+
' onclick="var grid = w2ui[\''+ obj.name +'\'];'+
' if (this.checked) grid.selectAll(); else grid.selectNone();'+
' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;'+
' clearTimeout(grid.last.kbd_timer); /* keep focus */' +
' "/>'+
' </div>'+
'</td>';
}
if (obj.show.expandColumn) {
html1 += '<td class="w2ui-head w2ui-col-expand">'+
' <div>&#160;</div>'+
'</td>';
}
var ii = 0;
var id = 0;
var colg;
html2 += '<td id="grid_'+ obj.name + '_column_start" class="w2ui-head" col="start" style="border-right: 0"></td>';
if (obj.show.orderColumn) {
html2 += '<td class="w2ui-head w2ui-col-order" col="order">'+
' <div>&#160;</div>'+
'</td>';
}
for (var i = 0; i < obj.columns.length; i++) {
var col = obj.columns[i];
if (col.text == null && col.caption != null) {
console.log('NOTICE: grid column.caption property is deprecated, please use column.text. Column -> ', col);
col.text = col.caption;
}
if (col.size == null) col.size = '100%';
if (i == id) { // always true on first iteration
colg = obj.columnGroups[ii++] || {};
id = id + colg.span;
}
if ((i < obj.last.colStart || i > obj.last.colEnd) && !col.frozen)
continue;
if (col.hidden)
continue;
if (colg.master !== true || master) { // grouping of columns
var colCellHTML = obj.getColumnCellHTML(i);
if (col && col.frozen) html1 += colCellHTML; else html2 += colCellHTML;
}
}
html1 += '<td class="w2ui-head w2ui-head-last"><div>&#160;</div></td>';
html2 += '<td class="w2ui-head w2ui-head-last" col="end"><div>&#160;</div></td>';
html1 += '</tr>';
html2 += '</tr>';
return [html1, html2];
}
},
getColumnCellHTML: function (i) {
var col = this.columns[i];
if (col == null) return '';
// reorder style
var reorderCols = (this.reorderColumns && (!this.columnGroups || !this.columnGroups.length)) ? ' w2ui-reorder-cols-head ' : '';
// sort style
var sortStyle = '';
for (var si = 0; si < this.sortData.length; si++) {
if (this.sortData[si].field == col.field) {
if ((this.sortData[si].direction || '').toLowerCase() === 'asc') sortStyle = 'w2ui-sort-up';
if ((this.sortData[si].direction || '').toLowerCase() === 'desc') sortStyle = 'w2ui-sort-down';
}
}
// col selected
var tmp = this.last.selection.columns;
var selected = false;
for (var t in tmp) {
for (var si = 0; si < tmp[t].length; si++) {
if (tmp[t][si] == i) selected = true;
}
}
var text = (typeof col.text == 'function' ? col.text(col) : col.text);
var html = '<td id="grid_'+ this.name + '_column_' + i +'" col="'+ i +'" class="w2ui-head '+ sortStyle + reorderCols + '" ' +
(this.columnTooltip == 'normal' && col.tooltip ? 'title="'+ col.tooltip +'" ' : '') +
' onmouseEnter = "w2ui[\''+ this.name +'\'].columnTooltipShow(\''+ i +'\', event);"'+
' onmouseLeave = "w2ui[\''+ this.name +'\'].columnTooltipHide(\''+ i +'\', event);"'+
' oncontextmenu = "w2ui[\''+ this.name +'\'].contextMenu(null, '+ i +', event);"'+
' onclick="w2ui[\''+ this.name +'\'].columnClick(\''+ col.field +'\', event);"'+
' ondblclick="w2ui[\''+ this.name +'\'].columnDblClick(\''+ col.field +'\', event);">'+
(col.resizable !== false ? '<div class="w2ui-resizer" name="'+ i +'"></div>' : '') +
' <div class="w2ui-col-header '+ (sortStyle ? 'w2ui-col-sorted' : '') +' '+ (selected ? 'w2ui-col-selected' : '') +'">'+
' <div class="'+ sortStyle +'"></div>'+
(!text ? '&#160;' : text) +
' </div>'+
'</td>';
return html
},
columnTooltipShow: function (ind) {
if (this.columnTooltip == 'normal') return;
var $el = $(this.box).find('#grid_'+ this.name + '_column_'+ ind);
var item = this.columns[ind];
var pos = this.columnTooltip;
$el.prop('_mouse_over', true);
setTimeout(function () {
if ($el.prop('_mouse_over') === true && $el.prop('_mouse_tooltip') !== true) {
$el.prop('_mouse_tooltip', true);
// show tooltip
$el.w2tag(item.tooltip, { position: pos, top: 5 });
}
}, 1);
},
columnTooltipHide: function (ind) {
if (this.columnTooltip == 'normal') return;
var $el = $(this.box).find('#grid_'+ this.name + '_column_'+ ind);
var item = this.columns[ind];
$el.removeProp('_mouse_over');
setTimeout(function () {
if ($el.prop('_mouse_over') !== true && $el.prop('_mouse_tooltip') === true) {
$el.removeProp('_mouse_tooltip');
// hide tooltip
$el.w2tag();
}
}, 1);
},
getRecordsHTML: function () {
var buffered = this.records.length;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (this.searchData.length != 0 && !url) buffered = this.last.searchIds.length;
// larger number works better with chrome, smaller with FF.
if (buffered > this.vs_start) this.last.show_extra = this.vs_extra; else this.last.show_extra = this.vs_start;
var records = $('#grid_'+ this.name +'_records');
var limit = Math.floor((records.height() || 0) / this.recordHeight) + this.last.show_extra + 1;
if (!this.fixedBody || limit > buffered) limit = buffered;
// always need first record for resizing purposes
var rec_html = this.getRecordHTML(-1, 0);
var html1 = '<table><tbody>' + rec_html[0];
var html2 = '<table><tbody>' + rec_html[1];
// first empty row with height
html1 += '<tr id="grid_'+ this.name + '_frec_top" line="top" style="height: '+ 0 +'px">'+
' <td colspan="2000"></td>'+
'</tr>';
html2 += '<tr id="grid_'+ this.name + '_rec_top" line="top" style="height: '+ 0 +'px">'+
' <td colspan="2000"></td>'+
'</tr>';
for (var i = 0; i < limit; i++) {
rec_html = this.getRecordHTML(i, i+1);
html1 += rec_html[0];
html2 += rec_html[1];
}
var h2 = (buffered - limit) * this.recordHeight;
html1 += '<tr id="grid_' + this.name + '_frec_bottom" rec="bottom" line="bottom" style="height: ' + h2 + 'px; vertical-align: top">' +
' <td colspan="2000" style="border-right: 1px solid #D6D5D7;"></td>'+
'</tr>'+
'<tr id="grid_'+ this.name +'_frec_more" style="display: none; ">'+
' <td colspan="2000" class="w2ui-load-more"></td>'+
'</tr>'+
'</tbody></table>';
html2 += '<tr id="grid_' + this.name + '_rec_bottom" rec="bottom" line="bottom" style="height: ' + h2 + 'px; vertical-align: top">' +
' <td colspan="2000" style="border: 0"></td>'+
'</tr>'+
'<tr id="grid_'+ this.name +'_rec_more" style="display: none">'+
' <td colspan="2000" class="w2ui-load-more"></td>'+
'</tr>'+
'</tbody></table>';
this.last.range_start = 0;
this.last.range_end = limit;
return [html1, html2];
},
getSummaryHTML: function () {
if (this.summary.length === 0) return;
var rec_html = this.getRecordHTML(-1, 0); // need this in summary too for colspan to work properly
var html1 = '<table><tbody>' + rec_html[0];
var html2 = '<table><tbody>' + rec_html[1];
for (var i = 0; i < this.summary.length; i++) {
rec_html = this.getRecordHTML(i, i+1, true);
html1 += rec_html[0];
html2 += rec_html[1];
}
html1 += '</tbody></table>';
html2 += '</tbody></table>';
return [html1, html2];
},
scroll: function (event) {
var time = (new Date()).getTime();
var obj = this;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
var records = $('#grid_'+ this.name +'_records');
var frecords = $('#grid_'+ this.name +'_frecords');
// sync scroll positions
if (event) {
var sTop = event.target.scrollTop;
var sLeft = event.target.scrollLeft;
obj.last.scrollTop = sTop;
obj.last.scrollLeft = sLeft;
$('#grid_'+ obj.name +'_columns')[0].scrollLeft = sLeft;
$('#grid_'+ obj.name +'_summary')[0].scrollLeft = sLeft;
frecords[0].scrollTop = sTop;
}
// hide bubble
if (this.last.bubbleEl) {
$(this.last.bubbleEl).w2tag();
this.last.bubbleEl = null;
}
// column virtual scroll
var colStart = null;
var colEnd = null;
if (obj.disableCVS || obj.columnGroups.length > 0) {
// disable virtual scroll
colStart = 0;
colEnd = obj.columns.length - 1;
} else {
var sWidth = records.width();
var cLeft = 0;
for (var i = 0; i < obj.columns.length; i++) {
if (obj.columns[i].frozen || obj.columns[i].hidden) continue;
var cSize = parseInt(obj.columns[i].sizeCalculated ? obj.columns[i].sizeCalculated : obj.columns[i].size);
if (cLeft + cSize + 30 > obj.last.scrollLeft && colStart == null) colStart = i;
if (cLeft + cSize - 30 > obj.last.scrollLeft + sWidth && colEnd == null) colEnd = i;
cLeft += cSize;
}
if (colEnd == null) colEnd = obj.columns.length - 1;
}
if (colStart != null) {
if (colStart < 0) colStart = 0;
if (colEnd < 0) colEnd = 0;
if (colStart == colEnd) {
if (colStart > 0) colStart--; else colEnd++; // show at least one column
}
// ---------
if (colStart != obj.last.colStart || colEnd != obj.last.colEnd) {
var $box = $(obj.box);
var deltaStart = Math.abs(colStart - obj.last.colStart);
var deltaEnd = Math.abs(colEnd - obj.last.colEnd)
// add/remove columns for small jumps
if (deltaStart < 5 && deltaEnd < 5) {
var $cfirst = $box.find('.w2ui-grid-columns #grid_'+ obj.name +'_column_start');
var $clast = $box.find('.w2ui-grid-columns .w2ui-head-last');
var $rfirst = $box.find('#grid_'+ obj.name +'_records .w2ui-grid-data-spacer');
var $rlast = $box.find('#grid_'+ obj.name +'_records .w2ui-grid-data-last');
var $sfirst = $box.find('#grid_'+ obj.name +'_summary .w2ui-grid-data-spacer');
var $slast = $box.find('#grid_'+ obj.name +'_summary .w2ui-grid-data-last');
// remove on left
if (colStart > obj.last.colStart) {
for (var i = obj.last.colStart; i < colStart; i++) {
$box.find('#grid_'+ obj.name +'_columns #grid_'+ obj.name +'_column_'+ i).remove(); // column
$box.find('#grid_'+ obj.name +'_records td[col="'+ i +'"]').remove(); // record
$box.find('#grid_'+ obj.name +'_summary td[col="'+ i +'"]').remove(); // summary
}
}
// remove on right
if (colEnd < obj.last.colEnd) {
for (var i = obj.last.colEnd; i > colEnd; i--) {
$box.find('#grid_'+ obj.name +'_columns #grid_'+ obj.name +'_column_'+ i).remove(); // column
$box.find('#grid_'+ obj.name +'_records td[col="'+ i +'"]').remove(); // record
$box.find('#grid_'+ obj.name +'_summary td[col="'+ i +'"]').remove(); // summary
}
}
// add on left
if (colStart < obj.last.colStart) {
for (var i = obj.last.colStart - 1; i >= colStart; i--) {
if (obj.columns[i] && (obj.columns[i].frozen || obj.columns[i].hidden)) continue;
$cfirst.after(obj.getColumnCellHTML(i)); // column
// record
$rfirst.each(function (ind, el) {
var index = $(el).parent().attr('index');
var td = '<td class="w2ui-grid-data" col="'+ i +'" style="height: 0px"></td>'; // width column
if (index != null) td = obj.getCellHTML(parseInt(index), i, false);
$(el).after(td);
});
// summary
$sfirst.each(function (ind, el) {
var index = $(el).parent().attr('index');
var td = '<td class="w2ui-grid-data" col="'+ i +'" style="height: 0px"></td>'; // width column
if (index != null) td = obj.getCellHTML(parseInt(index), i, true);
$(el).after(td);
});
}
}
// add on right
if (colEnd > obj.last.colEnd) {
for (var i = obj.last.colEnd + 1; i <= colEnd; i++) {
if (obj.columns[i] && (obj.columns[i].frozen || obj.columns[i].hidden)) continue;
$clast.before(obj.getColumnCellHTML(i)); // column
// record
$rlast.each(function (ind, el) {
var index = $(el).parent().attr('index');
var td = '<td class="w2ui-grid-data" col="'+ i +'" style="height: 0px"></td>'; // width column
if (index != null) td = obj.getCellHTML(parseInt(index), i, false);
$(el).before(td);
});
// summary
$slast.each(function (ind, el) {
var index = $(el).parent().attr('index') || -1;
var td = obj.getCellHTML(parseInt(index), i, true);
$(el).before(td);
});
}
}
obj.last.colStart = colStart;
obj.last.colEnd = colEnd;
obj.resizeRecords();
} else {
obj.last.colStart = colStart;
obj.last.colEnd = colEnd;
// dot not just call obj.refresh();
var colHTML = this.getColumnsHTML();
var recHTML = this.getRecordsHTML();
var sumHTML = this.getSummaryHTML();
var $columns = $box.find('#grid_'+ this.name +'_columns');
var $records = $box.find('#grid_'+ this.name +'_records');
var $frecords = $box.find('#grid_'+ this.name +'_frecords');
var $summary = $box.find('#grid_'+ this.name +'_summary');
$columns.find('tbody').html(colHTML[1]);
$frecords.html(recHTML[0]);
$records.prepend(recHTML[1]);
if (sumHTML != null) $summary.html(sumHTML[1]);
// need timeout to clean up (otherwise scroll problem)
setTimeout(function () {
$records.find('> table').not('table:first-child').remove();
if ($summary[0]) $summary[0].scrollLeft = obj.last.scrollLeft;
}, 1);
obj.resizeRecords();
}
}
}
// perform virtual scroll
var buffered = this.records.length;
if (buffered > this.total && this.total !== -1) buffered = this.total;
if (this.searchData.length != 0 && !url) buffered = this.last.searchIds.length;
if (buffered === 0 || records.length === 0 || records.height() === 0) return;
if (buffered > this.vs_start) this.last.show_extra = this.vs_extra; else this.last.show_extra = this.vs_start;
// update footer
var t1 = Math.round(records[0].scrollTop / this.recordHeight + 1);
var t2 = t1 + (Math.round(records.height() / this.recordHeight) - 1);
if (t1 > buffered) t1 = buffered;
if (t2 >= buffered - 1) t2 = buffered;
$('#grid_'+ this.name + '_footer .w2ui-footer-right').html(
(obj.show.statusRange
? w2utils.formatNumber(this.offset + t1) + '-' + w2utils.formatNumber(this.offset + t2) +
(this.total != -1 ? ' ' + w2utils.lang('of') + ' ' + w2utils.formatNumber(this.total) : '')
: '') +
(url && obj.show.statusBuffered ? ' ('+ w2utils.lang('buffered') + ' '+ w2utils.formatNumber(buffered) +
(this.offset > 0 ? ', skip ' + w2utils.formatNumber(this.offset) : '') + ')' : '')
);
// only for local data source, else no extra records loaded
if (!url && (!this.fixedBody || (this.total != -1 && this.total <= this.vs_start))) return;
// regular processing
var start = Math.floor(records[0].scrollTop / this.recordHeight) - this.last.show_extra;
var end = start + Math.floor(records.height() / this.recordHeight) + this.last.show_extra * 2 + 1;
// var div = start - this.last.range_start;
if (start < 1) start = 1;
if (end > this.total && this.total != -1) end = this.total;
var tr1 = records.find('#grid_'+ this.name +'_rec_top');
var tr2 = records.find('#grid_'+ this.name +'_rec_bottom');
var tr1f = frecords.find('#grid_'+ this.name +'_frec_top');
var tr2f = frecords.find('#grid_'+ this.name +'_frec_bottom');
// if row is expanded
if (String(tr1.next().prop('id')).indexOf('_expanded_row') != -1) {
tr1.next().remove();
tr1f.next().remove();
}
if (this.total > end && String(tr2.prev().prop('id')).indexOf('_expanded_row') != -1) {
tr2.prev().remove();
tr2f.prev().remove();
}
var first = parseInt(tr1.next().attr('line'));
var last = parseInt(tr2.prev().attr('line'));
//$('#log').html('buffer: '+ this.buffered +' start-end: ' + start + '-'+ end + ' ===> first-last: ' + first + '-' + last);
if (first < start || first == 1 || this.last.pull_refresh) { // scroll down
if (end <= last + this.last.show_extra - 2 && end != this.total) return;
this.last.pull_refresh = false;
// remove from top
while (true) {
var tmp1 = frecords.find('#grid_'+ this.name +'_frec_top').next();
var tmp2 = records.find('#grid_'+ this.name +'_rec_top').next();
if (tmp2.attr('line') == 'bottom') break;
if (parseInt(tmp2.attr('line')) < start) { tmp1.remove(); tmp2.remove(); } else break;
}
// add at bottom
var tmp = records.find('#grid_'+ this.name +'_rec_bottom').prev();
var rec_start = tmp.attr('line');
if (rec_start == 'top') rec_start = start;
for (var i = parseInt(rec_start) + 1; i <= end; i++) {
if (!this.records[i-1]) continue;
var tmp2 = this.records[i-1].w2ui;
if (tmp2 && !Array.isArray(tmp2.children)) {
tmp2.expanded = false;
}
var rec_html = this.getRecordHTML(i-1, i);
tr2.before(rec_html[1]);
tr2f.before(rec_html[0]);
}
markSearch();
setTimeout(function() { obj.refreshRanges(); }, 0);
} else { // scroll up
if (start >= first - this.last.show_extra + 2 && start > 1) return;
// remove from bottom
while (true) {
var tmp1 = frecords.find('#grid_'+ this.name +'_frec_bottom').prev();
var tmp2 = records.find('#grid_'+ this.name +'_rec_bottom').prev();
if (tmp2.attr('line') == 'top') break;
if (parseInt(tmp2.attr('line')) > end) { tmp1.remove(); tmp2.remove(); } else break;
}
// add at top
var tmp = records.find('#grid_'+ this.name +'_rec_top').next();
var rec_start = tmp.attr('line');
if (rec_start == 'bottom') rec_start = end;
for (var i = parseInt(rec_start) - 1; i >= start; i--) {
if (!this.records[i-1]) continue;
var tmp2 = this.records[i-1].w2ui;
if (tmp2 && !Array.isArray(tmp2.children)) {
tmp2.expanded = false;
}
var rec_html = this.getRecordHTML(i-1, i);
tr1.after(rec_html[1]);
tr1f.after(rec_html[0]);
}
markSearch();
setTimeout(function() { obj.refreshRanges(); }, 0);
}
// first/last row size
var h1 = (start - 1) * obj.recordHeight;
var h2 = (buffered - end) * this.recordHeight;
if (h2 < 0) h2 = 0;
tr1.css('height', h1 + 'px');
tr1f.css('height', h1 + 'px');
tr2.css('height', h2 + 'px');
tr2f.css('height', h2 + 'px');
obj.last.range_start = start;
obj.last.range_end = end;
// load more if needed
var s = Math.floor(records[0].scrollTop / this.recordHeight);
var e = s + Math.floor(records.height() / this.recordHeight);
if (e + 10 > buffered && this.last.pull_more !== true && (buffered < this.total - this.offset || (this.total == -1 && this.last.xhr_hasMore))) {
if (this.autoLoad === true) {
this.last.pull_more = true;
this.last.xhr_offset += this.limit;
this.request('get');
}
// scroll function
var more = $('#grid_'+ this.name +'_rec_more, #grid_'+ this.name +'_frec_more');
more.show()
.eq(1) // only main table
.off('.load-more')
.on('click.load-more', function () {
// show spinner
$(this).find('td').html('<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>');
// load more
obj.last.pull_more = true;
obj.last.xhr_offset += obj.limit;
obj.request('get');
})
.find('td')
.html(obj.autoLoad
? '<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>'
: '<div style="padding-top: 15px">'+ w2utils.lang('Load') + ' ' + obj.limit + ' ' + w2utils.lang('More') + '...</div>'
)
}
function markSearch() {
// mark search
if (!obj.markSearch) return;
clearTimeout(obj.last.marker_timer);
obj.last.marker_timer = setTimeout(function () {
// mark all search strings
var search = [];
for (var s = 0; s < obj.searchData.length; s++) {
var sdata = obj.searchData[s];
var fld = obj.getSearch(sdata.field);
if (!fld || fld.hidden) continue;
var ind = obj.getColumn(sdata.field, true);
search.push({ field: sdata.field, search: sdata.value, col: ind });
}
if (search.length > 0) {
search.forEach(function (item) {
$(obj.box).find('td[col="'+ item.col +'"]').not('.w2ui-head').w2marker(item.search);
});
}
}, 50);
}
},
getRecordHTML: function (ind, lineNum, summary) {
var tmph = '';
var rec_html1 = '';
var rec_html2 = '';
var sel = this.last.selection;
var record;
// first record needs for resize purposes
if (ind == -1) {
rec_html1 += '<tr line="0">';
rec_html2 += '<tr line="0">';
if (this.show.lineNumbers) rec_html1 += '<td class="w2ui-col-number" style="height: 0px;"></td>';
if (this.show.selectColumn) rec_html1 += '<td class="w2ui-col-select" style="height: 0px;"></td>';
if (this.show.expandColumn) rec_html1 += '<td class="w2ui-col-expand" style="height: 0px;"></td>';
rec_html2 += '<td class="w2ui-grid-data w2ui-grid-data-spacer" col="start" style="height: 0px; width: 0px;"></td>';
if (this.show.orderColumn) rec_html2 += '<td class="w2ui-col-order" style="height: 0px;" col="order"></td>';
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
tmph = '<td class="w2ui-grid-data" col="'+ i +'" style="height: 0px;"></td>';
if (col.frozen && !col.hidden) {
rec_html1 += tmph;
} else {
if (col.hidden || i < this.last.colStart || i > this.last.colEnd) continue;
rec_html2 += tmph;
}
}
rec_html1 += '<td class="w2ui-grid-data-last" style="height: 0px"></td>';
rec_html2 += '<td class="w2ui-grid-data-last" col="end" style="height: 0px"></td>';
rec_html1 += '</tr>';
rec_html2 += '</tr>';
return [rec_html1, rec_html2];
}
// regular record
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (summary !== true) {
if (this.searchData.length > 0 && !url) {
if (ind >= this.last.searchIds.length) return '';
ind = this.last.searchIds[ind];
record = this.records[ind];
} else {
if (ind >= this.records.length) return '';
record = this.records[ind];
}
} else {
if (ind >= this.summary.length) return '';
record = this.summary[ind];
}
if (!record) return '';
if (record.recid == null && this.recid != null) {
var rid = this.parseField(record, this.recid)
if (rid != null) record.recid = rid;
}
var id = w2utils.escapeId(record.recid);
var isRowSelected = false;
if (sel.indexes.indexOf(ind) != -1) isRowSelected = true;
var rec_style = (record.w2ui ? record.w2ui.style : '');
if (rec_style == null || typeof rec_style != 'string') rec_style = '';
var rec_class = (record.w2ui ? record.w2ui.class : '');
if (rec_class == null || typeof rec_class != 'string') rec_class = '';
// render TR
rec_html1 += '<tr id="grid_'+ this.name +'_frec_'+ record.recid +'" recid="'+ record.recid +'" line="'+ lineNum +'" index="'+ ind +'" '+
' class="'+ (lineNum % 2 === 0 ? 'w2ui-even' : 'w2ui-odd') + ' w2ui-record ' + rec_class +
(isRowSelected && this.selectType == 'row' ? ' w2ui-selected' : '') +
(record.w2ui && record.w2ui.editable === false ? ' w2ui-no-edit' : '') +
(record.w2ui && record.w2ui.expanded === true ? ' w2ui-expanded' : '') + '" ' +
' style="height: '+ this.recordHeight +'px; '+ (!isRowSelected && rec_style != '' ? rec_style : rec_style.replace('background-color', 'none')) +'" '+
(rec_style != '' ? 'custom_style="'+ rec_style +'"' : '') +
'>';
rec_html2 += '<tr id="grid_'+ this.name +'_rec_'+ record.recid +'" recid="'+ record.recid +'" line="'+ lineNum +'" index="'+ ind +'" '+
' class="'+ (lineNum % 2 === 0 ? 'w2ui-even' : 'w2ui-odd') + ' w2ui-record ' + rec_class +
(isRowSelected && this.selectType == 'row' ? ' w2ui-selected' : '') +
(record.w2ui && record.w2ui.editable === false ? ' w2ui-no-edit' : '') +
(record.w2ui && record.w2ui.expanded === true ? ' w2ui-expanded' : '') + '" ' +
' style="height: '+ this.recordHeight +'px; '+ (!isRowSelected && rec_style != '' ? rec_style : rec_style.replace('background-color', 'none')) +'" '+
(rec_style != '' ? 'custom_style="'+ rec_style +'"' : '') +
'>';
if (this.show.lineNumbers) {
rec_html1 += '<td id="grid_'+ this.name +'_cell_'+ ind +'_number' + (summary ? '_s' : '') + '" '+
' class="w2ui-col-number '+ (isRowSelected ? ' w2ui-row-selected' : '') +'"'+
(this.reorderRows ? ' style="cursor: move"' : '') + '>'+
(summary !== true ? this.getLineHTML(lineNum, record) : '') +
'</td>';
}
if (this.show.selectColumn) {
var hideCB = false;
if (record && record.w2ui && record.w2ui.hideCheckBox === true) hideCB = true;
rec_html1 +=
'<td id="grid_'+ this.name +'_cell_'+ ind +'_select' + (summary ? '_s' : '') + '" class="w2ui-grid-data w2ui-col-select">'+
(summary !== true && !(record.w2ui && record.w2ui.hideCheckBox === true) ?
' <div>'+
' <input class="w2ui-grid-select-check" type="checkbox" tabindex="-1" '+
(isRowSelected ? 'checked="checked"' : '') + ' style="pointer-events: none"/>'+
' </div>'
:
'' ) +
'</td>';
}
if (this.show.expandColumn) {
var tmp_img = '';
if (record.w2ui && record.w2ui.expanded === true) tmp_img = '-'; else tmp_img = '+';
if (record.w2ui && record.w2ui.expanded == 'none') tmp_img = '';
if (record.w2ui && record.w2ui.expanded == 'spinner') tmp_img = '<div class="w2ui-spinner" style="width: 16px; margin: -2px 2px;"></div>';
rec_html1 +=
'<td id="grid_'+ this.name +'_cell_'+ ind +'_expand' + (summary ? '_s' : '') + '" class="w2ui-grid-data w2ui-col-expand">'+
(summary !== true ?
' <div ondblclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;" '+
' onclick="w2ui[\''+ this.name +'\'].toggle(jQuery(this).parents(\'tr\').attr(\'recid\')); '+
' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
' '+ tmp_img +' </div>'
:
'' ) +
'</td>';
}
// insert empty first column
rec_html2 += '<td class="w2ui-grid-data-spacer" col="start" style="border-right: 0"></td>';
if (this.show.orderColumn) {
rec_html2 +=
'<td id="grid_'+ this.name +'_cell_'+ ind +'_order' + (summary ? '_s' : '') + '" class="w2ui-grid-data w2ui-col-order" col="order">'+
(summary !== true ? '<div title="Drag to reorder">&nbsp;</div>' : '' ) +
'</td>';
}
var col_ind = 0;
var col_skip = 0;
while (true) {
var col_span = 1;
var col = this.columns[col_ind];
if (col == null) break;
if (col.hidden) {
col_ind++;
if (col_skip > 0) col_skip--;
continue;
}
if (col_skip > 0) {
col_ind++;
if (this.columns[col_ind] == null) break;
record.w2ui.colspan[this.columns[col_ind-1].field] = 0; // need it for other methods
col_skip--;
continue;
} else if (record.w2ui) {
var tmp1 = record.w2ui.colspan;
var tmp2 = this.columns[col_ind].field;
if (tmp1 && tmp1[tmp2] === 0) {
delete tmp1[tmp2]; // if no longer colspan then remove 0
}
}
// column virtual scroll
if ((col_ind < this.last.colStart || col_ind > this.last.colEnd) && !col.frozen) {
col_ind++;
continue;
}
if (record.w2ui) {
if (typeof record.w2ui.colspan == 'object') {
var span = parseInt(record.w2ui.colspan[col.field]) || null;
if (span > 1) {
// if there are hidden columns, then no colspan on them
var hcnt = 0;
for (var i = col_ind; i < col_ind + span; i++) {
if (i >= this.columns.length) break;
if (this.columns[i].hidden) hcnt++;
}
col_span = span - hcnt;
col_skip = span - 1;
}
}
}
var rec_cell = this.getCellHTML(ind, col_ind, summary, col_span);
if (col.frozen) rec_html1 += rec_cell; else rec_html2 += rec_cell;
col_ind++;
}
rec_html1 += '<td class="w2ui-grid-data-last"></td>';
rec_html2 += '<td class="w2ui-grid-data-last" col="end"></td>';
rec_html1 += '</tr>';
rec_html2 += '</tr>';
return [rec_html1, rec_html2];
},
getLineHTML: function(lineNum) {
return '<div>' + lineNum + '</div>';
},
getCellHTML: function (ind, col_ind, summary, col_span) {
var obj = this;
var col = this.columns[col_ind];
if (col == null) return '';
var record = (summary !== true ? this.records[ind] : this.summary[ind]);
var data = (ind !== -1 ? this.getCellValue(ind, col_ind, summary) : '');
var edit = (ind !== -1 ? this.getCellEditable(ind, col_ind) : '');
var style = 'max-height: '+ parseInt(this.recordHeight) +'px;' + (col.clipboardCopy ? 'margin-right: 20px' : '');
var isChanged = !summary && record && record.w2ui && record.w2ui.changes && record.w2ui.changes[col.field] != null;
var addStyle = '';
var addClass = '';
var sel = this.last.selection;
var isRowSelected = false;
var infoBubble = '';
if (sel.indexes.indexOf(ind) != -1) isRowSelected = true;
if (col_span == null) {
if (record && record.w2ui && record.w2ui.colspan && record.w2ui.colspan[col.field]) {
col_span = record.w2ui.colspan[col.field];
} else {
col_span = 1;
}
}
// expand icon
if (col_ind === 0 && record && record.w2ui && Array.isArray(record.w2ui.children)) {
var level = 0;
var subrec = this.get(record.w2ui.parent_recid, true);
while (true) {
if (subrec != null) {
level++
var tmp = this.records[subrec].w2ui;
if (tmp != null && tmp.parent_recid != null) {
subrec = this.get(tmp.parent_recid, true);
} else {
break;
}
} else {
break;
}
}
if (record.w2ui.parent_recid){
for (var i = 0; i < level; i++) {
infoBubble += '<span class="w2ui-show-children w2ui-icon-empty"></span>';
}
}
infoBubble += '<span class="w2ui-show-children '+
(record.w2ui.children.length > 0
? (record.w2ui.expanded ? 'w2ui-icon-collapse' : 'w2ui-icon-expand')
: 'w2ui-icon-empty'
) +'" '+
' onclick="event.stopPropagation(); w2ui[\''+ this.name + '\'].toggle(jQuery(this).parents(\'tr\').attr(\'recid\'))"></span>';
}
// info bubble
if (col.info === true) col.info = {};
if (col.info != null) {
var infoIcon = 'w2ui-icon-info';
if (typeof col.info.icon == 'function') {
infoIcon = col.info.icon(record);
} else if (typeof col.info.icon == 'object') {
infoIcon = col.info.icon[this.parseField(record, col.field)] || ''
} else if (typeof col.info.icon == 'string') {
infoIcon = col.info.icon;
}
var infoStyle = col.info.style || '';
if (typeof col.info.style == 'function') {
infoStyle = col.info.style(record);
} else if (typeof col.info.style == 'object') {
infoStyle = col.info.style[this.parseField(record, col.field)] || '';
} else if (typeof col.info.style == 'string') {
infoStyle = col.info.style;
}
infoBubble += '<span class="w2ui-info '+ infoIcon +'" style="'+ infoStyle + '" '+
(col.info.showOn != null ? 'on' + col.info.showOn : 'onclick') +
'="event.stopPropagation(); w2ui[\''+ this.name + '\'].showBubble('+ ind +', '+ col_ind +')"'+
(col.info.hideOn != null ? 'on' + col.info.hideOn : '') +
'="var grid = w2ui[\''+ this.name + '\']; if (grid.last.bubbleEl) { $(grid.last.bubbleEl).w2tag() } grid.last.bubbleEl = null;"'+
'></span>';
}
if (col.render != null && ind !== -1) {
if (typeof col.render == 'function') {
var html = col.render.call(this, record, ind, col_ind, data);
if (html != null && typeof html == 'object') {
data = $.trim(html.html || '');
addClass = html.class || '';
addStyle = html.style || '';
} else {
data = $.trim(html);
}
if (data.length < 4 || data.substr(0, 4).toLowerCase() != '<div') {
data = '<div style="'+ style +'" title="'+ getTitle(data) +'">' + infoBubble + String(data) + '</div>';
}
}
// if it is an object
if (typeof col.render == 'object') {
var dsp = col.render[data];
if (dsp == null || dsp === '') dsp = data;
data = '<div style="'+ style +'" title="'+ getTitle(dsp) +'">' + infoBubble + String(dsp) + '</div>';
}
// formatters
if (typeof col.render == 'string') {
var t = col.render.toLowerCase().indexOf(':');
var tmp = [];
if (t == -1) {
tmp[0] = col.render.toLowerCase();
tmp[1] = '';
} else {
tmp[0] = col.render.toLowerCase().substr(0, t);
tmp[1] = col.render.toLowerCase().substr(t+1);
}
// formatters
var func = w2utils.formatters[tmp[0]];
if (col.options && col.options.autoFormat === false) {
func = null;
}
data = (typeof func == 'function' ? func(data, tmp[1], record) : '');
data = '<div style="'+ style +'" title="'+ getTitle(data) +'">' + infoBubble + String(data) + '</div>';
}
} else {
// if editable checkbox
if (edit && ['checkbox', 'check'].indexOf(edit.type) != -1) {
var changeInd = summary ? -(ind + 1) : ind;
style += 'text-align: center;';
data = '<input tabindex="-1" type="checkbox" '+ (data ? 'checked="checked"' : '') +' onclick="' +
' var obj = w2ui[\''+ this.name + '\']; '+
' obj.editChange.call(obj, this, '+ changeInd +', '+ col_ind +', event); ' +
'"/>';
infoBubble = '';
}
data = '<div style="'+ style +'" title="'+ getTitle(data) +'">' + infoBubble + String(data) + '</div>';
}
if (data == null) data = '';
// --> cell TD
if (typeof col.render == 'string') {
var tmp = col.render.toLowerCase().split(':');
if (['number', 'int', 'float', 'money', 'currency', 'percent', 'size'].indexOf(tmp[0]) != -1) addStyle += 'text-align: right;';
}
if (record && record.w2ui) {
if (typeof record.w2ui.style == 'object') {
if (typeof record.w2ui.style[col_ind] == 'string') addStyle += record.w2ui.style[col_ind] + ';';
if (typeof record.w2ui.style[col.field] == 'string') addStyle += record.w2ui.style[col.field] + ';';
}
if (typeof record.w2ui.class == 'object') {
if (typeof record.w2ui.class[col_ind] == 'string') addClass += record.w2ui.class[col_ind] + ' ';
if (typeof record.w2ui.class[col.field] == 'string') addClass += record.w2ui.class[col.field] + ' ';
}
}
var isCellSelected = false;
if (isRowSelected && $.inArray(col_ind, sel.columns[ind]) != -1) isCellSelected = true;
var clipboardTxt = (typeof col.clipboardCopy == 'string' ? col.clipboardCopy : 'Copy to clipboard')
var clipboardIcon = '<span onmouseEnter="jQuery(this).w2tag(\'' + clipboardTxt +'\', { position: \'top|bottom\' })"'
+ 'onclick="w2ui[\''+ this.name + '\'].clipboardCopy('+ ind +', '+ col_ind +'); jQuery(this).w2tag(\'Copied\', { position: \'top|bottom\' }); event.stopPropagation();" '
+ 'onmouseLeave="jQuery(this).w2tag()" class="w2ui-clipboard-copy w2ui-icon-paste"></span>'
// data
data = '<td class="w2ui-grid-data'+ (isCellSelected ? ' w2ui-selected' : '') + ' ' + addClass +
(isChanged ? ' w2ui-changed' : '') +
'" '+
' id="grid_'+ this.name +'_data_'+ ind +'_'+ col_ind +'" col="'+ col_ind +'" '+
' style="'+ addStyle + (col.style != null ? col.style : '') +'" '+
(col.attr != null ? col.attr : '') +
(col_span > 1 ? 'colspan="'+ col_span + '"' : '') +
'>' + data + (w2utils.stripTags(data) != '' && col.clipboardCopy && clipboardTxt ? clipboardIcon : '') +'</td>';
// summary top row
if (ind === -1 && summary === true) {
data = '<td class="w2ui-grid-data" col="'+ col_ind +'" style="height: 0px; '+ addStyle + '" '+
(col_span > 1 ? 'colspan="'+ col_span + '"' : '') +
'></td>';
}
return data;
function getTitle(cellData){
var title = "";
if (obj.show.recordTitles) {
if (col.title != null) {
if (typeof col.title == 'function') title = col.title.call(obj, record, ind, col_ind);
if (typeof col.title == 'string') title = col.title;
} else {
title = w2utils.stripTags(String(cellData).replace(/"/g, "''"));
}
}
return (title != null) ? String(title) : "";
}
},
clipboardCopy: function (ind, col_ind) {
var rec = this.records[ind]
var col = this.columns[col_ind]
var txt = (col ? this.parseField(rec, col.field) : '');
if (typeof col.clipboardCopy == 'function') {
txt = col.clipboardCopy(rec)
}
$('#grid_' + this.name + '_focus').text(txt).select();
document.execCommand('copy');
},
showBubble: function (ind, col_ind) {
var html = '';
var info = this.columns[col_ind].info;
var rec = this.records[ind];
var el = $(this.box).find('#grid_'+ this.name +'_data_'+ ind +'_'+ col_ind + ' .w2ui-info');
if (this.last.bubbleEl) $(this.last.bubbleEl).w2tag();
this.last.bubbleEl = el;
// if no fields defined - show all
if (info.fields == null) {
info.fields = [];
for (var i = 0; i < this.columns.length; i++) {
var col = this.columns[i];
info.fields.push(col.field + (typeof col.render == 'string' ? ':' + col.render : ''));
}
}
var fields = info.fields;
if (typeof fields == 'function') {
fields = fields(rec, ind, col_ind); // custom renderer
}
// generate html
if (typeof info.render == 'function') {
html = info.render(rec, ind, col_ind);
} else if ($.isArray(fields)) {
// display mentioned fields
html = '<table cellpadding="0" cellspacing="0">';
for (var i = 0; i < fields.length; i++) {
var tmp = String(fields[i]).split(':');
if (tmp[0] == '' || tmp[0] == '-' || tmp[0] == '--' || tmp[0] == '---') {
html += '<tr><td colspan=2><div style="border-top: '+ (tmp[0] == '' ? '0' : '1') +'px solid #C1BEBE; margin: 6px 0px;"></div></td></tr>';
continue;
}
var col = this.getColumn(tmp[0]);
if (col == null) col = { field: tmp[0], caption: tmp[0] }; // if not found in columns
var val = (col ? this.parseField(rec, col.field) : '');
if (tmp.length > 1) {
if (w2utils.formatters[tmp[1]]) {
val = w2utils.formatters[tmp[1]](val, tmp[2] || null, rec);
} else {
console.log('ERROR: w2utils.formatters["'+ tmp[1] + '"] does not exists.')
}
}
if (info.showEmpty !== true && (val == null || val == '')) continue;
if (info.maxLength != null && typeof val == 'string' && val.length > info.maxLength) val = val.substr(0, info.maxLength) + '...';
html += '<tr><td>' + col.text + '</td><td>' + ((val === 0 ? '0' : val) || '') + '</td></tr>';
}
html += '</table>';
} else if ($.isPlainObject(fields)) {
// display some fields
html = '<table cellpadding="0" cellspacing="0">';
for (var caption in fields) {
var fld = fields[caption];
if (fld == '' || fld == '-' || fld == '--' || fld == '---') {
html += '<tr><td colspan=2><div style="border-top: '+ (fld == '' ? '0' : '1') +'px solid #C1BEBE; margin: 6px 0px;"></div></td></tr>';
continue;
}
var tmp = String(fld).split(':');
var col = this.getColumn(tmp[0]);
if (col == null) col = { field: tmp[0], caption: tmp[0] }; // if not found in columns
var val = (col ? this.parseField(rec, col.field) : '');
if (tmp.length > 1) {
if (w2utils.formatters[tmp[1]]) {
val = w2utils.formatters[tmp[1]](val, tmp[2] || null, rec);
} else {
console.log('ERROR: w2utils.formatters["'+ tmp[1] + '"] does not exists.')
}
}
if (typeof fld == 'function') {
val = fld(rec, ind, col_ind);
}
if (info.showEmpty !== true && (val == null || val == '')) continue;
if (info.maxLength != null && typeof val == 'string' && val.length > info.maxLength) val = val.substr(0, info.maxLength) + '...';
html += '<tr><td>' + caption + '</td><td>' + (val || '') + '</td></tr>';
}
html += '</table>';
}
$(el).w2tag($.extend({
html : html,
left : -4,
position : 'bottom|top',
className : 'w2ui-info-bubble',
style : '',
hideOnClick : true
}, info.options || {}));
},
// return null or the editable object if the given cell is editable
getCellEditable: function (ind, col_ind) {
var col = this.columns[col_ind];
var rec = this.records[ind];
if (!rec || !col) return null;
var edit = (rec.w2ui ? rec.w2ui.editable : null);
if (edit === false) return null;
if (edit == null || edit === true) {
edit = (col ? col.editable : null);
if (typeof(edit) === 'function') {
var data = this.getCellValue(ind, col_ind, false);
// same arguments as col.render()
edit = edit.call(this, rec, ind, col_ind, data);
}
}
return edit;
},
getCellValue: function (ind, col_ind, summary) {
var col = this.columns[col_ind];
var record = (summary !== true ? this.records[ind] : this.summary[ind]);
var data = this.parseField(record, col.field);
if (record && record.w2ui && record.w2ui.changes && record.w2ui.changes[col.field] != null) {
data = record.w2ui.changes[col.field];
}
if ($.isPlainObject(data) /*&& col.editable*/) { //It can be an object btw
if (col.options && col.options.items) {
val=col.options.items.find(function(item){ return item.id==data.id});
if (val) data=val.text;
else data=data.id;
} else {
if (data.text != null) data = data.text;
if (data.id != null) data = data.id;
}
}
if (data == null) data = '';
return data;
},
getFooterHTML: function () {
return '<div>'+
' <div class="w2ui-footer-left"></div>'+
' <div class="w2ui-footer-right"></div>'+
' <div class="w2ui-footer-center"></div>'+
'</div>';
},
status: function (msg) {
if (msg != null) {
$('#grid_'+ this.name +'_footer').find('.w2ui-footer-left').html(msg);
} else {
// show number of selected
var msgLeft = '';
var sel = this.getSelection();
if (sel.length > 0) {
if (this.show.statusSelection && sel.length > 1) {
msgLeft = String(sel.length).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,") + ' ' + w2utils.lang('selected');
}
if (this.show.statusRecordID && sel.length == 1) {
var tmp = sel[0];
if (typeof tmp == 'object') tmp = tmp.recid + ', '+ w2utils.lang('Column') +': '+ tmp.column;
msgLeft = w2utils.lang('Record ID') + ': '+ tmp + ' ';
}
}
$('#grid_'+ this.name +'_footer .w2ui-footer-left').html(msgLeft);
// toolbar
if (sel.length == 1) this.toolbar.enable('w2ui-edit'); else this.toolbar.disable('w2ui-edit');
if (sel.length >= 1) this.toolbar.enable('w2ui-delete'); else this.toolbar.disable('w2ui-delete');
}
},
lock: function (msg, showSpinner) {
var obj = this;
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(this.box);
setTimeout(function () {
// hide empty msg if any
$(obj.box).find('#grid_'+ obj.name +'_empty_msg').remove();
w2utils.lock.apply(window, args);
}, 10);
},
unlock: function (speed) {
var box = this.box;
setTimeout(function () {
// do not unlock if there is a message
if ($(box).find('.w2ui-message').not('.w2ui-closing').length > 0) return;
w2utils.unlock(box, speed);
}, 25); // needed timer so if server fast, it will not flash
},
stateSave: function (returnOnly) {
var obj = this;
if (!w2utils.hasLocalStorage) return null;
var state = {
columns : [],
show : $.extend({}, this.show),
last : {
search : this.last.search,
multi : this.last.multi,
logic : this.last.logic,
label : this.last.label,
field : this.last.field,
scrollTop : this.last.scrollTop,
scrollLeft : this.last.scrollLeft
},
sortData : [],
searchData : []
};
var prop_val;
for (var i = 0; i < this.columns.length; i++) {
var col = obj.columns[i];
var col_save_obj = {};
// iterate properties to save
Object.keys(obj.stateColProps).forEach(function(prop, idx) {
if(obj.stateColProps[prop]){
// check if the property is defined on the column
if(col[prop] !== undefined){
prop_val = col[prop];
} else {
// use fallback or null
prop_val = obj.stateColDefaults[prop] || null;
}
col_save_obj[prop] = prop_val;
}
});
state.columns.push(col_save_obj);
}
for (var i = 0; i < this.sortData.length; i++) state.sortData.push($.extend({}, this.sortData[i]));
for (var i = 0; i < this.searchData.length; i++) state.searchData.push($.extend({}, this.searchData[i]));
// save into local storage
if (returnOnly !== true) {
// event before
var edata = this.trigger({ phase: 'before', type: 'stateSave', target: this.name, state: state });
if (edata.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
try {
var savedState = $.parseJSON(localStorage.w2ui || '{}');
if (!savedState) savedState = {};
if (!savedState.states) savedState.states = {};
savedState.states[(this.stateId || this.name)] = state;
localStorage.w2ui = JSON.stringify(savedState);
} catch (e) {
delete localStorage.w2ui;
return null;
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
return state;
},
stateRestore: function (newState) {
var obj = this;
var url = (typeof this.url != 'object' ? this.url : this.url.get);
if (!newState) {
// read it from local storage
try {
if (!w2utils.hasLocalStorage) return false;
var tmp = $.parseJSON(localStorage.w2ui || '{}');
if (!tmp) tmp = {};
if (!tmp.states) tmp.states = {};
newState = tmp.states[(this.stateId || this.name)];
} catch (e) {
delete localStorage.w2ui;
return null;
}
}
// event before
var edata = this.trigger({ phase: 'before', type: 'stateRestore', target: this.name, state: newState });
if (edata.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
// default behavior
if ($.isPlainObject(newState)) {
$.extend(this.show, newState.show);
$.extend(this.last, newState.last);
var sTop = this.last.scrollTop;
var sLeft = this.last.scrollLeft;
for (var c = 0; c < newState.columns.length; c++) {
var tmp = newState.columns[c];
var col_index = this.getColumn(tmp.field, true);
if (col_index !== null) {
$.extend(this.columns[col_index], tmp);
// restore column order from saved state
if (c !== col_index) this.columns.splice(c, 0, this.columns.splice(col_index, 1)[0]);
}
}
this.sortData.splice(0, this.sortData.length);
for (var c = 0; c < newState.sortData.length; c++) this.sortData.push(newState.sortData[c]);
this.searchData.splice(0, this.searchData.length);
for (var c = 0; c < newState.searchData.length; c++) this.searchData.push(newState.searchData[c]);
// apply sort and search
setTimeout(function () {
// needs timeout as records need to be populated
// ez 10.09.2014 this -->
if (!url) {
if (obj.sortData.length > 0) obj.localSort();
if (obj.searchData.length > 0) obj.localSearch();
}
obj.last.scrollTop = sTop;
obj.last.scrollLeft = sLeft;
obj.refresh();
}, 1);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return true;
},
stateReset: function () {
var obj = this;
this.stateRestore(this.last.state);
// remove from local storage
if (w2utils.hasLocalStorage) {
try {
var tmp = $.parseJSON(localStorage.w2ui || '{}');
if (tmp.states && tmp.states[(this.stateId || this.name)]) {
delete tmp.states[(this.stateId || this.name)];
}
localStorage.w2ui = JSON.stringify(tmp);
} catch (e) {
delete localStorage.w2ui;
return null;
}
}
},
parseField: function (obj, field) {
if (this.nestedFields) {
var val = '';
try { // need this to make sure no error in fields
val = obj;
var tmp = String(field).split('.');
for (var i = 0; i < tmp.length; i++) {
val = val[tmp[i]];
}
} catch (event) {
val = '';
}
return val;
} else {
return obj ? obj[field] : '';
}
},
prepareData: function () {
var obj = this;
// loops thru records and prepares date and time objects
for (var r = 0; r < this.records.length; r++) {
var rec = this.records[r];
prepareRecord(rec);
}
// prepare date and time objects for the 'rec' record and its closed children
function prepareRecord(rec) {
for (var c = 0; c < obj.columns.length; c++) {
var column = obj.columns[c];
if (rec[column.field] == null || typeof column.render != 'string') continue;
// number
if (['number', 'int', 'float', 'money', 'currency', 'percent'].indexOf(column.render.split(':')[0]) != -1) {
if (typeof rec[column.field] != 'number') rec[column.field] = parseFloat(rec[column.field]);
}
// date
if (['date', 'age'].indexOf(column.render.split(':')[0]) != -1) {
if (!rec[column.field + '_']) {
var dt = rec[column.field];
if (w2utils.isInt(dt)) dt = parseInt(dt);
rec[column.field + '_'] = new Date(dt);
}
}
// time
if (['time'].indexOf(column.render) != -1) {
if (w2utils.isTime(rec[column.field])) { // if string
var tmp = w2utils.isTime(rec[column.field], true);
var dt = new Date();
dt.setHours(tmp.hours, tmp.minutes, (tmp.seconds ? tmp.seconds : 0), 0); // sets hours, min, sec, mills
if (!rec[column.field + '_']) rec[column.field + '_'] = dt;
} else { // if date object
var tmp = rec[column.field];
if (w2utils.isInt(tmp)) tmp = parseInt(tmp);
var tmp = (tmp != null ? new Date(tmp) : new Date());
var dt = new Date();
dt.setHours(tmp.getHours(), tmp.getMinutes(), tmp.getSeconds(), 0); // sets hours, min, sec, mills
if (!rec[column.field + '_']) rec[column.field + '_'] = dt;
}
}
}
if (rec.w2ui && rec.w2ui.children && rec.w2ui.expanded !== true) {
// there are closed children, prepare them too.
for (var r = 0; r < rec.w2ui.children.length; r++) {
var subRec = rec.w2ui.children[r];
prepareRecord(subRec);
}
}
}
},
nextCell: function (index, col_ind, editable) {
var check = col_ind + 1;
if (check >= this.columns.length) return null;
var tmp = this.records[index].w2ui;
var ccol = this.columns[col_ind];
// if (tmp && tmp.colspan[ccol.field]) check += parseInt(tmp.colspan[ccol.field]) -1; // colspan of a column
var col = this.columns[check];
var span = (tmp && tmp.colspan && !isNaN(tmp.colspan[col.field]) ? parseInt(tmp.colspan[col.field]) : 1);
if (col == null) return null;
if (col && col.hidden || span === 0) return this.nextCell(index, check, editable);
if (editable) {
var edit = this.getCellEditable(index, col_ind);
if (edit == null || ['checkbox', 'check'].indexOf(edit.type) != -1) {
return this.nextCell(index, check, editable);
}
}
return check;
},
prevCell: function (index, col_ind, editable) {
var check = col_ind - 1;
if (check < 0) return null;
var tmp = this.records[index].w2ui;
var col = this.columns[check];
var span = (tmp && tmp.colspan && !isNaN(tmp.colspan[col.field]) ? parseInt(tmp.colspan[col.field]) : 1);
if (col == null) return null;
if (col && col.hidden || span === 0) return this.prevCell(index, check, editable);
if (editable) {
var edit = this.getCellEditable(index, col_ind);
if (edit == null || ['checkbox', 'check'].indexOf(edit.type) != -1) {
return this.prevCell(index, check, editable);
}
}
return check;
},
nextRow: function (ind, col_ind) {
var sids = this.last.searchIds;
var ret = null;
if ((ind + 1 < this.records.length && sids.length === 0) // if there are more records
|| (sids.length > 0 && ind < sids[sids.length-1])) {
ind++;
if (sids.length > 0) while (true) {
if ($.inArray(ind, sids) != -1 || ind > this.records.length) break;
ind++;
}
// colspan
var tmp = this.records[ind].w2ui;
var col = this.columns[col_ind];
var span = (tmp && tmp.colspan && col != null && !isNaN(tmp.colspan[col.field]) ? parseInt(tmp.colspan[col.field]) : 1);
if (span === 0) {
ret = this.nextRow(ind, col_ind);
} else {
ret = ind;
}
}
return ret;
},
prevRow: function (ind, col_ind) {
var sids = this.last.searchIds;
var ret = null;
if ((ind > 0 && sids.length === 0) // if there are more records
|| (sids.length > 0 && ind > sids[0])) {
ind--;
if (sids.length > 0) while (true) {
if ($.inArray(ind, sids) != -1 || ind < 0) break;
ind--;
}
// colspan
var tmp = this.records[ind].w2ui;
var col = this.columns[col_ind];
var span = (tmp && tmp.colspan && col != null && !isNaN(tmp.colspan[col.field]) ? parseInt(tmp.colspan[col.field]) : 1);
if (span === 0) {
ret = this.prevRow(ind, col_ind);
} else {
ret = ind;
}
}
return ret;
},
selectionSave: function () {
this.last._selection = this.getSelection();
return this.last._selection;
},
selectionRestore: function (noRefresh) {
var time = (new Date()).getTime();
this.last.selection = { indexes: [], columns: {} };
var sel = this.last.selection;
var lst = this.last._selection;
if (lst) for (var i = 0; i < lst.length; i++) {
if ($.isPlainObject(lst[i])) {
// selectType: cell
var tmp = this.get(lst[i].recid, true);
if (tmp != null) {
if (sel.indexes.indexOf(tmp) == -1) sel.indexes.push(tmp);
if (!sel.columns[tmp]) sel.columns[tmp] = [];
sel.columns[tmp].push(lst[i].column);
}
} else {
// selectType: row
var tmp = this.get(lst[i], true);
if (tmp != null) sel.indexes.push(tmp);
}
}
delete this.last._selection;
if (noRefresh !== true) this.refresh();
return (new Date()).getTime() - time;
},
message: function(options, callBack) {
if (typeof options == 'string') {
options = {
width : (options.length < 300 ? 350 : 550),
height : (options.length < 300 ? 170: 250),
body : '<div class="w2ui-centered">' + options + '</div>',
buttons : '<button type="button" class="w2ui-btn" onclick="w2ui[\''+ this.name +'\'].message()">Ok</button>',
onOpen : function (event) {
setTimeout(function () {
$(this.box).find('.w2ui-btn').focus();
}, 25);
},
onClose: function (even) {
if (typeof callBack == 'function') callBack();
}
};
}
w2utils.message.call(this, {
box : this.box,
path : 'w2ui.' + this.name,
title : '.w2ui-grid-header:visible',
body : '.w2ui-grid-box'
}, options);
}
}
$.extend(w2grid.prototype, w2utils.event);
w2obj.grid = w2grid;
})(jQuery);
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2layout - layout widget
* - $().w2layout - jQuery wrapper
* - Dependencies: jQuery, w2utils, w2toolbar, w2tabs
*
* == changes
* - negative values for left, right panel
* - onResize for layout as well as onResizing
* - panel.callBack - one time
* - layout.html().replaced(function () {})
*
* == NICE TO HAVE ==
* - onResize for the panel
* - add more panel title positions (left=rotated, right=rotated, bottom)
* - bug: when you assign content before previous transition completed.
*
************************************************************************/
(function ($) {
var w2layout = function (options) {
this.box = null; // DOM Element that holds the element
this.name = null; // unique name for w2ui
this.panels = [];
this.tmp = {};
this.padding = 1; // panel padding
this.resizer = 4; // resizer width or height
this.style = '';
$.extend(true, this, w2obj.layout, options);
};
var w2panels = ['top', 'left', 'main', 'preview', 'right', 'bottom'];
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2layout = function(method) {
if ($.isPlainObject(method)) {
// check name parameter
if (!w2utils.checkName(method, 'w2layout')) return;
var panels = method.panels || [];
var object = new w2layout(method);
$.extend(object, { handlers: [], panels: [] });
// add defined panels
for (var p = 0, len = panels.length; p < len; p++) {
object.panels[p] = $.extend(true, {}, w2layout.prototype.panel, panels[p]);
if ($.isPlainObject(object.panels[p].tabs) || $.isArray(object.panels[p].tabs)) initTabs(object, panels[p].type);
if ($.isPlainObject(object.panels[p].toolbar) || $.isArray(object.panels[p].toolbar)) initToolbar(object, panels[p].type);
}
// add all other panels
for (var p1 = 0; p1 < w2panels.length; p1++) {
if (object.get(w2panels[p1]) != null) continue;
object.panels.push($.extend(true, {}, w2layout.prototype.panel, { type: w2panels[p1], hidden: (w2panels[p1] !== 'main'), size: 50 }));
}
w2ui[object.name] = object;
if ($(this).length > 0) {
object.render($(this)[0]);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
function initTabs(object, panel, tabs) {
var pan = object.get(panel);
if (pan != null && tabs == null) tabs = pan.tabs;
if (pan == null || tabs == null) return false;
// instanciate tabs
if ($.isArray(tabs)) tabs = { tabs: tabs };
$().w2destroy(object.name + '_' + panel + '_tabs'); // destroy if existed
pan.tabs = $().w2tabs($.extend({}, tabs, { owner: object, name: object.name + '_' + panel + '_tabs' }));
pan.show.tabs = true;
return true;
}
function initToolbar(object, panel, toolbar) {
var pan = object.get(panel);
if (pan != null && toolbar == null) toolbar = pan.toolbar;
if (pan == null || toolbar == null) return false;
// instanciate toolbar
if ($.isArray(toolbar)) toolbar = { items: toolbar };
$().w2destroy(object.name + '_' + panel + '_toolbar'); // destroy if existed
pan.toolbar = $().w2toolbar($.extend({}, toolbar, { owner: object, name: object.name + '_' + panel + '_toolbar' }));
pan.show.toolbar = true;
return true;
}
};
// ====================================================
// -- Implementation of core functionality
w2layout.prototype = {
onShow : null,
onHide : null,
onResizing : null,
onResizerClick: null,
onRender : null,
onRefresh : null,
onContent : null,
onResize : null,
onDestroy : null,
// default setting for a panel
panel: {
type : null, // left, right, top, bottom
title : '',
size : 100, // width or height depending on panel name
minSize : 20,
maxSize : false,
hidden : false,
resizable : false,
overflow : 'auto',
style : '',
content : '', // can be String or Object with .render(box) method
tabs : null,
toolbar : null,
width : null, // read only
height : null, // read only
show : {
toolbar : false,
tabs : false
},
callBack : null, // function to call when content is overwritten
onRefresh : null,
onShow : null,
onHide : null
},
// alias for content
content: function (panel, data, transition) {
console.log('NOTICE: layout.content method is deprecated, please use layout.html() instead');
return this.html(panel, data, transition);
},
html: function (panel, data, transition) {
var obj = this;
var p = this.get(panel);
var promise = {
panel : panel,
html : p.content,
error : false,
cancelled : false,
removed : function (callBack) {
if (typeof callBack == 'function') {
p.callBack = callBack
}
}
}
if (typeof p.callBack == 'function') {
p.callBack({ panel: panel, content: p.content, new_content: data, transition: transition || 'none' });
p.callBack = null; // this is one time call back only
}
// if it is CSS panel
if (panel == 'css') {
$('#layout_'+ obj.name +'_panel_css').html('<style>'+ data +'</style>');
promise.status = true;
return promise;
}
if (p == null) {
console.log('ERROR: incorrect panel name. Panel name can be main, left, right, top, bottom, preview or css')
promise.error = true;
return promise;
}
if (data == null) {
return promise;
}
// event before
var edata = this.trigger({ phase: 'before', type: 'content', target: panel, object: p, content: data, transition: transition });
if (edata.isCancelled === true) {
promise.cancelled = true;
return promise;
}
if (data instanceof jQuery) {
console.log('ERROR: You can not pass jQuery object to w2layout.content() method');
return promise;
}
var pname = '#layout_'+ this.name + '_panel_'+ p.type;
var current = $(pname + '> .w2ui-panel-content');
var panelTop = 0;
if (current.length > 0) {
$(pname).scrollTop(0);
panelTop = $(current).position().top;
}
if (p.content === '') {
p.content = data;
this.refresh(panel);
} else {
p.content = data;
if (!p.hidden) {
if (transition != null && transition !== '') {
// apply transition
var div1 = $(pname + '> .w2ui-panel-content');
div1.after('<div class="w2ui-panel-content new-panel" style="'+ div1[0].style.cssText +'"></div>');
var div2 = $(pname + '> .w2ui-panel-content.new-panel');
div1.css('top', panelTop);
div2.css('top', panelTop);
if (typeof data == 'object') {
data.box = div2[0]; // do not do .render(box);
data.render();
} else {
div2.html(data);
}
w2utils.transition(div1[0], div2[0], transition, function () {
div1.remove();
div2.removeClass('new-panel');
div2.css('overflow', p.overflow);
// make sure only one content left
$(pname + '> .w2ui-panel-content').slice(1).remove()
// IE Hack
obj.resize();
if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
});
}
}
this.refresh(panel);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
// IE Hack
obj.resize();
if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
return promise;
},
message: function(panel, options) {
var obj = this;
if (typeof options == 'string') {
options = {
width : (options.length < 300 ? 350 : 550),
height : (options.length < 300 ? 170: 250),
body : '<div class="w2ui-centered">' + options + '</div>',
buttons : '<button class="w2ui-btn" onclick="w2ui[\''+ this.name +'\'].message(\''+ panel +'\')">Ok</button>',
onOpen : function (event) {
setTimeout(function () {
$(this.box).find('.w2ui-btn').focus();
}, 25);
}
};
}
var p = this.get(panel);
var $el = $('#layout_'+ this.name + '_panel_'+ p.type);
var oldOverflow = $el.css('overflow');
var oldOnClose;
if (options) {
if (options.onClose) oldOnClose = options.onClose;
options.onClose = function (event) {
if (typeof oldOnClose == 'function') oldOnClose(event);
event.done(function () {
$('#layout_'+ obj.name + '_panel_'+ p.type).css('overflow', oldOverflow);
});
};
}
$('#layout_'+ this.name + '_panel_'+ p.type).css('overflow', 'hidden');
w2utils.message.call(this, {
box : $('#layout_'+ this.name + '_panel_'+ p.type),
param : panel,
path : 'w2ui.' + this.name,
title : '.w2ui-panel-title:visible',
body : '.w2ui-panel-content'
}, options);
},
load: function (panel, url, transition, onLoad) {
var obj = this;
if (panel == 'css') {
$.get(url, function (data, status, xhr) { // should always be $.get as it is template
obj.html(panel, xhr.responseText);
if (onLoad) onLoad();
});
return true;
}
if (this.get(panel) != null) {
$.get(url, function (data, status, xhr) { // should always be $.get as it is template
obj.html(panel, xhr.responseText, transition);
if (onLoad) onLoad();
// IE Hack
obj.resize();
if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
});
return true;
}
return false;
},
sizeTo: function (panel, size, instant) {
var obj = this;
var pan = obj.get(panel);
if (pan == null) return false;
// resize
$(obj.box).find(' > div > .w2ui-panel')
.css(w2utils.cssPrefix('transition', (instant !== true ? '.2s' : '0s')));
setTimeout(function () {
obj.set(panel, { size: size });
}, 1);
// clean
setTimeout(function () {
$(obj.box).find(' > div > .w2ui-panel').css(w2utils.cssPrefix('transition', '0s'));
obj.resize();
}, 500);
return true;
},
show: function (panel, immediate) {
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', type: 'show', target: panel, object: this.get(panel), immediate: immediate });
if (edata.isCancelled === true) return;
var p = obj.get(panel);
if (p == null) return false;
p.hidden = false;
if (immediate === true) {
$('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '1' });
obj.trigger($.extend(edata, { phase: 'after' }));
obj.resize();
} else {
// resize
$('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
$(obj.box).find(' > div > .w2ui-panel').css(w2utils.cssPrefix('transition', '.2s'));
setTimeout(function () { obj.resize(); }, 1);
// show
setTimeout(function() {
$('#layout_'+ obj.name +'_panel_'+ panel).css({ 'opacity': '1' });
}, 250);
// clean
setTimeout(function () {
$(obj.box).find(' > div > .w2ui-panel').css(w2utils.cssPrefix('transition', '0s'));
obj.trigger($.extend(edata, { phase: 'after' }));
obj.resize();
}, 500);
}
return true;
},
hide: function (panel, immediate) {
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', type: 'hide', target: panel, object: this.get(panel), immediate: immediate });
if (edata.isCancelled === true) return;
var p = obj.get(panel);
if (p == null) return false;
p.hidden = true;
if (immediate === true) {
$('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
obj.trigger($.extend(edata, { phase: 'after' }));
obj.resize();
} else {
// hide
$(obj.box).find(' > div > .w2ui-panel').css(w2utils.cssPrefix('transition', '.2s'));
$('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
setTimeout(function () { obj.resize(); }, 1);
// clean
setTimeout(function () {
$(obj.box).find(' > div > .w2ui-panel').css(w2utils.cssPrefix('transition', '0s'));
obj.trigger($.extend(edata, { phase: 'after' }));
obj.resize();
}, 500);
}
return true;
},
toggle: function (panel, immediate) {
var p = this.get(panel);
if (p == null) return false;
if (p.hidden) return this.show(panel, immediate); else return this.hide(panel, immediate);
},
set: function (panel, options) {
var ind = this.get(panel, true);
if (ind == null) return false;
$.extend(this.panels[ind], options);
// refresh only when content changed
if (options.content != null || options.resizable != null) {
this.refresh(panel);
}
// show/hide resizer
this.resize(); // resize is needed when panel size is changed
return true;
},
get: function (panel, returnIndex) {
for (var p = 0; p < this.panels.length; p++) {
if (this.panels[p].type == panel) {
if (returnIndex === true) return p; else return this.panels[p];
}
}
return null;
},
el: function (panel) {
var el = $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-content');
if (el.length != 1) return null;
return el[0];
},
hideToolbar: function (panel) {
var pan = this.get(panel);
if (!pan) return;
pan.show.toolbar = false;
$('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-toolbar').hide();
this.resize();
},
showToolbar: function (panel) {
var pan = this.get(panel);
if (!pan) return;
pan.show.toolbar = true;
$('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-toolbar').show();
this.resize();
},
toggleToolbar: function (panel) {
var pan = this.get(panel);
if (!pan) return;
if (pan.show.toolbar) this.hideToolbar(panel); else this.showToolbar(panel);
},
assignToolbar: function (panel, toolbar) {
if (typeof toolbar == 'string' && w2ui[toolbar] != null) toolbar = w2ui[toolbar];
var pan = this.get(panel);
pan.toolbar = toolbar;
var tmp = $(this.box).find(panel +'> .w2ui-panel-toolbar');
if (pan.toolbar != null) {
if (tmp.find('[name='+ pan.toolbar.name +']').length === 0) {
tmp.w2render(pan.toolbar);
} else if (pan.toolbar != null) {
pan.toolbar.refresh();
}
toolbar.owner = this;
this.showToolbar(panel);
this.refresh(panel);
} else {
tmp.html('');
this.hideToolbar(panel);
}
},
hideTabs: function (panel) {
var pan = this.get(panel);
if (!pan) return;
pan.show.tabs = false;
$('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-tabs').hide();
this.resize();
},
showTabs: function (panel) {
var pan = this.get(panel);
if (!pan) return;
pan.show.tabs = true;
$('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-tabs').show();
this.resize();
},
toggleTabs: function (panel) {
var pan = this.get(panel);
if (!pan) return;
if (pan.show.tabs) this.hideTabs(panel); else this.showTabs(panel);
},
render: function (box) {
var obj = this;
// if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
var time = (new Date()).getTime();
// event before
var edata = obj.trigger({ phase: 'before', type: 'render', target: obj.name, box: box });
if (edata.isCancelled === true) return;
if (box != null) {
if ($(obj.box).find('#layout_'+ obj.name +'_panel_main').length > 0) {
$(obj.box)
.removeAttr('name')
.removeClass('w2ui-layout')
.html('');
}
obj.box = box;
}
if (!obj.box) return false;
$(obj.box)
.attr('name', obj.name)
.addClass('w2ui-layout')
.html('<div></div>');
if ($(obj.box).length > 0) $(obj.box)[0].style.cssText += obj.style;
// create all panels
for (var p1 = 0; p1 < w2panels.length; p1++) {
var pan = obj.get(w2panels[p1]);
var html = '<div id="layout_'+ obj.name + '_panel_'+ w2panels[p1] +'" class="w2ui-panel">'+
' <div class="w2ui-panel-title"></div>'+
' <div class="w2ui-panel-tabs"></div>'+
' <div class="w2ui-panel-toolbar"></div>'+
' <div class="w2ui-panel-content"></div>'+
'</div>'+
'<div id="layout_'+ obj.name + '_resizer_'+ w2panels[p1] +'" class="w2ui-resizer"></div>';
$(obj.box).find(' > div').append(html);
// tabs are rendered in refresh()
}
$(obj.box).find(' > div')
.append('<div id="layout_'+ obj.name + '_panel_css" style="position: absolute; top: 10000px;"></div>');
obj.refresh(); // if refresh is not called here, the layout will not be available right after initialization
// process event
obj.trigger($.extend(edata, { phase: 'after' }));
// reinit events
setTimeout(function () { // needed this timeout to allow browser to render first if there are tabs or toolbar
initEvents();
obj.resize();
}, 0);
return (new Date()).getTime() - time;
function initEvents() {
obj.tmp.events = {
resize : function (event) {
if (w2ui[obj.name] == null) {
$(window).off('resize.w2ui-'+ obj.name);
} else {
w2ui[obj.name].resize();
}
},
resizeStart : resizeStart,
mouseMove : resizeMove,
mouseUp : resizeStop
};
$(window).on('resize.w2ui-'+ obj.name, obj.tmp.events.resize);
}
function resizeStart(type, evnt) {
if (!obj.box) return;
if (!evnt) evnt = window.event;
$(document).off('mousemove', obj.tmp.events.mouseMove).on('mousemove', obj.tmp.events.mouseMove);
$(document).off('mouseup', obj.tmp.events.mouseUp).on('mouseup', obj.tmp.events.mouseUp);
obj.tmp.resize = {
type : type,
x : evnt.screenX,
y : evnt.screenY,
diff_x : 0,
diff_y : 0,
value : 0
};
// lock all panels
for (var p1 = 0; p1 < w2panels.length; p1++) {
var $tmp = $(obj.el(w2panels[p1])).parent().find('.w2ui-lock');
if ($tmp.length > 0) {
$tmp.attr('locked', 'previous');
} else {
obj.lock(w2panels[p1], { opacity: 0 });
}
}
if (type == 'left' || type == 'right') {
obj.tmp.resize.value = parseInt($('#layout_'+ obj.name +'_resizer_'+ type)[0].style.left);
}
if (type == 'top' || type == 'preview' || type == 'bottom') {
obj.tmp.resize.value = parseInt($('#layout_'+ obj.name +'_resizer_'+ type)[0].style.top);
}
}
function resizeStop(evnt) {
if (!obj.box) return;
if (!evnt) evnt = window.event;
$(document).off('mousemove', obj.tmp.events.mouseMove);
$(document).off('mouseup', obj.tmp.events.mouseUp);
if (obj.tmp.resize == null) return;
// unlock all panels
for (var p1 = 0; p1 < w2panels.length; p1++) {
var $tmp = $(obj.el(w2panels[p1])).parent().find('.w2ui-lock');
if ($tmp.attr('locked') == 'previous') {
$tmp.removeAttr('locked');
} else {
obj.unlock(w2panels[p1]);
}
}
// set new size
if (obj.tmp.diff_x !== 0 || obj.tmp.resize.diff_y !== 0) { // only recalculate if changed
var ptop = obj.get('top');
var pbottom = obj.get('bottom');
var panel = obj.get(obj.tmp.resize.type);
var height = parseInt($(obj.box).height());
var width = parseInt($(obj.box).width());
var str = String(panel.size);
var ns, nd;
switch (obj.tmp.resize.type) {
case 'top':
ns = parseInt(panel.sizeCalculated) + obj.tmp.resize.diff_y;
nd = 0;
break;
case 'bottom':
ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_y;
nd = 0;
break;
case 'preview':
ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_y;
nd = (ptop && !ptop.hidden ? ptop.sizeCalculated : 0) +
(pbottom && !pbottom.hidden ? pbottom.sizeCalculated : 0);
break;
case 'left':
ns = parseInt(panel.sizeCalculated) + obj.tmp.resize.diff_x;
nd = 0;
break;
case 'right':
ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_x;
nd = 0;
break;
}
// set size
if (str.substr(str.length-1) == '%') {
panel.size = Math.floor(ns * 100 / (panel.type == 'left' || panel.type == 'right' ? width : height - nd) * 100) / 100 + '%';
} else {
if (String(panel.size).substr(0, 1) == '-') {
panel.size = parseInt(panel.size) - panel.sizeCalculated + ns;
} else {
panel.size = ns;
}
}
obj.resize();
}
$('#layout_'+ obj.name + '_resizer_'+ obj.tmp.resize.type).removeClass('active');
delete obj.tmp.resize;
}
function resizeMove(evnt) {
if (!obj.box) return;
if (!evnt) evnt = window.event;
if (obj.tmp.resize == null) return;
var panel = obj.get(obj.tmp.resize.type);
// event before
var tmp = obj.tmp.resize;
var edata = obj.trigger({ phase: 'before', type: 'resizing', target: obj.name, object: panel, originalEvent: evnt,
panel: tmp ? tmp.type : 'all', diff_x: tmp ? tmp.diff_x : 0, diff_y: tmp ? tmp.diff_y : 0 });
if (edata.isCancelled === true) return;
var p = $('#layout_'+ obj.name + '_resizer_'+ tmp.type);
var resize_x = (evnt.screenX - tmp.x);
var resize_y = (evnt.screenY - tmp.y);
var mainPanel = obj.get('main');
if (!p.hasClass('active')) p.addClass('active');
switch (tmp.type) {
case 'left':
if (panel.minSize - resize_x > panel.width) {
resize_x = panel.minSize - panel.width;
}
if (panel.maxSize && (panel.width + resize_x > panel.maxSize)) {
resize_x = panel.maxSize - panel.width;
}
if (mainPanel.minSize + resize_x > mainPanel.width) {
resize_x = mainPanel.width - mainPanel.minSize;
}
break;
case 'right':
if (panel.minSize + resize_x > panel.width) {
resize_x = panel.width - panel.minSize;
}
if (panel.maxSize && (panel.width - resize_x > panel.maxSize)) {
resize_x = panel.width - panel.maxSize;
}
if (mainPanel.minSize - resize_x > mainPanel.width) {
resize_x = mainPanel.minSize - mainPanel.width;
}
break;
case 'top':
if (panel.minSize - resize_y > panel.height) {
resize_y = panel.minSize - panel.height;
}
if (panel.maxSize && (panel.height + resize_y > panel.maxSize)) {
resize_y = panel.maxSize - panel.height;
}
if (mainPanel.minSize + resize_y > mainPanel.height) {
resize_y = mainPanel.height - mainPanel.minSize;
}
break;
case 'preview':
case 'bottom':
if (panel.minSize + resize_y > panel.height) {
resize_y = panel.height - panel.minSize;
}
if (panel.maxSize && (panel.height - resize_y > panel.maxSize)) {
resize_y = panel.height - panel.maxSize;
}
if (mainPanel.minSize - resize_y > mainPanel.height) {
resize_y = mainPanel.minSize - mainPanel.height;
}
break;
}
tmp.diff_x = resize_x;
tmp.diff_y = resize_y;
switch (tmp.type) {
case 'top':
case 'preview':
case 'bottom':
tmp.diff_x = 0;
if (p.length > 0) p[0].style.top = (tmp.value + tmp.diff_y) + 'px';
break;
case 'left':
case 'right':
tmp.diff_y = 0;
if (p.length > 0) p[0].style.left = (tmp.value + tmp.diff_x) + 'px';
break;
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}
},
refresh: function (panel) {
var obj = this;
// if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
if (panel == null) panel = null;
var time = (new Date()).getTime();
// event before
var edata = obj.trigger({ phase: 'before', type: 'refresh', target: (panel != null ? panel : obj.name), object: obj.get(panel) });
if (edata.isCancelled === true) return;
// obj.unlock(panel);
if (typeof panel == 'string') {
var p = obj.get(panel);
if (p == null) return;
var pname = '#layout_'+ obj.name + '_panel_'+ p.type;
var rname = '#layout_'+ obj.name +'_resizer_'+ p.type;
// apply properties to the panel
$(pname).css({ display: p.hidden ? 'none' : 'block' });
if (p.resizable) $(rname).show(); else $(rname).hide();
// insert content
if (typeof p.content == 'object' && typeof p.content.render === 'function') {
p.content.box = $(pname +'> .w2ui-panel-content')[0];
setTimeout(function () {
// need to remove unnecessary classes
if ($(pname +'> .w2ui-panel-content').length > 0) {
$(pname +'> .w2ui-panel-content')
.removeClass()
.removeAttr('name')
.addClass('w2ui-panel-content')
.css('overflow', p.overflow)[0].style.cssText += ';' + p.style;
}
if (p.content && typeof p.content.render == 'function') {
p.content.render(); // do not do .render(box);
}
}, 1);
} else {
// need to remove unnecessary classes
if ($(pname +'> .w2ui-panel-content').length > 0) {
$(pname +'> .w2ui-panel-content')
.removeClass()
.removeAttr('name')
.addClass('w2ui-panel-content')
.html(p.content)
.css('overflow', p.overflow)[0].style.cssText += ';' + p.style;
}
}
// if there are tabs and/or toolbar - render it
var tmp = $(obj.box).find(pname +'> .w2ui-panel-tabs');
if (p.show.tabs) {
if (tmp.find('[name='+ p.tabs.name +']').length === 0 && p.tabs != null) tmp.w2render(p.tabs); else p.tabs.refresh();
} else {
tmp.html('').removeClass('w2ui-tabs').hide();
}
tmp = $(obj.box).find(pname +'> .w2ui-panel-toolbar');
if (p.show.toolbar) {
if (tmp.find('[name='+ p.toolbar.name +']').length === 0 && p.toolbar != null) tmp.w2render(p.toolbar); else p.toolbar.refresh();
} else {
tmp.html('').removeClass('w2ui-toolbar').hide();
}
// show title
tmp = $(obj.box).find(pname +'> .w2ui-panel-title');
if (p.title) {
tmp.html(p.title).show();
} else {
tmp.html('').hide();
}
} else {
if ($('#layout_'+ obj.name +'_panel_main').length === 0) {
obj.render();
return;
}
obj.resize();
// refresh all of them
for (var p1 = 0; p1 < this.panels.length; p1++) { obj.refresh(this.panels[p1].type); }
}
obj.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
resize: function () {
// if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
if (!this.box) return false;
var time = (new Date()).getTime();
// event before
var tmp = this.tmp.resize;
var edata = this.trigger({ phase: 'before', type: 'resize', target: this.name,
panel: tmp ? tmp.type : 'all', diff_x: tmp ? tmp.diff_x : 0, diff_y: tmp ? tmp.diff_y : 0 });
if (edata.isCancelled === true) return;
if (this.padding < 0) this.padding = 0;
// layout itself
var width = parseInt($(this.box).width());
var height = parseInt($(this.box).height());
$(this.box).find(' > div').css({
width : width + 'px',
height : height + 'px'
});
var obj = this;
// panels
var pmain = this.get('main');
var pprev = this.get('preview');
var pleft = this.get('left');
var pright = this.get('right');
var ptop = this.get('top');
var pbottom = this.get('bottom');
var smain = true; // main always on
var sprev = (pprev != null && pprev.hidden !== true ? true : false);
var sleft = (pleft != null && pleft.hidden !== true ? true : false);
var sright = (pright != null && pright.hidden !== true ? true : false);
var stop = (ptop != null && ptop.hidden !== true ? true : false);
var sbottom = (pbottom != null && pbottom.hidden !== true ? true : false);
var l, t, w, h, e;
// calculate %
for (var p = 0; p < w2panels.length; p++) {
if (w2panels[p] === 'main') continue;
tmp = this.get(w2panels[p]);
if (!tmp) continue;
var str = String(tmp.size || 0);
if (str.substr(str.length-1) == '%') {
var tmph = height;
if (tmp.type == 'preview') {
tmph = tmph -
(ptop && !ptop.hidden ? ptop.sizeCalculated : 0) -
(pbottom && !pbottom.hidden ? pbottom.sizeCalculated : 0);
}
tmp.sizeCalculated = parseInt((tmp.type == 'left' || tmp.type == 'right' ? width : tmph) * parseFloat(tmp.size) / 100);
} else {
tmp.sizeCalculated = parseInt(tmp.size);
}
tmp.sizeCalculated = Math.max(tmp.sizeCalculated, parseInt(tmp.minSize));
}
// negative size
if (String(pright.size).substr(0, 1) == '-') {
if (sleft && String(pleft.size).substr(0, 1) == '-') {
console.log('ERROR: you cannot have both left panel.size and right panel.size be negative.');
} else {
pright.sizeCalculated = width - (sleft ? pleft.sizeCalculated : 0) + parseInt(pright.size);
}
}
if (String(pleft.size).substr(0, 1) == '-') {
if (sright && pright.size.substr(0, 1) == '-') {
console.log('ERROR: you cannot have both left panel.size and right panel.size be negative.');
} else {
pleft.sizeCalculated = width - (sright ? pright.sizeCalculated : 0) + parseInt(pleft.size);
}
}
// top if any
if (ptop != null && ptop.hidden !== true) {
l = 0;
t = 0;
w = width;
h = ptop.sizeCalculated;
$('#layout_'+ this.name +'_panel_top').css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
}).show();
ptop.width = w;
ptop.height = h;
// resizer
if (ptop.resizable) {
t = ptop.sizeCalculated - (this.padding === 0 ? this.resizer : 0);
h = (this.resizer > this.padding ? this.resizer : this.padding);
$('#layout_'+ this.name +'_resizer_top').show().css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px',
'cursor': 'ns-resize'
}).off('mousedown').on('mousedown', function (event) {
// event before
var edata = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'top', originalEvent: event });
if (edata.isCancelled === true) return;
// default action
w2ui[obj.name].tmp.events.resizeStart('top', event);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
return false;
});
}
} else {
$('#layout_'+ this.name +'_panel_top').hide();
$('#layout_'+ this.name +'_resizer_top').hide();
}
// left if any
if (pleft != null && pleft.hidden !== true) {
l = 0;
t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
w = pleft.sizeCalculated;
h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
(sbottom ? pbottom.sizeCalculated + this.padding : 0);
e = $('#layout_'+ this.name +'_panel_left');
if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
e.css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
}).show();
pleft.width = w;
pleft.height = h;
// resizer
if (pleft.resizable) {
l = pleft.sizeCalculated - (this.padding === 0 ? this.resizer : 0);
w = (this.resizer > this.padding ? this.resizer : this.padding);
$('#layout_'+ this.name +'_resizer_left').show().css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px',
'cursor': 'ew-resize'
}).off('mousedown').on('mousedown', function (event) {
// event before
var edata = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'left', originalEvent: event });
if (edata.isCancelled === true) return;
// default action
w2ui[obj.name].tmp.events.resizeStart('left', event);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
return false;
});
}
} else {
$('#layout_'+ this.name +'_panel_left').hide();
$('#layout_'+ this.name +'_resizer_left').hide();
}
// right if any
if (pright != null && pright.hidden !== true) {
l = width - pright.sizeCalculated;
t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
w = pright.sizeCalculated;
h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
(sbottom ? pbottom.sizeCalculated + this.padding : 0);
$('#layout_'+ this.name +'_panel_right').css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
}).show();
pright.width = w;
pright.height = h;
// resizer
if (pright.resizable) {
l = l - this.padding;
w = (this.resizer > this.padding ? this.resizer : this.padding);
$('#layout_'+ this.name +'_resizer_right').show().css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px',
'cursor': 'ew-resize'
}).off('mousedown').on('mousedown', function (event) {
// event before
var edata = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'right', originalEvent: event });
if (edata.isCancelled === true) return;
// default action
w2ui[obj.name].tmp.events.resizeStart('right', event);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
return false;
});
}
} else {
$('#layout_'+ this.name +'_panel_right').hide();
$('#layout_'+ this.name +'_resizer_right').hide();
}
// bottom if any
if (pbottom != null && pbottom.hidden !== true) {
l = 0;
t = height - pbottom.sizeCalculated;
w = width;
h = pbottom.sizeCalculated;
$('#layout_'+ this.name +'_panel_bottom').css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
}).show();
pbottom.width = w;
pbottom.height = h;
// resizer
if (pbottom.resizable) {
t = t - (this.padding === 0 ? 0 : this.padding);
h = (this.resizer > this.padding ? this.resizer : this.padding);
$('#layout_'+ this.name +'_resizer_bottom').show().css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px',
'cursor': 'ns-resize'
}).off('mousedown').on('mousedown', function (event) {
// event before
var edata = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'bottom', originalEvent: event });
if (edata.isCancelled === true) return;
// default action
w2ui[obj.name].tmp.events.resizeStart('bottom', event);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
return false;
});
}
} else {
$('#layout_'+ this.name +'_panel_bottom').hide();
$('#layout_'+ this.name +'_resizer_bottom').hide();
}
// main - always there
l = 0 + (sleft ? pleft.sizeCalculated + this.padding : 0);
t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
w = width - (sleft ? pleft.sizeCalculated + this.padding : 0) -
(sright ? pright.sizeCalculated + this.padding: 0);
h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
(sbottom ? pbottom.sizeCalculated + this.padding : 0) -
(sprev ? pprev.sizeCalculated + this.padding : 0);
e = $('#layout_'+ this.name +'_panel_main');
if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
e.css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
});
pmain.width = w;
pmain.height = h;
// preview if any
if (pprev != null && pprev.hidden !== true) {
l = 0 + (sleft ? pleft.sizeCalculated + this.padding : 0);
t = height - (sbottom ? pbottom.sizeCalculated + this.padding : 0) - pprev.sizeCalculated;
w = width - (sleft ? pleft.sizeCalculated + this.padding : 0) -
(sright ? pright.sizeCalculated + this.padding : 0);
h = pprev.sizeCalculated;
e = $('#layout_'+ this.name +'_panel_preview');
if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
e.css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px'
}).show();
pprev.width = w;
pprev.height = h;
// resizer
if (pprev.resizable) {
t = t - (this.padding === 0 ? 0 : this.padding);
h = (this.resizer > this.padding ? this.resizer : this.padding);
$('#layout_'+ this.name +'_resizer_preview').show().css({
'display': 'block',
'left': l + 'px',
'top': t + 'px',
'width': w + 'px',
'height': h + 'px',
'cursor': 'ns-resize'
}).off('mousedown').on('mousedown', function (event) {
// event before
var edata = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'preview', originalEvent: event });
if (edata.isCancelled === true) return;
// default action
w2ui[obj.name].tmp.events.resizeStart('preview', event);
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
return false;
});
}
} else {
$('#layout_'+ this.name +'_panel_preview').hide();
$('#layout_'+ this.name +'_resizer_preview').hide();
}
// display tabs and toolbar if needed
for (var p1 = 0; p1 < w2panels.length; p1++) {
var pan = this.get(w2panels[p1]);
var tmp2 = '#layout_'+ this.name +'_panel_'+ w2panels[p1] +' > .w2ui-panel-';
var tabHeight = 0;
if (pan) {
if (pan.title) {
tabHeight += w2utils.getSize($(tmp2 + 'title').css({ top: tabHeight + 'px', display: 'block' }), 'height');
}
if (pan.show.tabs) {
if (pan.tabs != null && w2ui[this.name +'_'+ w2panels[p1] +'_tabs']) w2ui[this.name +'_'+ w2panels[p1] +'_tabs'].resize();
tabHeight += w2utils.getSize($(tmp2 + 'tabs').css({ top: tabHeight + 'px', display: 'block' }), 'height');
}
if (pan.show.toolbar) {
if (pan.toolbar != null && w2ui[this.name +'_'+ w2panels[p1] +'_toolbar']) w2ui[this.name +'_'+ w2panels[p1] +'_toolbar'].resize();
tabHeight += w2utils.getSize($(tmp2 + 'toolbar').css({ top: tabHeight + 'px', display: 'block' }), 'height');
}
}
$(tmp2 + 'content').css({ display: 'block' }).css({ top: tabHeight + 'px' });
}
// send resize to all objects
clearTimeout(this._resize_timer);
this._resize_timer = setTimeout(function () {
for (var e in w2ui) {
if (typeof w2ui[e].resize == 'function') {
// sent to all none-layouts
if (w2ui[e].panels == null) w2ui[e].resize();
// only send to nested layouts
var parent = $(w2ui[e].box).parents('.w2ui-layout');
if (parent.length > 0 && parent.attr('name') == obj.name) w2ui[e].resize();
}
}
}, 100);
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
if (edata.isCancelled === true) return;
if (w2ui[this.name] == null) return false;
// clean up
if ($(this.box).find('#layout_'+ this.name +'_panel_main').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-layout')
.html('');
}
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
if (this.tmp.events && this.tmp.events.resize) $(window).off('resize', this.tmp.events.resize);
return true;
},
lock: function (panel, msg, showSpinner) {
if (w2panels.indexOf(panel) == -1) {
console.log('ERROR: First parameter needs to be the a valid panel name.');
return;
}
var args = Array.prototype.slice.call(arguments, 0);
args[0] = '#layout_'+ this.name + '_panel_' + panel;
w2utils.lock.apply(window, args);
},
unlock: function (panel, speed) {
if (w2panels.indexOf(panel) == -1) {
console.log('ERROR: First parameter needs to be the a valid panel name.');
return;
}
var nm = '#layout_'+ this.name + '_panel_' + panel;
w2utils.unlock(nm, speed);
}
};
$.extend(w2layout.prototype, w2utils.event);
w2obj.layout = w2layout;
})(jQuery);
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2popup - popup widget
* - $().w2popup - jQuery wrapper
* - Dependencies: jQuery, w2utils
*
* == changes
* - added onMove event
* - w2prompt.options.ok_class, cancel_class
* - w2confirm.options.onOpen, w2confirm.options.onClose
* - w2prompt.options.onOpen, w2prompt.options.onClose
* - w2popup.actions, w2popup.action, w2popup.onAction
* - w2popup.onMsgOpen, w2popup.onMsgClose
* - options.multiple
*
* == NICE TO HAVE ==
* - hide overlay on esc
* - make popup width/height in %
*
************************************************************************/
var w2popup = {};
(function ($) {
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2popup = function(method, options) {
if (method == null) {
options = {};
method = 'open';
}
if ($.isPlainObject(method)) {
options = method;
method = 'open';
}
method = method.toLowerCase();
if (method === 'load' && typeof options === 'string') {
options = $.extend({ url: options }, arguments.length > 2 ? arguments[2] : {});
}
if (method === 'open' && options.url != null) method = 'load';
options = options || {};
// load options from markup
var dlgOptions = {};
if ($(this).length > 0 && method == 'open') {
if ($(this).find('div[rel=title], div[rel=body], div[rel=buttons]').length > 0) {
// remember previous tempalte
if ($('#w2ui-popup').length > 0) {
var tmp = $('#w2ui-popup').data('options');
w2popup._prev = {
template : w2popup._template,
title : tmp.title,
body : tmp.body,
buttons : tmp.buttons
};
}
w2popup._template = this;
if ($(this).find('div[rel=title]').length > 0) {
dlgOptions['title'] = $(this).find('div[rel=title]');
}
if ($(this).find('div[rel=body]').length > 0) {
dlgOptions['body'] = $(this).find('div[rel=body]');
dlgOptions['style'] = $(this).find('div[rel=body]')[0].style.cssText;
}
if ($(this).find('div[rel=buttons]').length > 0) {
dlgOptions['buttons'] = $(this).find('div[rel=buttons]');
}
} else {
dlgOptions['title'] = '&#160;';
dlgOptions['body'] = $(this).html();
}
if (parseInt($(this).css('width')) !== 0) dlgOptions['width'] = parseInt($(this).css('width'));
//if the popup will have a title bar, we must add the height of title bar to the popup height
var hasTitlebar = options.title || (options.showClose || options.showClose === undefined) || (options.showMax || options.showMax === undefined) ;
if (parseInt($(this).css('height')) !== 0) dlgOptions['height'] = parseInt($(this).css('height')) + (hasTitlebar?32:0);
}
// show popup
return w2popup[method]($.extend({}, dlgOptions, options));
};
// ====================================================
// -- Implementation of core functionality (SINGLETON)
w2popup = {
defaults: {
title : '',
body : '',
buttons : '',
style : '',
color : '#000',
opacity : 0.4,
speed : 0.3,
modal : false,
maximized : false,
keyboard : true, // will close popup on esc if not modal
width : 500,
height : 300,
showClose : true,
showMax : false,
transition: null,
multiple : false // if popup already open, opens as a message
},
status : 'closed', // string that describes current status
handlers : [],
onOpen : null,
onClose : null,
onMax : null,
onMin : null,
onToggle : null,
onKeydown : null,
open: function (options) {
var obj = this;
var orig_options = $.extend(true, {}, options);
if (w2popup.status == 'closing') {
setTimeout(function () { obj.open.call(obj, options); }, 100);
return;
}
// get old options and merge them
var old_options = $('#w2ui-popup').data('options');
var options = $.extend({}, this.defaults, old_options, { title: '', body : '', buttons: '' }, options, { maximized: false });
// need timer because popup might not be open
setTimeout(function () { $('#w2ui-popup').data('options', options); }, 100);
// if new - reset event handlers
if ($('#w2ui-popup').length === 0) {
// w2popup.handlers = []; // if commented, allows to add w2popup.on() for all
w2popup.onMax = null;
w2popup.onMin = null;
w2popup.onToggle = null;
w2popup.onOpen = null;
w2popup.onClose = null;
w2popup.onKeydown = null;
w2popup.onAction = null;
}
if (options.onOpen) w2popup.onOpen = options.onOpen;
if (options.onClose) w2popup.onClose = options.onClose;
if (options.onMax) w2popup.onMax = options.onMax;
if (options.onMin) w2popup.onMin = options.onMin;
if (options.onToggle) w2popup.onToggle = options.onToggle;
if (options.onKeydown) w2popup.onKeydown = options.onKeydown;
if (options.onAction) w2popup.onAction = options.onAction;
options.width = parseInt(options.width);
options.height = parseInt(options.height);
var maxW, maxH;
if (window.innerHeight == undefined) {
maxW = parseInt(document.documentElement.offsetWidth);
maxH = parseInt(document.documentElement.offsetHeight);
if (w2utils.engine === 'IE7') { maxW += 21; maxH += 4; }
} else {
maxW = parseInt(window.innerWidth);
maxH = parseInt(window.innerHeight);
}
if (maxW - 10 < options.width) options.width = maxW - 10;
if (maxH - 10 < options.height) options.height = maxH - 10;
var top = (maxH - options.height) / 2 * 0.6;
var left = (maxW - options.width) / 2;
// convert action arrays into buttons
if (options.actions != null) {
options.buttons = '';
Object.keys(options.actions).forEach(function (action) {
var handler = options.actions[action];
if (typeof handler == 'function') {
options.buttons += '<button class="w2ui-btn" onclick="w2popup.action(\''+ action +'\')">'+ action +'</button>'
}
if (typeof handler == 'object') {
options.buttons += '<button class="w2ui-btn '+ (handler['class'] || '') +'" style="'+ (handler['style'] || '') +'"'
+ 'onclick="w2popup.action(\''+ action +'\')">'+ (handler['text'] || action) +'</button>'
}
if (typeof handler == 'string') {
options.buttons += handler
}
});
}
// check if message is already displayed
if ($('#w2ui-popup').length === 0) {
// trigger event
var edata = this.trigger({ phase: 'before', type: 'open', target: 'popup', options: options, present: false });
if (edata.isCancelled === true) return;
w2popup.status = 'opening';
// output message
w2popup.lockScreen(options);
var btn = '';
if (options.showClose) {
btn += '<div class="w2ui-popup-button w2ui-popup-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>';
}
if (options.showMax) {
btn += '<div class="w2ui-popup-button w2ui-popup-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>';
}
// first insert just body
var msg = '<div id="w2ui-popup" class="w2ui-popup w2ui-popup-opening" style="left: '+ left +'px; top: '+ top +'px;'+
' width: ' + parseInt(options.width) + 'px; height: ' + parseInt(options.height) + 'px;"></div>';
$('body').append(msg);
// parse rel=*
var parts = $('#w2ui-popup');
if (parts.find('div[rel=title], div[rel=body], div[rel=buttons]').length > 0) {
// title
var tmp = parts.find('div[rel=title]');
if (tmp.length > 0) { options.title = tmp.html(); tmp.remove(); }
// buttons
var tmp = parts.find('div[rel=buttons]');
if (tmp.length > 0) { options.buttons = tmp.html(); tmp.remove(); }
// body
var tmp = parts.find('div[rel=body]');
if (tmp.length > 0) options.body = tmp.html(); else options.body = parts.html();
}
// then content
var msg = '<div class="w2ui-popup-title" style="'+ (!options.title ? 'display: none' : '') +'">' + btn + '</div>'+
'<div class="w2ui-box" style="'+ (!options.title ? 'top: 0px !important;' : '') +
(!options.buttons ? 'bottom: 0px !important;' : '') + '">'+
' <div class="w2ui-popup-body' + (!options.title ? ' w2ui-popup-no-title' : '') +
(!options.buttons ? ' w2ui-popup-no-buttons' : '') + '" style="' + options.style + '">' +
' </div>'+
'</div>'+
'<div class="w2ui-popup-buttons" style="'+ (!options.buttons ? 'display: none' : '') +'"></div>'+
'<input class="w2ui-popup-hidden" style="position: absolute; top: -100px"/>'; // this is needed to keep focus in popup
$('#w2ui-popup').html(msg);
if (options.title) $('#w2ui-popup .w2ui-popup-title').append(options.title);
if (options.buttons) $('#w2ui-popup .w2ui-popup-buttons').append(options.buttons);
if (options.body) $('#w2ui-popup .w2ui-popup-body').append(options.body);
// allow element to render
setTimeout(function () {
$('#w2ui-popup')
.css(w2utils.cssPrefix({
'transition': options.speed + 's opacity, ' + options.speed + 's -webkit-transform'
}))
.removeClass("w2ui-popup-opening");
obj.focus();
}, 1);
// clean transform
setTimeout(function () {
$('#w2ui-popup').css(w2utils.cssPrefix('transform', ''));
}, options.speed * 1000);
// event after
w2popup.status = 'open';
obj.trigger($.extend(edata, { phase: 'after' }));
} else if (options.multiple === true) {
// popup is not compatible with w2popup.message
w2popup.message(orig_options)
} else {
// if was from template and now not
if (w2popup._prev == null && w2popup._template != null) obj.restoreTemplate();
// trigger event
var edata = this.trigger({ phase: 'before', type: 'open', target: 'popup', options: options, present: true });
if (edata.isCancelled === true) return;
// check if size changed
w2popup.status = 'opening';
if (old_options != null) {
if (!old_options.maximized && (old_options['width'] != options['width'] || old_options['height'] != options['height'])) {
w2popup.resize(options.width, options.height);
}
options.prevSize = options.width + 'px:' + options.height + 'px';
options.maximized = old_options.maximized;
}
// show new items
var cloned = $('#w2ui-popup .w2ui-box').clone();
cloned.removeClass('w2ui-box').addClass('w2ui-box-temp').find('.w2ui-popup-body').empty().append(options.body);
// parse rel=*
if (typeof options.body == 'string' && cloned.find('div[rel=title], div[rel=body], div[rel=buttons]').length > 0) {
// title
var tmp = cloned.find('div[rel=title]');
if (tmp.length > 0) { options['title'] = tmp.html(); tmp.remove(); }
// buttons
var tmp = cloned.find('div[rel=buttons]');
if (tmp.length > 0) { options['buttons'] = tmp.html(); tmp.remove(); }
// body
var tmp = cloned.find('div[rel=body]');
if (tmp.length > 0) options['body'] = tmp.html(); else options['body'] = cloned.html();
// set proper body
cloned.html(options.body);
}
$('#w2ui-popup .w2ui-box').after(cloned);
if (options.buttons) {
$('#w2ui-popup .w2ui-popup-buttons').show().html('').append(options.buttons);
$('#w2ui-popup .w2ui-popup-body').removeClass('w2ui-popup-no-buttons');
$('#w2ui-popup .w2ui-box, #w2ui-popup .w2ui-box-temp').css('bottom', '');
} else {
$('#w2ui-popup .w2ui-popup-buttons').hide().html('');
$('#w2ui-popup .w2ui-popup-body').addClass('w2ui-popup-no-buttons');
$('#w2ui-popup .w2ui-box, #w2ui-popup .w2ui-box-temp').css('bottom', '0px');
}
if (options.title) {
$('#w2ui-popup .w2ui-popup-title')
.show()
.html((options.showClose ? '<div class="w2ui-popup-button w2ui-popup-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>' : '') +
(options.showMax ? '<div class="w2ui-popup-button w2ui-popup-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>' : ''))
.append(options.title);
$('#w2ui-popup .w2ui-popup-body').removeClass('w2ui-popup-no-title');
$('#w2ui-popup .w2ui-box, #w2ui-popup .w2ui-box-temp').css('top', '');
} else {
$('#w2ui-popup .w2ui-popup-title').hide().html('');
$('#w2ui-popup .w2ui-popup-body').addClass('w2ui-popup-no-title');
$('#w2ui-popup .w2ui-box, #w2ui-popup .w2ui-box-temp').css('top', '0px');
}
// transition
var div_old = $('#w2ui-popup .w2ui-box')[0];
var div_new = $('#w2ui-popup .w2ui-box-temp')[0];
w2utils.transition(div_old, div_new, options.transition, function () {
// clean up
obj.restoreTemplate();
$(div_old).remove();
$(div_new).removeClass('w2ui-box-temp').addClass('w2ui-box');
var $body = $(div_new).find('.w2ui-popup-body');
if ($body.length == 1) $body[0].style.cssText = options.style;
// remove max state
$('#w2ui-popup').data('prev-size', null);
// focus on first button
obj.focus();
});
// call event onOpen
w2popup.status = 'open';
obj.trigger($.extend(edata, { phase: 'after' }));
}
// save new options
options._last_focus = $(':focus');
// keyboard events
if (options.keyboard) $(document).on('keydown', this.keydown);
// initialize move
var tmp = {
resizing : false,
mvMove : mvMove,
mvStop : mvStop
};
$('#w2ui-popup .w2ui-popup-title').on('mousedown', function (event) {
if (!w2popup.get().maximized) mvStart(event);
});
return this;
// handlers
function mvStart(evnt) {
if (!evnt) evnt = window.event;
w2popup.status = 'moving';
tmp.resizing = true;
tmp.isLocked = $('#w2ui-popup > .w2ui-lock').length == 1 ? true : false;
tmp.x = evnt.screenX;
tmp.y = evnt.screenY;
tmp.pos_x = $('#w2ui-popup').position().left;
tmp.pos_y = $('#w2ui-popup').position().top;
if (!tmp.isLocked) w2popup.lock({ opacity: 0 });
$(document).on('mousemove', tmp.mvMove);
$(document).on('mouseup', tmp.mvStop);
if (evnt.stopPropagation) evnt.stopPropagation(); else evnt.cancelBubble = true;
if (evnt.preventDefault) evnt.preventDefault(); else return false;
}
function mvMove(evnt) {
if (tmp.resizing != true) return;
if (!evnt) evnt = window.event;
tmp.div_x = evnt.screenX - tmp.x;
tmp.div_y = evnt.screenY - tmp.y;
// trigger event
var edata = w2popup.trigger({ phase: 'before', type: 'move', target: 'popup', div_x: tmp.div_x, div_y: tmp.div_y });
if (edata.isCancelled === true) return;
// default behavior
$('#w2ui-popup').css(w2utils.cssPrefix({
'transition': 'none',
'transform' : 'translate3d('+ tmp.div_x +'px, '+ tmp.div_y +'px, 0px)'
}));
// event after
w2popup.trigger($.extend(edata, { phase: 'after'}));
}
function mvStop(evnt) {
if (tmp.resizing != true) return;
if (!evnt) evnt = window.event;
w2popup.status = 'open';
tmp.div_x = (evnt.screenX - tmp.x);
tmp.div_y = (evnt.screenY - tmp.y);
$('#w2ui-popup').css({
'left': (tmp.pos_x + tmp.div_x) + 'px',
'top' : (tmp.pos_y + tmp.div_y) + 'px'
}).css(w2utils.cssPrefix({
'transition': 'none',
'transform' : 'translate3d(0px, 0px, 0px)'
}));
tmp.resizing = false;
$(document).off('mousemove', tmp.mvMove);
$(document).off('mouseup', tmp.mvStop);
if (!tmp.isLocked) w2popup.unlock();
}
},
action: function (action, msgId) {
var obj = this;
var options = $('#w2ui-popup').data('options');
if (msgId != null) {
options = $('#w2ui-message' + msgId).data('options');
obj = {
parent: this,
options: options,
close: function () {
w2popup.message({ msgId: msgId })
}
}
}
var act = options.actions[action];
var click = act;
if ($.isPlainObject(act) && act.onClick) click = act.onClick;
// event before
var edata = this.trigger({ phase: 'before', target: action, msgId: msgId, type: 'action', action: act, originalEvent: event });
if (edata.isCancelled === true) return;
// default actions
if (typeof click === 'function') click.call(obj, event);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
keydown: function (event) {
var options = $('#w2ui-popup').data('options');
if (options && !options.keyboard) return;
// trigger event
var edata = w2popup.trigger({ phase: 'before', type: 'keydown', target: 'popup', options: options, originalEvent: event });
if (edata.isCancelled === true) return;
// default behavior
switch (event.keyCode) {
case 27:
event.preventDefault();
if ($('#w2ui-popup .w2ui-message').length > 0) w2popup.message(); else w2popup.close();
break;
}
// event after
w2popup.trigger($.extend(edata, { phase: 'after'}));
},
close: function (options) {
var obj = this;
var options = $.extend({}, $('#w2ui-popup').data('options'), options);
if ($('#w2ui-popup').length === 0 || this.status == 'closed') return;
if (this.status == 'opening') {
setTimeout(function () { w2popup.close(); }, 100);
return;
}
// trigger event
var edata = this.trigger({ phase: 'before', type: 'close', target: 'popup', options: options });
if (edata.isCancelled === true) return;
// default behavior
w2popup.status = 'closing';
$('#w2ui-popup')
.css(w2utils.cssPrefix({
'transition': options.speed + 's opacity, ' + options.speed + 's -webkit-transform'
}))
.addClass("w2ui-popup-closing");
w2popup.unlockScreen(options);
setTimeout(function () {
// return template
obj.restoreTemplate();
$('#w2ui-popup').remove();
w2popup.status = 'closed';
// restore active
if (options._last_focus && options._last_focus.length > 0) options._last_focus.focus();
// event after
obj.trigger($.extend(edata, { phase: 'after'}));
}, options.speed * 1000);
// remove keyboard events
if (options.keyboard) $(document).off('keydown', this.keydown);
},
toggle: function () {
var obj = this;
var options = $('#w2ui-popup').data('options');
// trigger event
var edata = this.trigger({ phase: 'before', type: 'toggle', target: 'popup', options: options });
if (edata.isCancelled === true) return;
// defatul action
if (options.maximized === true) w2popup.min(); else w2popup.max();
// event after
setTimeout(function () {
obj.trigger($.extend(edata, { phase: 'after'}));
}, (options.speed * 1000) + 50);
},
max: function () {
var obj = this;
var options = $('#w2ui-popup').data('options');
if (options.maximized === true) return;
// trigger event
var edata = this.trigger({ phase: 'before', type: 'max', target: 'popup', options: options });
if (edata.isCancelled === true) return;
// default behavior
w2popup.status = 'resizing';
options.prevSize = $('#w2ui-popup').css('width') + ':' + $('#w2ui-popup').css('height');
// do resize
w2popup.resize(10000, 10000, function () {
w2popup.status = 'open';
options.maximized = true;
obj.trigger($.extend(edata, { phase: 'after'}));
// resize gird, form, layout inside popup
$('#w2ui-popup .w2ui-grid, #w2ui-popup .w2ui-form, #w2ui-popup .w2ui-layout').each(function () {
var name = $(this).attr('name');
if (w2ui[name] && w2ui[name].resize) w2ui[name].resize();
})
});
},
min: function () {
var obj = this;
var options = $('#w2ui-popup').data('options');
if (options.maximized !== true) return;
var size = options.prevSize.split(':');
// trigger event
var edata = this.trigger({ phase: 'before', type: 'min', target: 'popup', options: options });
if (edata.isCancelled === true) return;
// default behavior
w2popup.status = 'resizing';
// do resize
w2popup.resize(parseInt(size[0]), parseInt(size[1]), function () {
w2popup.status = 'open';
options.maximized = false;
options.prevSize = null;
obj.trigger($.extend(edata, { phase: 'after'}));
// resize gird, form, layout inside popup
$('#w2ui-popup .w2ui-grid, #w2ui-popup .w2ui-form, #w2ui-popup .w2ui-layout').each(function () {
var name = $(this).attr('name');
if (w2ui[name] && w2ui[name].resize) w2ui[name].resize();
})
});
},
get: function () {
return $('#w2ui-popup').data('options');
},
set: function (options) {
w2popup.open(options);
},
clear: function() {
$('#w2ui-popup .w2ui-popup-title').html('');
$('#w2ui-popup .w2ui-popup-body').html('');
$('#w2ui-popup .w2ui-popup-buttons').html('');
},
reset: function () {
w2popup.open(w2popup.defaults);
},
load: function (options) {
w2popup.status = 'loading';
if (options.url == null) {
console.log('ERROR: The url parameter is empty.');
return;
}
var tmp = String(options.url).split('#');
var url = tmp[0];
var selector = tmp[1];
if (options == null) options = {};
// load url
var html = $('#w2ui-popup').data(url);
if (html != null) {
popup(html, selector);
} else {
$.get(url, function (data, status, obj) { // should always be $.get as it is template
popup(obj.responseText, selector);
$('#w2ui-popup').data(url, obj.responseText); // remember for possible future purposes
});
}
function popup(html, selector) {
delete options.url;
$('body').append('<div id="w2ui-tmp" style="display: none">' + html + '</div>');
if (selector != null && $('#w2ui-tmp #'+selector).length > 0) {
$('#w2ui-tmp #' + selector).w2popup(options);
} else {
$('#w2ui-tmp > div').w2popup(options);
}
// link styles
if ($('#w2ui-tmp > style').length > 0) {
var style = $('<div>').append($('#w2ui-tmp > style').clone()).html();
if ($('#w2ui-popup #div-style').length === 0) {
$('#w2ui-popup').append('<div id="div-style" style="position: absolute; left: -100; width: 1px"></div>');
}
$('#w2ui-popup #div-style').html(style);
}
$('#w2ui-tmp').remove();
}
},
message: function (options) {
var obj = this;
$().w2tag(); // hide all tags
if (!options) options = { width: 200, height: 100 };
var pWidth = parseInt($('#w2ui-popup').width());
var pHeight = parseInt($('#w2ui-popup').height());
options.originalWidth = options.width;
options.originalHeight = options.height;
if (parseInt(options.width) < 10) options.width = 10;
if (parseInt(options.height) < 10) options.height = 10;
if (options.hideOnClick == null) options.hideOnClick = false;
var poptions = $('#w2ui-popup').data('options') || {};
var titleHeight = parseInt($('#w2ui-popup > .w2ui-popup-title').css('height'));
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 = $('#w2ui-popup .w2ui-popup-title');
var msgCount = $('#w2ui-popup .w2ui-message').length;
// convert action arrays into buttons
if (options.actions != null) {
options.buttons = '';
Object.keys(options.actions).forEach(function (action) {
var handler = options.actions[action];
if (typeof handler == 'function') {
options.buttons += '<button class="w2ui-btn" onclick="w2popup.action(\''+ action +'\', '+ msgCount +')">'+ action +'</button>'
}
if (typeof handler == 'object') {
options.buttons += '<button class="w2ui-btn '+ (handler['class'] || '') +'" style="'+ (handler['style'] || '') +'"'
+ 'onclick="w2popup.action(\''+ action +'\', '+ msgCount +')">'+ (handler['text'] || action) +'</button>'
}
if (typeof handler == 'string') {
options.buttons += handler
}
});
}
// remove message
if ($.trim(options.html) === '' && $.trim(options.body) === '' && $.trim(options.buttons) === '') {
var $msg = $('#w2ui-popup .w2ui-message').last();
if (options.msgId != null) {
$msg = $('#w2ui-message'+ options.msgId);
}
var options = $msg.data('options') || {};
// message close event
var edata = obj.trigger({ phase: 'before', type: 'msgClose', msgId: $msg.attr('data-msgId'), target: 'popup', options: options });
if (edata.isCancelled === true) return;
// start hide transition
$msg.css(w2utils.cssPrefix({
'transition': '0.15s',
'transform': 'translateY(-' + options.height + 'px)'
}));
var $focus = $('#w2ui-popup .w2ui-message');
$focus = $($focus[$focus.length - 2])
.css('z-index', 1500)
.data('msg-focus');
if ($focus && $focus.length > 0) $focus.focus(); else obj.focus();
if (msgCount == 1) w2popup.unlock(150);
setTimeout(function () {
$msg.remove();
// default action
if (typeof options.onClose == 'function') {
options.onClose(edata);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}, 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
$('#w2ui-popup .w2ui-message').css('z-index', 1390).data('msg-focus', $(':focus'));
head.css('z-index', 1501);
if (options.close == null) {
options.close = function () {
w2popup.message({ msgId: msgCount })
}
}
// add message
$('#w2ui-popup .w2ui-box')
.before('<div id="w2ui-message' + msgCount + '" 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', '0s', true) + '" data-msgId="' + msgCount +'" ' +
(options.hideOnClick === true ? 'onclick="w2popup.message();"' : '') + '>' +
'</div>');
$('#w2ui-popup #w2ui-message'+ msgCount).data('options', options);
var display = $('#w2ui-popup #w2ui-message'+ msgCount).css('display');
$('#w2ui-popup #w2ui-message'+ msgCount).css(w2utils.cssPrefix({
'transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)')
}));
if (display == 'none') {
$('#w2ui-popup #w2ui-message'+ msgCount).show().html(options.html);
// timer needs to animation
setTimeout(function () {
$('#w2ui-popup #w2ui-message'+ msgCount).css(
$.extend(
w2utils.cssPrefix('transition', '.3s', false),
w2utils.cssPrefix({
'transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)')
})
)
);
}, 1);
// timer for lock
if (msgCount === 0) w2popup.lock();
// message open event
var edata = obj.trigger({ phase: 'before', type: 'msgOpen', msgId: msgCount, target: 'popup', options: options });
if (edata.isCancelled === true) return;
setTimeout(function() {
obj.focus();
// has to be on top of lock
$('#w2ui-popup #w2ui-message'+ msgCount).css(w2utils.cssPrefix({ 'transition': '0s' }));
if (typeof options.onOpen == 'function') {
options.onOpen(edata);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}, 350);
}
}
},
focus: function () {
var tmp = null;
var pop = $('#w2ui-popup');
var sel = 'input:visible, button:visible, select:visible, textarea:visible, [contentEditable], .w2ui-input';
// clear previous blur
$(pop).find(sel).off('.keep-focus');
// in message or popup
var cnt = $('#w2ui-popup .w2ui-message').length - 1;
var msg = $('#w2ui-popup #w2ui-message' + cnt);
if (msg.length > 0) {
var btn =$(msg[msg.length - 1]).find('button');
if (btn.length > 0) btn[0].focus();
tmp = msg;
} else if (pop.length > 0) {
var btn = pop.find('.w2ui-popup-buttons button');
if (btn.length > 0) btn[0].focus();
tmp = pop;
}
// keep focus/blur inside popup
$(tmp).find(sel)
.on('blur.keep-focus', function (event) {
setTimeout(function () {
var focus = $(':focus');
if ((focus.length > 0 && !$(tmp).find(sel).is(focus)) || focus.hasClass('w2ui-popup-hidden')) {
var el = $(tmp).find(sel);
if (el.length > 0) el[0].focus();
}
}, 1);
});
},
lock: function (msg, showSpinner) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift($('#w2ui-popup'));
w2utils.lock.apply(window, args);
},
unlock: function (speed) {
w2utils.unlock($('#w2ui-popup'), speed);
},
// --- INTERNAL FUNCTIONS
lockScreen: function (options) {
if ($('#w2ui-lock').length > 0) return false;
if (options == null) options = $('#w2ui-popup').data('options');
if (options == null) options = {};
options = $.extend({}, w2popup.defaults, options);
// show element
$('body').append('<div id="w2ui-lock" ' +
' onmousewheel="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true; if (event.preventDefault) event.preventDefault(); else return false;"'+
' style="position: ' + (w2utils.engine == 'IE5' ? 'absolute' : 'fixed') + '; z-Index: 1199; left: 0px; top: 0px; ' +
' padding: 0px; margin: 0px; background-color: ' + options.color + '; width: 100%; height: 100%; opacity: 0;"></div>');
// lock screen
setTimeout(function () {
$('#w2ui-lock')
.css('opacity', options.opacity)
.css(w2utils.cssPrefix('transition', options.speed + 's opacity'));
}, 1);
// add events
if (options.modal == true) {
$('#w2ui-lock').on('mousedown', function () {
$('#w2ui-lock')
.css('opacity', '0.6')
.css(w2utils.cssPrefix('transition', '.1s'));
});
$('#w2ui-lock').on('mouseup', function () {
setTimeout(function () {
$('#w2ui-lock')
.css('opacity', options.opacity)
.css(w2utils.cssPrefix('transition', '.1s'));
}, 100);
});
} else {
$('#w2ui-lock').on('mousedown', function () { w2popup.close(); });
}
return true;
},
unlockScreen: function (options) {
if ($('#w2ui-lock').length === 0) return false;
if (options == null) options = $('#w2ui-popup').data('options');
if (options == null) options = {};
options = $.extend({}, w2popup.defaults, options);
$('#w2ui-lock')
.css('opacity', '0')
.css(w2utils.cssPrefix('transition', options.speed + 's opacity'));
setTimeout(function () {
$('#w2ui-lock').remove();
}, options.speed * 1000);
return true;
},
resizeMessages: function () {
var obj = this;
var options = $('#w2ui-popup').data('options');
// see if there are messages and resize them
$('#w2ui-popup .w2ui-message').each(function () {
var moptions = $(this).data('options');
var $popup = $('#w2ui-popup');
if (parseInt(moptions.width) < 10) moptions.width = 10;
if (parseInt(moptions.height) < 10) moptions.height = 10;
var titleHeight = parseInt($popup.find('> .w2ui-popup-title').css('height'));
var pWidth = parseInt($popup.width());
var pHeight = parseInt($popup.height());
// recalc width
moptions.width = moptions.originalWidth;
if (moptions.width > pWidth - 10) {
moptions.width = pWidth - 10;
}
// recalc height
moptions.height = moptions.originalHeight;
if (moptions.height > pHeight - titleHeight - 5) {
moptions.height = pHeight - titleHeight - 5;
}
if (moptions.originalHeight < 0) moptions.height = pHeight + moptions.originalHeight - titleHeight;
if (moptions.originalWidth < 0) moptions.width = pWidth + moptions.originalWidth * 2; // x 2 because there is left and right margin
$(this).css({
left : ((pWidth - moptions.width) / 2) + 'px',
width : moptions.width + 'px',
height : moptions.height + 'px'
});
});
},
resize: function (width, height, callBack) {
var obj = this;
var options = $('#w2ui-popup').data('options') || {};
if (options.speed == null) options.speed = 0;
width = parseInt(width);
height = parseInt(height);
// calculate new position
var maxW, maxH;
if (window.innerHeight == undefined) {
maxW = parseInt(document.documentElement.offsetWidth);
maxH = parseInt(document.documentElement.offsetHeight);
if (w2utils.engine === 'IE7') { maxW += 21; maxH += 4; }
} else {
maxW = parseInt(window.innerWidth);
maxH = parseInt(window.innerHeight);
}
if (maxW - 10 < width) width = maxW - 10;
if (maxH - 10 < height) height = maxH - 10;
var top = (maxH - height) / 2 * 0.6;
var left = (maxW - width) / 2;
// resize there
$('#w2ui-popup')
.css(w2utils.cssPrefix({
'transition': options.speed + 's width, ' + options.speed + 's height, ' + options.speed + 's left, ' + options.speed + 's top'
}))
.css({
'top' : top,
'left' : left,
'width' : width,
'height': height
});
var tmp_int = setInterval(function () { obj.resizeMessages(); }, 10); // then messages resize nicely
setTimeout(function () {
clearInterval(tmp_int);
options.width = width;
options.height = height;
obj.resizeMessages();
if (typeof callBack == 'function') callBack();
}, (options.speed * 1000) + 50); // give extra 50 ms
},
/***********************
* Internal
**/
// restores template
restoreTemplate: function () {
var options = $('#w2ui-popup').data('options');
if (options == null) return;
var template = w2popup._template;
var title = options.title;
var body = options.body;
var buttons = options.buttons;
if (w2popup._prev) {
template = w2popup._prev.template;
title = w2popup._prev.title;
body = w2popup._prev.body;
buttons = w2popup._prev.buttons;
delete w2popup._prev;
} else {
delete w2popup._template;
}
if (template != null) {
var $tmp = $(template);
if ($tmp.length === 0) return;
if ($(body).attr('rel') == 'body') {
if (title) $tmp.append(title);
if (body) $tmp.append(body);
if (buttons) $tmp.append(buttons);
} else {
$tmp.append(body);
}
}
}
};
// merge in event handling
$.extend(w2popup, w2utils.event);
})(jQuery);
// ============================================
// --- Common dialogs
var w2alert = function (msg, title, callBack) {
var $ = jQuery;
if (title == null) title = w2utils.lang('Notification');
if ($('#w2ui-popup').length > 0 && w2popup.status != 'closing') {
w2popup.message({
width : 400,
height : 170,
body : '<div class="w2ui-centered w2ui-alert-msg" style="font-size: 13px;">' + msg + '</div>',
buttons : '<button onclick="w2popup.message();" class="w2ui-popup-btn w2ui-btn">' + w2utils.lang('Ok') + '</button>',
onOpen: function () {
$('#w2ui-popup .w2ui-message .w2ui-popup-btn').focus();
},
onClose: function () {
if (typeof callBack == 'function') callBack();
}
});
} else {
w2popup.open({
width : 450,
height : 220,
showMax : false,
showClose : false,
title : title,
body : '<div class="w2ui-centered w2ui-alert-msg" style="font-size: 13px;">' + msg + '</div>',
buttons : '<button onclick="w2popup.close();" class="w2ui-popup-btn w2ui-btn">' + w2utils.lang('Ok') + '</button>',
onOpen: function (event) {
// do not use onComplete as it is slower
setTimeout(function () { $('#w2ui-popup .w2ui-popup-btn').focus(); }, 1);
},
onKeydown: function (event) {
$('#w2ui-popup .w2ui-popup-btn').focus().addClass('clicked');
},
onClose: function () {
if (typeof callBack == 'function') callBack();
}
});
}
return {
ok: function (fun) {
callBack = fun;
return this;
},
done: function (fun) {
callBack = fun;
return this;
}
};
};
var w2confirm = function (msg, title, callBack) {
var $ = jQuery;
var options = {};
var defaults = {
msg : '',
title : w2utils.lang('Confirmation'),
width : ($('#w2ui-popup').length > 0 ? 400 : 450),
height : ($('#w2ui-popup').length > 0 ? 170 : 220),
yes_text : 'Yes',
yes_class : '',
yes_style : '',
yes_callBack: null,
no_text : 'No',
no_class : '',
no_style : '',
no_callBack : null,
focus_to_no : false,
callBack : null
};
if (arguments.length == 1 && typeof msg == 'object') {
$.extend(options, defaults, msg);
} else {
if (typeof title == 'function') {
$.extend(options, defaults, {
msg : msg,
callBack: title
})
} else {
$.extend(options, defaults, {
msg : msg,
title : title,
callBack: callBack
})
}
}
// if there is a yes/no button object
if (typeof options.btn_yes == 'object') {
options.yes_text = options.btn_yes.text || options.yes_text;
options.yes_class = options.btn_yes["class"] || options.yes_class;
options.yes_style = options.btn_yes.style || options.yes_style;
options.yes_callBack = options.btn_yes.callBack || options.yes_callBack;
}
if (typeof options.btn_no == 'object') {
options.no_text = options.btn_no.text || options.no_text;
options.no_class = options.btn_no["class"] || options.no_class;
options.no_style = options.btn_no.style || options.no_style;
options.no_callBack = options.btn_no.callBack || options.no_callBack;
}
if ($('#w2ui-popup').length > 0 && w2popup.status != 'closing' && w2popup.get()) {
if (options.width > w2popup.get().width) options.width = w2popup.get().width;
if (options.height > (w2popup.get().height - 50)) options.height = w2popup.get().height - 50;
w2popup.message({
width : options.width,
height : options.height,
body : '<div class="w2ui-centered w2ui-confirm-msg" style="font-size: 13px;">' + options.msg + '</div>',
buttons : (w2utils.settings.macButtonOrder
? '<button id="No" class="w2ui-popup-btn w2ui-btn '+ options.no_class +'" style="'+ options.no_style +'">' + w2utils.lang(options.no_text) + '</button>' +
'<button id="Yes" class="w2ui-popup-btn w2ui-btn '+ options.yes_class +'" style="'+ options.yes_style +'">' + w2utils.lang(options.yes_text) + '</button>'
: '<button id="Yes" class="w2ui-popup-btn w2ui-btn '+ options.yes_class +'" style="'+ options.yes_style +'">' + w2utils.lang(options.yes_text) + '</button>' +
'<button id="No" class="w2ui-popup-btn w2ui-btn '+ options.no_class +'" style="'+ options.no_style +'">' + w2utils.lang(options.no_text) + '</button>'
),
onOpen: function (event) {
$('#w2ui-popup .w2ui-message .w2ui-btn').on('click.w2confirm', function (event) {
w2popup._confirm_btn = event.target.id;
w2popup.message();
});
if (typeof options.onOpen == 'function') options.onOpen()
},
onClose: function (event) {
// needed this because there might be other messages
$('#w2ui-popup .w2ui-message .w2ui-btn').off('click.w2confirm');
// need to wait for message to slide up
setTimeout(function () {
if (typeof options.callBack == 'function') options.callBack(w2popup._confirm_btn);
if (w2popup._confirm_btn == 'Yes' && typeof options.yes_callBack == 'function') options.yes_callBack();
if (w2popup._confirm_btn == 'No' && typeof options.no_callBack == 'function') options.no_callBack();
}, 300);
if (typeof options.onClose == 'function') options.onClose()
}
// onKeydown will not work here
});
} else {
if (!w2utils.isInt(options.height)) options.height = options.height + 50;
w2popup.open({
width : options.width,
height : options.height,
title : options.title,
modal : true,
showClose : false,
body : '<div class="w2ui-centered w2ui-confirm-msg" style="font-size: 13px;">' + options.msg + '</div>',
buttons : (w2utils.settings.macButtonOrder
? '<button id="No" class="w2ui-popup-btn w2ui-btn '+ options.no_class +'" style="'+ options.no_style +'">'+ w2utils.lang(options.no_text) +'</button>' +
'<button id="Yes" class="w2ui-popup-btn w2ui-btn '+ options.yes_class +'" style="'+ options.yes_style +'">'+ w2utils.lang(options.yes_text) +'</button>'
: '<button id="Yes" class="w2ui-popup-btn w2ui-btn '+ options.yes_class +'" style="'+ options.yes_style +'">'+ w2utils.lang(options.yes_text) +'</button>' +
'<button id="No" class="w2ui-popup-btn w2ui-btn '+ options.no_class +'" style="'+ options.no_style +'">'+ w2utils.lang(options.no_text) +'</button>'
),
onOpen: function (event) {
// do not use onComplete as it is slower
setTimeout(function () {
$('#w2ui-popup .w2ui-popup-btn').on('click', function (event) {
w2popup.close();
if (typeof options.callBack == 'function') options.callBack(event.target.id);
if (event.target.id == 'Yes' && typeof options.yes_callBack == 'function') options.yes_callBack();
if (event.target.id == 'No' && typeof options.no_callBack == 'function') options.no_callBack();
});
if(options.focus_to_no){
$('#w2ui-popup .w2ui-popup-btn#No').focus();
}else{
$('#w2ui-popup .w2ui-popup-btn#Yes').focus();
}
if (typeof options.onOpen == 'function') options.onOpen()
}, 1);
},
onClose: function (event) {
if (typeof options.onClose == 'function') options.onClose()
},
onKeydown: function (event) {
// if there are no messages
if ($('#w2ui-popup .w2ui-message').length === 0) {
switch (event.originalEvent.keyCode) {
case 13: // enter
$('#w2ui-popup .w2ui-popup-btn#Yes').focus().addClass('clicked'); // no need fo click as enter will do click
w2popup.close();
break;
case 27: // esc
$('#w2ui-popup .w2ui-popup-btn#No').focus().click();
w2popup.close();
break;
}
}
}
});
}
return {
yes: function (fun) {
options.yes_callBack = fun;
return this;
},
no: function (fun) {
options.no_callBack = fun;
return this;
}
};
};
var w2prompt = function (label, title, callBack) {
var $ = jQuery;
var options = {};
var defaults = {
title : w2utils.lang('Notification'),
width : ($('#w2ui-popup').length > 0 ? 400 : 450),
height : ($('#w2ui-popup').length > 0 ? 170 : 220),
label : '',
value : '',
attrs : '',
textarea : false,
ok_text : w2utils.lang('Ok'),
ok_class : '',
cancel_text : w2utils.lang('Cancel'),
cancel_class: '',
callBack : null,
onOpen : null,
onClose : null
}
w2popup.tmp = w2popup.tmp || {}
if (arguments.length == 1 && typeof label == 'object') {
$.extend(options, defaults, label);
} else {
if (typeof title == 'function') {
$.extend(options, defaults, {
label : label,
callBack: title
})
} else {
$.extend(options, defaults, {
label : label,
title : title,
callBack: callBack
})
}
}
if ($('#w2ui-popup').length > 0 && w2popup.status != 'closing' && w2popup.get()) {
if (options.width > w2popup.get().width) options.width = w2popup.get().width;
if (options.height > (w2popup.get().height - 50)) options.height = w2popup.get().height - 50;
w2popup.message({
width : options.width,
height : options.height,
body : (options.textarea
? '<div class="w2ui-prompt textarea">'+
' <div>' + options.label + '</div>'+
' <textarea id="w2prompt" class="w2ui-input" '+ options.attrs +'></textarea>'+
'</div>'
: '<div class="w2ui-prompt w2ui-centered">'+
' <label>' + options.label + '</label>'+
' <input id="w2prompt" class="w2ui-input" '+ options.attrs +'>'+
'</div>'
),
buttons : (w2utils.settings.macButtonOrder
? '<button id="Cancel" class="w2ui-popup-btn w2ui-btn '+ options.cancel_class +'">' + options.cancel_text + '</button>' +
'<button id="Ok" class="w2ui-popup-btn w2ui-btn '+ options.ok_class +'">' + options.ok_text + '</button>'
: '<button id="Ok" class="w2ui-popup-btn w2ui-btn '+ options.ok_class +'">' + options.ok_text + '</button>' +
'<button id="Cancel" class="w2ui-popup-btn w2ui-btn '+ options.cancel_class +'">' + options.cancel_text + '</button>'
),
onOpen: function () {
$('#w2prompt').val(options.value).off('.w2prompt').on('keydown.w2prompt', function(event) {
if (event.keyCode == 13) {
$('#w2ui-popup .w2ui-message .w2ui-btn#Ok').click()
}
});
$('#w2ui-popup .w2ui-message .w2ui-btn#Ok').off('.w2prompt').on('click.w2prompt', function (event) {
w2popup.tmp.btn = 'ok';
w2popup.tmp.value = $('#w2prompt').val();
w2popup.message();
});
$('#w2ui-popup .w2ui-message .w2ui-btn#Cancel').off('.w2prompt').on('click.w2prompt', function (event) {
w2popup.tmp.btn = 'cancel';
w2popup.tmp.value = null;
w2popup.message();
});
// set focus
setTimeout(function () { $('#w2prompt').focus(); }, 100);
// some event
if (typeof options.onOpen == 'function') options.onOpen()
},
onClose: function () {
// needed this because there might be other messages
$('#w2ui-popup .w2ui-message .w2ui-btn').off('click.w2prompt');
// need to wait for message to slide up
setTimeout(function () {
btnClick(w2popup.tmp.btn, w2popup.tmp.value);
}, 300);
// some event
if (typeof options.onClose == 'function') options.onClose()
}
// onKeydown will not work here
});
} else {
if (!w2utils.isInt(options.height)) options.height = options.height + 50;
w2popup.open({
width : options.width,
height : options.height,
title : options.title,
modal : true,
showClose : false,
body : (options.textarea
? '<div class="w2ui-prompt">'+
' <div>' + options.label + '</div>'+
' <textarea id="w2prompt" class="w2ui-input" '+ options.attrs +'></textarea>'+
'</div>'
: '<div class="w2ui-prompt w2ui-centered" style="font-size: 13px;">'+
' <label>' + options.label + '</label>'+
' <input id="w2prompt" class="w2ui-input" '+ options.attrs +'>'+
'</div>'
),
buttons : (w2utils.settings.macButtonOrder
? '<button id="Cancel" class="w2ui-popup-btn w2ui-btn '+ options.cancel_class +'">' + options.cancel_text + '</button>' +
'<button id="Ok" class="w2ui-popup-btn w2ui-btn '+ options.ok_class +'">' + options.ok_text + '</button>'
: '<button id="Ok" class="w2ui-popup-btn w2ui-btn '+ options.ok_class +'">' + options.ok_text + '</button>'+
'<button id="Cancel" class="w2ui-popup-btn w2ui-btn '+ options.cancel_class +'">' + options.cancel_text + '</button>'
),
onOpen: function (event) {
// do not use onComplete as it is slower
setTimeout(function () {
$('#w2prompt').val(options.value);
$('#w2prompt').w2field('text');
$('#w2ui-popup .w2ui-popup-btn#Ok').on('click', function (event) {
w2popup.tmp.btn = 'ok';
w2popup.tmp.value = $('#w2prompt').val();
w2popup.close();
});
$('#w2ui-popup .w2ui-popup-btn#Cancel').on('click', function (event) {
w2popup.tmp.btn = 'cancel';
w2popup.tmp.value = null;
w2popup.close();
});
$('#w2ui-popup .w2ui-popup-btn#Ok');
// set focus
setTimeout(function () { $('#w2prompt').focus(); }, 100);
// some event
if (typeof options.onOpen == 'function') options.onOpen()
}, 1);
},
onClose: function (event) {
// some event
btnClick(w2popup.tmp.btn, w2popup.tmp.value);
if (typeof options.onClose == 'function') options.onClose()
},
onKeydown: function (event) {
// if there are no messages
if ($('#w2ui-popup .w2ui-message').length === 0) {
switch (event.originalEvent.keyCode) {
case 13: // enter
$('#w2ui-popup .w2ui-popup-btn#Ok').focus().addClass('clicked'); // no need fo click as enter will do click
break;
case 27: // esc
w2popup.tmp.btn = 'cancel';
w2popup.tmp.value = null;
break;
}
}
}
});
}
function btnClick(btn, value) {
if (btn == 'ok' && typeof options.ok_callBack == 'function') {
options.ok_callBack(value)
}
if (btn == 'cancel' && typeof options.cancel_callBack == 'function') {
options.cancel_callBack(value)
}
if (typeof options.callBack == 'function') {
options.callBack(btn, value);
}
}
return {
change: function (fun) {
$('#w2prompt').on('keyup', fun).keyup();
return this;
},
ok: function (fun) {
options.ok_callBack = fun;
return this;
},
cancel: function (fun) {
options.cancel_callBack = fun;
return this;
}
};
};
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2tabs - tabs widget
* - $().w2tabs - jQuery wrapper
* - Dependencies: jQuery, w2utils
*
* == NICE TO HAVE ==
* - align = left, right, center ??
*
* == 1.5 changes ==
* - tab.caption - deprecated
* - getTabHTML()
* - refactored with display: flex
* - reorder
* - initReorder
* - dragMove
* - tmp
*
************************************************************************/
(function ($) {
var w2tabs = function (options) {
this.box = null; // DOM Element that holds the element
this.name = null; // unique name for w2ui
this.active = null;
this.reorder = false;
this.flow = 'down'; // can be down or up
this.tooltip = 'top|left'; // can be top, bottom, left, right
this.tabs = [];
this.routeData = {}; // data for dynamic routes
this.tmp = {}; // placeholder for internal variables
this.right = '';
this.style = '';
$.extend(this, { handlers: [] });
$.extend(true, this, w2obj.tabs, options);
};
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2tabs = function(method) {
if ($.isPlainObject(method)) {
// check name parameter
if (!w2utils.checkName(method, 'w2tabs')) return;
// extend tabs
var tabs = method.tabs || [];
var object = new w2tabs(method);
for (var i = 0; i < tabs.length; i++) {
object.tabs[i] = $.extend({}, w2tabs.prototype.tab, tabs[i]);
}
// register new object
w2ui[object.name] = object;
// render
if ($(this).length !== 0) {
object.render($(this)[0]);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
};
// ====================================================
// -- Implementation of core functionality
w2tabs.prototype = {
onClick : null,
onClose : null,
onRender : null,
onRefresh : null,
onResize : null,
onDestroy : null,
tab : {
id : null, // command to be sent to all event handlers
text : null,
route : null,
hidden : false,
disabled : false,
closable : false,
tooltip : null,
style : '',
onClick : null,
onRefresh : null,
onClose : null
},
add: function (tab) {
return this.insert(null, tab);
},
insert: function (id, tab) {
if (!$.isArray(tab)) tab = [tab];
// assume it is array
for (var i = 0; i < tab.length; i++) {
// checks
if (tab[i].id == null) {
console.log('ERROR: The parameter "id" is required but not supplied. (obj: '+ this.name +')');
return;
}
if (!w2utils.checkUniqueId(tab[i].id, this.tabs, 'tabs', this.name)) return;
// add tab
var newTab = $.extend({}, w2tabs.prototype.tab, tab[i]);
if (id == null) {
this.tabs.push(newTab);
} else {
var middle = this.get(id, true);
var before = this.tabs[middle].id
this.insertTabHTML(before, newTab)
}
}
},
remove: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
var tab = this.get(arguments[a]);
if (!tab) return false;
removed++;
// remove from array
this.tabs.splice(this.get(tab.id, true), 1);
// remove from screen
$(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id)).remove();
}
this.resize();
return removed;
},
select: function (id) {
if (this.active == id || this.get(id) == null) return false;
this.active = id;
this.refresh();
return true;
},
set: function (id, tab) {
var index = this.get(id, true);
if (index == null) return false;
$.extend(this.tabs[index], tab);
this.refresh(id);
return true;
},
get: function (id, returnIndex) {
if (arguments.length === 0) {
var all = [];
for (var i1 = 0; i1 < this.tabs.length; i1++) {
if (this.tabs[i1].id != null) {
all.push(this.tabs[i1].id);
}
}
return all;
} else {
for (var i2 = 0; i2 < this.tabs.length; i2++) {
if (this.tabs[i2].id == id) { // need to be == since id can be numeric
return (returnIndex === true ? i2 : this.tabs[i2]);
}
}
}
return null;
},
show: function () {
var obj = this;
var shown = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var tab = this.get(arguments[a]);
if (!tab || tab.hidden === false) continue;
shown++;
tab.hidden = false;
tmp.push(tab.id);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); obj.resize(); }, 15); // needs timeout
return shown;
},
hide: function () {
var obj = this;
var hidden= 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var tab = this.get(arguments[a]);
if (!tab || tab.hidden === true) continue;
hidden++;
tab.hidden = true;
tmp.push(tab.id);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); obj.resize(); }, 15); // needs timeout
return hidden;
},
enable: function () {
var obj = this;
var enabled = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var tab = this.get(arguments[a]);
if (!tab || tab.disabled === false) continue;
enabled++;
tab.disabled = false;
tmp.push(tab.id);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); }, 15); // needs timeout
return enabled;
},
disable: function () {
var obj = this;
var disabled = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var tab = this.get(arguments[a]);
if (!tab || tab.disabled === true) continue;
disabled++;
tab.disabled = true;
tmp.push(tab.id);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); }, 15); // needs timeout
return disabled;
},
dragMove: function(event) {
if (!this.tmp.reordering) return
var obj = this;
var info = this.tmp.moving
var tab = this.tabs[info.index];
var next = _find(info.index, 1);
var prev = _find(info.index, -1);
var $el = $('#tabs_'+ this.name + '_tab_'+ w2utils.escapeId(tab.id))
if (info.divX > 0 && next) {
var $nextEl = $('#tabs_'+ this.name + '_tab_'+ w2utils.escapeId(next.id))
var width1 = parseInt($el.css('width'));
var width2 = parseInt($nextEl.css('width'));
if (width1 < width2) {
width1 = Math.floor(width1 / 3)
width2 = width2 - width1;
} else {
width1 = Math.floor(width2 / 3)
width2 = width2 - width1;
}
if (info.divX > width2) {
var index = this.tabs.indexOf(next)
this.tabs.splice(info.index, 0, this.tabs.splice(index, 1)[0]); // reorder in the array
info.$tab.before($nextEl)
info.$tab.css('opacity', 0);
Object.assign(this.tmp.moving, {
index: index,
divX: -width1,
x: event.pageX + width1,
left: info.left + info.divX + width1
})
return
}
}
if (info.divX < 0 && prev) {
var $prevEl = $('#tabs_'+ this.name + '_tab_'+ w2utils.escapeId(prev.id))
var width1 = parseInt($el.css('width'));
var width2 = parseInt($prevEl.css('width'));
if (width1 < width2) {
width1 = Math.floor(width1 / 3)
width2 = width2 - width1;
} else {
width1 = Math.floor(width2 / 3)
width2 = width2 - width1;
}
if (Math.abs(info.divX) > width2) {
var index = this.tabs.indexOf(prev)
this.tabs.splice(info.index, 0, this.tabs.splice(index, 1)[0]); // reorder in the array
$prevEl.before(info.$tab)
info.$tab.css('opacity', 0);
Object.assign(info, {
index: index,
divX: width1,
x: event.pageX - width1,
left: info.left + info.divX - width1
})
return
}
}
function _find(ind, inc) {
ind += inc;
var tab = obj.tabs[ind]
if (tab && tab.hidden) {
tab = _find(ind, inc)
}
return tab
}
},
tooltipShow: function (id, event, forceRefresh) {
var item = this.get(id);
var $el = $(this.box).find('#tabs_'+ this.name + '_tab_'+ w2utils.escapeId(id));
if (this.tooltip == null || item.disabled || this.tmp.reordering) {
return;
}
var pos = this.tooltip;
var txt = item.tooltip;
if (typeof txt == 'function') txt = txt.call(this, item);
$el.prop('_mouse_over', true);
setTimeout(function () {
if ($el.prop('_mouse_over') === true && $el.prop('_mouse_tooltip') !== true) {
$el.prop('_mouse_tooltip', true);
// show tooltip
$el.w2tag(w2utils.lang(txt), { position: pos });
}
if (forceRefresh == true) {
$el.w2tag(w2utils.lang(txt), { position: pos });
}
}, 1);
},
tooltipHide: function (id) {
var item = this.get(id);
var $el = $(this.box).find('#tabs_'+ this.name + '_tab_'+ w2utils.escapeId(id));
if (this.tooltip == null || item.disabled || this.tmp.reordering) {
return;
}
$el.removeProp('_mouse_over');
setTimeout(function () {
if ($el.prop('_mouse_over') !== true && $el.prop('_mouse_tooltip') === true) {
$el.removeProp('_mouse_tooltip');
$el.w2tag(); // hide tooltip
}
}, 1);
},
getTabHTML: function (id) {
var index = this.get(id, true);
var tab = this.tabs[index];
if (tab == null) return false;
if (tab.text == null && tab.caption != null) tab.text = tab.caption;
if (tab.tooltip == null && tab.hint != null) tab.tooltip = tab.hint; // for backward compatibility
if (tab.caption != null) {
console.log('NOTICE: tabs tab.caption property is deprecated, please use tab.text. Tab -> ', tab)
}
if (tab.hint != null) {
console.log('NOTICE: tabs tab.hint property is deprecated, please use tab.tooltip. Tab -> ', tab)
}
var text = tab.text;
if (typeof text == 'function') text = text.call(this, tab);
if (text == null) text = '';
var closable = '';
var addStyle = '';
if (tab.hidden) { addStyle += 'display: none;'; }
if (tab.disabled) { addStyle += 'opacity: 0.2;'; }
if (tab.closable && !tab.disabled) {
closable = '<div class="w2ui-tab-close'+ (this.active === tab.id ? ' active' : '') + '"'
+ ' onmouseover= "w2ui[\''+ this.name +'\'].tooltipShow(\''+ tab.id +'\', event)"'
+ ' onmouseout = "w2ui[\''+ this.name +'\'].tooltipHide(\''+ tab.id +'\', event)"'
+ ' onmousedown= "event.stopPropagation()"'
+ ' onmouseup = "w2ui[\''+ this.name +'\'].animateClose(\''+ tab.id +'\', event); event.stopPropagation()">'
+ '</div>';
}
var tabHTML = '<div id="tabs_'+ this.name + '_tab_'+ tab.id +'" style="'+ addStyle + ' ' + tab.style + '"'
+ ' class="w2ui-tab'+ (this.active === tab.id ? ' active' : '') + (tab.closable ? ' closable' : '') + (tab['class'] ? ' ' + tab['class'] : '') +'"'
+ ' onmouseover = "w2ui[\''+ this.name +"'].tooltipShow('"+ tab.id +'\', event)"'
+ ' onmouseout = "w2ui[\''+ this.name +"'].tooltipHide('"+ tab.id +'\', event)"'
+ ' onmousedown = "w2ui[\''+ this.name +"'].initReorder('"+ tab.id +'\', event)"'
+' onclick = "w2ui[\''+ this.name +"'].click('"+ tab.id +'\', event)">'
+ w2utils.lang(text) + closable
+ '</div>';
return tabHTML;
},
refresh: function (id) {
var time = (new Date()).getTime();
if (this.flow == 'up') $(this.box).addClass('w2ui-tabs-up'); else $(this.box).removeClass('w2ui-tabs-up');
// event before
var edata = this.trigger({ phase: 'before', type: 'refresh', target: (id != null ? id : this.name), object: this.get(id) });
if (edata.isCancelled === true) return;
if (id == null) {
// refresh all
for (var i = 0; i < this.tabs.length; i++) {
this.refresh(this.tabs[i].id);
}
} else {
// create or refresh only one item
var $tab = $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(id));
var tabHTML = this.getTabHTML(id);
if ($tab.length === 0) {
$(this.box).find('#tabs_'+ this.name +'_right').before(tabHTML);
} else {
$tab.replaceWith(tabHTML);
}
}
// right html
$('#tabs_'+ this.name +'_right').html(this.right);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
// this.resize();
return (new Date()).getTime() - time;
},
render: function (box) {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
if (edata.isCancelled === true) return;
// default action
// if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
if (box != null) {
if ($(this.box).find('#tabs_'+ this.name + '_right').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-tabs')
.html('');
}
this.box = box;
}
if (!this.box) return false;
// render all buttons
var html =
'<div class="w2ui-scroll-wrapper" onmousedown="var el=w2ui[\''+ this.name +'\']; if (el) el.resize();">'+
' <div class="w2ui-tabs-line"></div>'+
' <div id="tabs_'+ this.name +'_right" class="w2ui-tabs-right">'+ this.right +'</div>'+
'</div>'+
'<div class="w2ui-scroll-left" onclick="var el=w2ui[\''+ this.name +'\']; if (el) el.scroll(\'left\');"></div>'+
'<div class="w2ui-scroll-right" onclick="var el=w2ui[\''+ this.name +'\']; if (el) el.scroll(\'right\');"></div>';
$(this.box)
.attr('name', this.name)
.addClass('w2ui-reset w2ui-tabs')
.html(html);
if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
this.refresh();
this.resize();
return (new Date()).getTime() - time;
},
initReorder: function(id, event) {
if (!this.reorder) return
var obj = this;
var $tab = $('#tabs_' + this.name + '_tab_' + w2utils.escapeId(id));
var tabIndex = this.get(id, true);
var $ghost = $tab.clone();
var edata;
$ghost.attr('id', '#tabs_' + this.name + '_tab_ghost')
// debugger
this.tmp.moving = {
index: tabIndex,
indexFrom: tabIndex,
$tab: $tab,
$ghost: $ghost,
divX: 0,
left: $tab.offset().left,
parentX: $(this.box).offset().left,
x: event.pageX,
opacity: $tab.css('opacity')
}
$('body')
.off('.w2uiTabReorder')
.on('mousemove.w2uiTabReorder', function (event) {
if (!obj.tmp.reordering) {
// event before
edata = obj.trigger({ phase: 'before', type: 'reorder', target: obj.tabs[tabIndex].id, indexFrom: tabIndex, tab: obj.tabs[tabIndex] });
if (edata.isCancelled === true) return;
$().w2tag()
obj.tmp.reordering = true;
$ghost.addClass('moving')
$ghost.css({
'pointer-events': 'none',
'position': 'absolute',
'left': $tab.offset().left
})
$tab.css('opacity', 0);
$(obj.box).find('.w2ui-scroll-wrapper').append($ghost);
$(obj.box).find('.w2ui-tab-close').hide();
}
obj.tmp.moving.divX = event.pageX - obj.tmp.moving.x;
$ghost.css('left', (obj.tmp.moving.left - obj.tmp.moving.parentX + obj.tmp.moving.divX) + 'px');
obj.dragMove(event);
})
.on('mouseup.w2uiTabReorder', function () {
$('body').off('.w2uiTabReorder');
$ghost.css({
'transition': '0.1s',
'left': obj.tmp.moving.$tab.offset().left - obj.tmp.moving.parentX
})
$(obj.box).find('.w2ui-tab-close').show();
setTimeout(function () {
$ghost.remove();
$tab.css({ opacity: obj.tmp.moving.opacity });
// obj.render()
if (obj.tmp.reordering) {
obj.trigger($.extend(edata, { phase: 'after', indexTo: obj.tmp.moving.index }));
if (edata.isCancelled === true) return;
}
obj.tmp.reordering = false;
}, 100)
})
},
scroll: function (direction) {
var box = $(this.box);
var obj = this;
var scrollBox = box.find('.w2ui-scroll-wrapper');
var scrollLeft = scrollBox.scrollLeft();
var $right = $(this.box).find('.w2ui-tabs-right');
var width1 = scrollBox.outerWidth();
var width2 = scrollLeft + parseInt($right.offset().left) + parseInt($right.width());
var scroll;
switch (direction) {
case 'left':
scroll = scrollLeft - width1 + 50; // 35 is width of both button
if (scroll <= 0) scroll = 0;
scrollBox.animate({ scrollLeft: scroll }, 300);
break;
case 'right':
scroll = scrollLeft + width1 - 50; // 35 is width of both button
if (scroll >= width2 - width1) scroll = width2 - width1;
scrollBox.animate({ scrollLeft: scroll }, 300);
break;
}
setTimeout(function () { obj.resize(); }, 350);
},
resize: function () {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'resize', target: this.name });
if (edata.isCancelled === true) return;
// show hide overflow buttons
var box = $(this.box);
box.find('.w2ui-scroll-left, .w2ui-scroll-right').hide();
var scrollBox = box.find('.w2ui-scroll-wrapper');
var $right = $(this.box).find('.w2ui-tabs-right');
var boxWidth = scrollBox.outerWidth()
var itemsWidth = ($right.length > 0 ? $right[0].offsetLeft + $right[0].clientWidth : 0);
if (itemsWidth > boxWidth) {
// we have overflowed content
if (scrollBox.scrollLeft() > 0) {
box.find('.w2ui-scroll-left').show();
}
var padding = parseInt(scrollBox.css('padding-right'))
if (boxWidth < itemsWidth - scrollBox.scrollLeft() - padding) {
box.find('.w2ui-scroll-right').show();
}
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
if (edata.isCancelled === true) return;
// clean up
if ($(this.box).find('#tabs_'+ this.name + '_right').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-tabs')
.html('');
}
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
// ===================================================
// -- Internal Event Handlers
click: function (id, event) {
var tab = this.get(id);
if (tab == null || tab.disabled || this.tmp.reordering) return false;
// event before
var edata = this.trigger({ phase: 'before', type: 'click', target: id, tab: tab, object: tab, originalEvent: event });
if (edata.isCancelled === true) return;
// default action
$(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.active)).removeClass('active');
$(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.active)).removeClass('active');
this.active = tab.id;
// route processing
if (typeof tab.route == 'string') {
var route = tab.route !== '' ? String('/'+ tab.route).replace(/\/{2,}/g, '/') : '';
var info = w2utils.parseRoute(route);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (this.routeData[info.keys[k].name] == null) continue;
route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
}
}
setTimeout(function () { window.location.hash = route; }, 1);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
this.refresh(id);
},
animateClose: function(id, event) {
var tab = this.get(id);
if (tab == null || tab.disabled) return false;
// event before
var edata = this.trigger({ phase: 'before', type: 'close', target: id, object: this.get(id), originalEvent: event });
if (edata.isCancelled === true) return;
// default action
var obj = this;
var $tab = $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id))
$tab.css({ // need to be separate transition
'opacity': 0,
'transition': '.25s'
})
.find('.w2ui-tab-close').remove();
$tab.css({
'padding-left': 0,
'padding-right': 0,
'text-overflow': 'clip',
'overflow': 'hidden',
'width': '0px'
})
setTimeout(function () {
obj.remove(id);
obj.trigger($.extend(edata, { phase: 'after' }));
obj.refresh();
}, 250);
},
insertTabHTML: function(id, tab) {
var obj = this;
var middle = this.get(id, true);
this.tabs = this.tabs.slice(0, middle).concat([tab], this.tabs.slice(middle));
var $before = $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(id));
var $tab = $(this.getTabHTML(tab.id));
$before.before($tab);
this.resize();
}
}
$.extend(w2tabs.prototype, w2utils.event);
w2obj.tabs = w2tabs;
})(jQuery);
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2toolbar - toolbar widget
* - $().w2toolbar - jQuery wrapper
* - Dependencies: jQuery, w2utils, w2field
*
* == NICE TO HAVE ==
* - vertical toolbar
*
* == 1.5 changes ==
* - menu drop down can have groups now
* - item.caption - deprecated
* - item.text - can be a function
* - item.icon - can be a function
* - item.tooltip - can be a function
* - item.color
* - item.options
* - event.item.get - finds selected item
* - item.keepOpen, drop down will not close
* - item.type = 'new-line'
*
************************************************************************/
(function ($) {
var w2toolbar = function (options) {
this.box = null; // DOM Element that holds the element
this.name = null; // unique name for w2ui
this.routeData = {}; // data for dynamic routes
this.items = [];
this.right = ''; // HTML text on the right of toolbar
this.tooltip = 'top|left';// can be top, bottom, left, right
$.extend(true, this, w2obj.toolbar, options);
};
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2toolbar = function(method) {
if ($.isPlainObject(method)) {
// check name parameter
if (!w2utils.checkName(method, 'w2toolbar')) return;
// extend items
var items = method.items || [];
var object = new w2toolbar(method);
$.extend(object, { items: [], handlers: [] });
for (var i = 0; i < items.length; i++) {
object.items[i] = $.extend({}, w2toolbar.prototype.item, items[i]);
// menus
if (object.items[i].type == 'menu-check') {
var item = object.items[i];
if (!Array.isArray(item.selected)) item.selected = [];
if (Array.isArray(item.items)) {
for (var j = 0; j < item.items.length; j++) {
var it = item.items[j];
if (it.checked && item.selected.indexOf(it.id) == -1) item.selected.push(it.id);
if (!it.checked && item.selected.indexOf(it.id) != -1) it.checked = true;
if (it.checked == null) it.checked = false;
}
}
}
else if (object.items[i].type == 'menu-radio') {
var item = object.items[i];
if (Array.isArray(item.items)) {
for (var j = 0; j < item.items.length; j++) {
var it = item.items[j];
if (it.checked && item.selected == null) item.selected = it.id; else it.checked = false;
if (!it.checked && item.selected == it.id) it.checked = true;
if (it.checked == null) it.checked = false;
}
}
}
}
// register new object
w2ui[object.name] = object;
// render
if ($(this).length !== 0) {
object.render($(this)[0]);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
};
// ====================================================
// -- Implementation of core functionality
w2toolbar.prototype = {
onClick : null,
onRender : null,
onRefresh : null,
onResize : null,
onDestroy : null,
item: {
id : null, // command to be sent to all event handlers
type : 'button', // button, check, radio, drop, menu, menu-radio, menu-check, break, html, spacer
text : null,
html : '',
tooltip : null, // w2toolbar.tooltip should be
count : null,
hidden : false,
disabled : false,
checked : false, // used for radio buttons
img : null,
icon : null,
route : null, // if not null, it is route to go
arrow : true, // arrow down for drop/menu types
style : null, // extra css style for caption
group : null, // used for radio buttons
items : null, // for type menu* it is an array of items in the menu
selected : null, // used for menu-check, menu-radio
overlay : {},
color : null, // color value - used in color pickers
options : {
advanced : false, // advanced picker t/f - user in color picker
transparent : true, // transparent t/f - used in color picker
html : '' // additional buttons for color picker
},
onClick : null,
onRefresh : null
},
add: function (items) {
this.insert(null, items);
},
insert: function (id, items) {
if (!$.isArray(items)) items = [items];
for (var o = 0; o < items.length; o++) {
// checks
if (items[o].type == null) {
console.log('ERROR: The parameter "type" is required but not supplied in w2toolbar.add() method.');
return;
}
if ($.inArray(String(items[o].type), ['button', 'check', 'radio', 'drop', 'menu', 'menu-radio', 'menu-check', 'color', 'text-color', 'break', 'html', 'spacer']) == -1) {
console.log('ERROR: The parameter "type" should be one of the following [button, check, radio, drop, menu, break, html, spacer] '+
'in w2toolbar.add() method.');
return;
}
if (items[o].id == null && items[o].type != 'break' && items[o].type != 'spacer') {
console.log('ERROR: The parameter "id" is required but not supplied in w2toolbar.add() method.');
return;
}
if (!w2utils.checkUniqueId(items[o].id, this.items, 'toolbar items', this.name)) return;
// add item
var it = $.extend({}, w2toolbar.prototype.item, items[o]);
if (id == null) {
this.items.push(it);
} else {
var middle = this.get(id, true);
this.items = this.items.slice(0, middle).concat([it], this.items.slice(middle));
}
this.refresh(it.id);
this.resize();
}
},
remove: function () {
var removed = 0;
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it || String(arguments[a]).indexOf(':') != -1) continue;
removed++;
// remove from screen
$(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id)).remove();
// remove from array
var ind = this.get(it.id, true);
if (ind != null) this.items.splice(ind, 1);
}
this.resize();
return removed;
},
set: function (id, newOptions) {
var item = this.get(id);
if (item == null) return false;
$.extend(item, newOptions);
this.refresh(String(id).split(':')[0]);
return true;
},
get: function (id, returnIndex) {
if (arguments.length === 0) {
var all = [];
for (var i1 = 0; i1 < this.items.length; i1++) if (this.items[i1].id != null) all.push(this.items[i1].id);
return all;
}
var tmp = String(id).split(':');
for (var i2 = 0; i2 < this.items.length; i2++) {
var it = this.items[i2];
// find a menu item
if (['menu', 'menu-radio', 'menu-check'].indexOf(it.type) != -1 && tmp.length == 2 && it.id == tmp[0]) {
var subItems = it.items
if (typeof subItems == 'function') subItems = subItems(this);
for (var i = 0; i < subItems.length; i++) {
var item = subItems[i];
if (item.id == tmp[1] || (item.id == null && item.text == tmp[1])) {
if (returnIndex == true) return i; else return item;
}
if (Array.isArray(item.items)) {
for (var j = 0; j < item.items.length; j++) {
if (item.items[j].id == tmp[1] || (item.items[j].id == null && item.items[j].text == tmp[1])) {
if (returnIndex == true) return i; else return item.items[j];
}
}
}
}
} else if (it.id == tmp[0]) {
if (returnIndex == true) return i2; else return it;
}
}
return null;
},
show: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it) continue;
items++;
it.hidden = false;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); obj.resize(); }, 15); // needs timeout
return items;
},
hide: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it) continue;
items++;
it.hidden = true;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) { obj.refresh(tmp[t]); obj.tooltipHide(tmp[t]); } obj.resize(); }, 15); // needs timeout
return items;
},
enable: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it) continue;
items++;
it.disabled = false;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); }, 15); // needs timeout
return items;
},
disable: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it) continue;
items++;
it.disabled = true;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) { obj.refresh(tmp[t]); obj.tooltipHide(tmp[t]); } }, 15); // needs timeout
return items;
},
check: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it || String(arguments[a]).indexOf(':') != -1) continue;
items++;
it.checked = true;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () { for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]); }, 15); // needs timeout
return items;
},
uncheck: function () {
var obj = this;
var items = 0;
var tmp = [];
for (var a = 0; a < arguments.length; a++) {
var it = this.get(arguments[a]);
if (!it || String(arguments[a]).indexOf(':') != -1) continue;
// remove overlay
if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(it.type) != -1 && it.checked) {
// hide overlay
setTimeout(function () {
var el = $('#tb_'+ obj.name +'_item_'+ w2utils.escapeId(it.id));
el.w2overlay({ name: obj.name, data: { "tb-item": it.id }});
}, 1);
}
items++;
it.checked = false;
tmp.push(String(arguments[a]).split(':')[0]);
}
setTimeout(function () {
for (var t=0; t<tmp.length; t++) obj.refresh(tmp[t]);
}, 15); // needs timeout
return items;
},
click: function (id, event) {
var obj = this;
// click on menu items
var tmp = String(id).split(':');
var it = this.get(tmp[0]);
var items = (it && it.items ? w2obj.field.prototype.normMenu.call(this, it.items, it) : []);
if (tmp.length > 1) {
var subItem = this.get(id);
if (subItem && !subItem.disabled) {
obj.menuClick({ name: obj.name, item: it, subItem: subItem, originalEvent: event });
}
return;
}
if (it && !it.disabled) {
// event before
var edata = this.trigger({ phase: 'before', type: 'click', target: (id != null ? id : this.name),
item: it, object: it, originalEvent: event });
if (edata.isCancelled === true) return;
var btn = '#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id) +' table.w2ui-button';
$(btn).removeClass('down'); // need to requery at the moment -- as well as elsewhere in this function
if (it.type == 'radio') {
for (var i = 0; i < this.items.length; i++) {
var itt = this.items[i];
if (itt == null || itt.id == it.id || itt.type !== 'radio') continue;
if (itt.group == it.group && itt.checked) {
itt.checked = false;
this.refresh(itt.id);
}
}
it.checked = true;
$(btn).addClass('checked');
}
if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(it.type) != -1) {
obj.tooltipHide(id);
if (it.checked) {
// if it was already checked, second click will hide it
setTimeout(function () {
// hide overlay
var el = $('#tb_'+ obj.name +'_item_'+ w2utils.escapeId(it.id));
el.w2overlay({ name: obj.name, data: { "tb-item": it.id }});
// uncheck
it.checked = false;
obj.refresh(it.id);
}, 1);
} else {
// show overlay
setTimeout(function () {
var el = $('#tb_'+ obj.name +'_item_'+ w2utils.escapeId(it.id));
if (!$.isPlainObject(it.overlay)) it.overlay = {};
var left = (el.width() - 50) / 2;
if (left > 19) left = 19;
if (it.type == 'drop') {
el.w2overlay(it.html, $.extend({ name: obj.name, left: left, top: 3, data: { "tb-item": it.id } }, it.overlay, {
onHide: function (event) {
hideDrop();
}
}));
}
if (['menu', 'menu-radio', 'menu-check'].indexOf(it.type) != -1) {
var menuType = 'normal';
if (it.type == 'menu-radio') {
menuType = 'radio';
items.forEach(function (item) {
if (it.selected == item.id) item.checked = true; else item.checked = false;
});
}
if (it.type == 'menu-check') {
menuType = 'check';
items.forEach(function (item) {
if ($.isArray(it.selected) && it.selected.indexOf(item.id) != -1) item.checked = true; else item.checked = false;
});
}
el.w2menu($.extend({ name: obj.name, items: items, left: left, top: 3, data: { "tb-item": it.id } }, it.overlay, {
type: menuType,
remove: function (event) {
obj.menuClick({ name: obj.name, remove: true, item: it, subItem: event.item, originalEvent: event.originalEvent, keepOpen: event.keepOpen });
},
select: function (event) {
obj.menuClick({ name: obj.name, item: it, subItem: event.item, originalEvent: event.originalEvent, keepOpen: event.keepOpen });
},
onHide: function (event) {
hideDrop();
}
}));
}
if (['color', 'text-color'].indexOf(it.type) != -1) {
$(el).w2color($.extend({
color: it.color,
onHide: function (event) {
hideDrop();
if (obj._tmpColor) {
obj.colorClick({ name: obj.name, item: it, color: obj._tmpColor, final: true });
}
delete obj._tmpColor;
},
onSelect: function (color) {
if (color != null) {
obj.colorClick({ name: obj.name, item: it, color: color });
obj._tmpColor = color;
}
}
}, it.options));
}
function hideDrop(event) {
it.checked = false;
$(btn).removeClass('checked');
}
}, 1);
}
}
if (['check', 'menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(it.type) != -1) {
it.checked = !it.checked;
if (it.checked) {
$(btn).addClass('checked');
} else {
$(btn).removeClass('checked');
}
}
// route processing
if (it.route) {
var route = String('/'+ it.route).replace(/\/{2,}/g, '/');
var info = w2utils.parseRoute(route);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
}
}
setTimeout(function () { window.location.hash = route; }, 1);
}
if (event && ['button', 'check', 'radio'].indexOf(it.type) != -1) {
// need to refresh toolbar as it might be dynamic
this.tooltipShow(id, event, true);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
},
scroll: function (direction) {
var box = $(this.box);
var obj = this;
var scrollBox = box.find('.w2ui-scroll-wrapper');
var scrollLeft = scrollBox.scrollLeft();
var width1, width2, scroll;
switch (direction) {
case 'left':
width1 = scrollBox.outerWidth();
width2 = scrollBox.find(':first').outerWidth();
scroll = scrollLeft - width1 + 50; // 35 is width of both button
if (scroll <= 0) scroll = 0;
scrollBox.animate({ scrollLeft: scroll }, 300);
break;
case 'right':
width1 = scrollBox.outerWidth();
width2 = scrollBox.find(':first').outerWidth();
scroll = scrollLeft + width1 - 50; // 35 is width of both button
if (scroll >= width2 - width1) scroll = width2 - width1;
scrollBox.animate({ scrollLeft: scroll }, 300);
break;
}
setTimeout(function () { obj.resize(); }, 350);
},
render: function (box) {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
if (edata.isCancelled === true) return;
if (box != null) {
if ($(this.box).find('> table #tb_'+ this.name + '_right').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-toolbar')
.html('');
}
this.box = box;
}
if (!this.box) return;
// render all buttons
var html = '<div class="w2ui-scroll-wrapper" onmousedown="var el=w2ui[\''+ this.name +'\']; if (el) el.resize();">'+
'<table cellspacing="0" cellpadding="0" width="100%"><tbody>'+
'<tr>';
for (var i = 0; i < this.items.length; i++) {
var it = this.items[i];
if (it == null) continue;
if (it.id == null) it.id = "item_" + i;
if (it.caption != null) {
console.log('NOTICE: toolbar item.caption property is deprecated, please use item.text. Item -> ', it)
}
if (it.hint != null) {
console.log('NOTICE: toolbar item.hint property is deprecated, please use item.tooltip. Item -> ', it)
}
if (it.type == 'spacer') {
html += '<td width="100%" id="tb_'+ this.name +'_item_'+ it.id +'" align="right"></td>';
} else if (it.type == 'new-line') {
html += '<td width="100%"></td></tr></tbody></table>'
+ '<div class="w2ui-toolbar-new-line"></div>'
+ '<table cellspacing="0" cellpadding="0" width="100%"><tbody><tr>';
} else {
html += '<td id="tb_'+ this.name + '_item_'+ it.id +'" style="'+ (it.hidden ? 'display: none' : '') +'" '+
' class="'+ (it.disabled ? 'disabled' : '') +'" valign="middle">'+
'</td>';
}
}
html += '<td width="100%" id="tb_'+ this.name +'_right" align="right">'+ this.right +'</td>';
html += '</tr>'+
'</tbody></table></div>'+
'<div class="w2ui-scroll-left" onclick="var el=w2ui[\''+ this.name +'\']; if (el) el.scroll(\'left\');"></div>'+
'<div class="w2ui-scroll-right" onclick="var el=w2ui[\''+ this.name +'\']; if (el) el.scroll(\'right\');"></div>';
$(this.box)
.attr('name', this.name)
.addClass('w2ui-reset w2ui-toolbar')
.html(html);
if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
// refresh all
this.refresh();
this.resize();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
refresh: function (id) {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'refresh', target: (id != null ? id : this.name), item: this.get(id) });
if (edata.isCancelled === true) return;
// refresh all
if (id == null) {
for (var i = 0; i < this.items.length; i++) {
var it1 = this.items[i];
if (it1.id == null) it1.id = "item_" + i;
this.refresh(it1.id);
}
return;
}
// create or refresh only one item
var it = this.get(id);
if (it == null) return false;
if (typeof it.onRefresh == 'function') {
var edata2 = this.trigger({ phase: 'before', type: 'refresh', target: id, item: it, object: it });
if (edata2.isCancelled === true) return;
}
var el = $(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id));
var html = this.getItemHTML(it);
// hide tooltip
this.tooltipHide(id, {});
if (el.length === 0) {
// does not exist - create it
if (it.type == 'spacer') {
html = '<td width="100%" id="tb_'+ this.name +'_item_'+ it.id +'" align="right"></td>';
} else {
html = '<td id="tb_'+ this.name + '_item_'+ it.id +'" style="'+ (it.hidden ? 'display: none' : '') +'" '+
' class="'+ (it.disabled ? 'disabled' : '') +'" valign="middle">'+ html +
'</td>';
}
if (this.get(id, true) == this.items.length-1) {
$(this.box).find('#tb_'+ this.name +'_right').before(html);
} else {
$(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(this.items[parseInt(this.get(id, true))+1].id)).before(html);
}
} else {
if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(it.type) != -1) {
var drop = $('#w2ui-overlay-'+ this.name);
if (drop.length > 0) {
if (it.checked == false) {
drop[0].hide();
} else {
if (['menu', 'menu-radio', 'menu-check'].indexOf(it.type) != -1) {
drop.w2menu('refresh', { items: it.items });
}
}
}
}
// refresh
el.html(html);
if (it.hidden) { el.css('display', 'none'); } else { el.css('display', ''); }
if (it.disabled) { el.addClass('disabled'); } else { el.removeClass('disabled'); }
}
// event after
if (typeof it.onRefresh == 'function') {
this.trigger($.extend(edata2, { phase: 'after' }));
}
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
resize: function () {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'resize', target: this.name });
if (edata.isCancelled === true) return;
// show hide overflow buttons
var box = $(this.box);
box.find('.w2ui-scroll-left, .w2ui-scroll-right').hide();
var scrollBox = box.find('.w2ui-scroll-wrapper');
if (scrollBox.find(':first').outerWidth() > scrollBox.outerWidth()) {
// we have overflowed content
if (scrollBox.scrollLeft() > 0) {
box.find('.w2ui-scroll-left').show();
}
if (scrollBox.scrollLeft() < scrollBox.find(':first').outerWidth() - scrollBox.outerWidth()) {
box.find('.w2ui-scroll-right').show();
}
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
if (edata.isCancelled === true) return;
// clean up
if ($(this.box).find('> table #tb_'+ this.name + '_right').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-toolbar')
.html('');
}
$(this.box).html('');
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
// ========================================
// --- Internal Functions
getItemHTML: function (item) {
var html = '';
if (item.caption != null && item.text == null) item.text = item.caption; // for backward compatibility
if (item.text == null) item.text = '';
if (item.tooltip == null && item.hint != null) item.tooltip = item.hint; // for backward compatibility
if (item.tooltip == null) item.tooltip = '';
if (typeof item.get !== 'function' && (Array.isArray(item.items) || typeof item.items == 'function')) {
item.get = function (id) {
var tmp = item.items;
if (typeof tmp == 'function') tmp = item.items(item);
return tmp.find(function (it) {
if (it.id == id) return true; else return false
})
}
}
var img = '<td>&#160;</td>';
var text = (typeof item.text == 'function' ? item.text.call(this, item) : item.text);
if (item.img) img = '<td><div class="w2ui-tb-image w2ui-icon '+ item.img +'"></div></td>';
if (item.icon) {
img = '<td><div class="w2ui-tb-image"><span class="'+
(typeof item.icon == 'function' ? item.icon.call(this, item) : item.icon) +'"></span></div></td>';
}
if (html === '') switch (item.type) {
case 'color':
case 'text-color':
if (typeof item.color == 'string') {
if (item.color.substr(0,1) == '#') item.color = item.color.substr(1);
if (item.color.length == 3 || item.color.length == 6) item.color = '#' + item.color;
}
if (item.type == 'color') {
text = '<div style="height: 12px; width: 12px; margin-top: 1px; border: 1px solid #8A8A8A; border-radius: 1px; box-shadow: 0px 0px 1px #fff; '+
' background-color: '+ (item.color != null ? item.color : '#fff') +'; float: left;"></div>'+
(item.text ? '<div style="margin-left: 17px;">' + w2utils.lang(item.text) + '</div>' : '');
}
if (item.type == 'text-color') {
text = '<div style="color: '+ (item.color != null ? item.color : '#444') +';">'+
(item.text ? w2utils.lang(item.text) : '<b>Aa</b>') +
'</div>';
}
case 'menu':
case 'menu-check':
case 'menu-radio':
case 'button':
case 'check':
case 'radio':
case 'drop':
html += '<table cellpadding="0" cellspacing="0" '+
' class="w2ui-button '+ (item.checked ? 'checked' : '') +' '+ (item.class ? item.class : '') +'" '+
' onclick = "var el=w2ui[\''+ this.name + '\']; if (el) el.click(\''+ item.id +'\', event);" '+
' onmouseenter = "' + (!item.disabled ? "jQuery(this).addClass('over'); w2ui['"+ this.name +"'].tooltipShow('"+ item.id +"', event);" : "") + '"'+
' onmouseleave = "' + (!item.disabled ? "jQuery(this).removeClass('over').removeClass('down'); w2ui['"+ this.name +"'].tooltipHide('"+ item.id +"', event);" : "") + '"'+
' onmousedown = "' + (!item.disabled ? "jQuery(this).addClass('down');" : "") + '"'+
' onmouseup = "' + (!item.disabled ? "jQuery(this).removeClass('down');" : "") + '"'+
'><tbody>'+
'<tr><td>'+
' <table cellpadding="1" cellspacing="0"><tbody>'+
' <tr>' +
img +
(text !== ''
? '<td class="w2ui-tb-text w2ui-tb-caption" nowrap="nowrap" style="'+ (item.style ? item.style : '') +'">'+ w2utils.lang(text) +'</td>'
: ''
) +
(item.count != null
? '<td class="w2ui-tb-count" nowrap="nowrap"><span>'+ item.count +'</span></td>'
: ''
) +
(((['menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(item.type) != -1) && item.arrow !== false) ?
'<td class="w2ui-tb-down" nowrap="nowrap"><div></div></td>' : '') +
' </tr></tbody></table>'+
'</td></tr></tbody></table>';
break;
case 'break':
html += '<table cellpadding="0" cellspacing="0"><tbody><tr>'+
' <td><div class="w2ui-break">&#160;</div></td>'+
'</tr></tbody></table>';
break;
case 'html':
html += '<table cellpadding="0" cellspacing="0"><tbody><tr>'+
' <td nowrap="nowrap">' + (typeof item.html == 'function' ? item.html.call(this, item) : item.html) + '</td>'+
'</tr></tbody></table>';
break;
}
return '<div>' + html + '</div>';
},
tooltipShow: function (id, event, forceRefresh) {
if (this.tooltip == null) return;
var $el = $(this.box).find('#tb_'+ this.name + '_item_'+ w2utils.escapeId(id));
var item = this.get(id);
var pos = this.tooltip;
var txt = item.tooltip;
if (typeof txt == 'function') txt = txt.call(this, item);
clearTimeout(this._tooltipTimer);
this._tooltipTimer = setTimeout(function () {
if ($el.prop('_mouse_tooltip') !== true) {
$el.prop('_mouse_tooltip', true);
// show tooltip
if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 'text-color'].indexOf(item.type) != -1 && item.checked == true) return; // not for opened drop downs
$el.w2tag(w2utils.lang(txt), { position: pos });
}
}, 0);
// refresh only
if ($el.prop('_mouse_tooltip') && forceRefresh == true) {
$el.w2tag(w2utils.lang(txt), { position: pos });
}
},
tooltipHide: function (id, event) {
if (this.tooltip == null) return;
var $el = $(this.box).find('#tb_'+ this.name + '_item_'+ w2utils.escapeId(id));
var item = this.get(id);
clearTimeout(this._tooltipTimer);
setTimeout(function () {
if ($el.prop('_mouse_tooltip') === true) {
$el.removeProp('_mouse_tooltip');
// hide tooltip
$el.w2tag();
}
}, 1);
},
menuClick: function (event) {
var obj = this;
if (event.item && !event.item.disabled) {
// event before
var edata = this.trigger({ phase: 'before', type: (event.remove !== true ? 'click' : 'remove'), target: event.item.id + ':' + event.subItem.id, item: event.item,
subItem: event.subItem, originalEvent: event.originalEvent });
if (edata.isCancelled === true) return;
// route processing
var it = event.subItem;
var item = this.get(event.item.id);
var items = item.items;
if (typeof items == 'function') items = item.items();
if (item.type == 'menu-radio') {
item.selected = it.id;
if (Array.isArray(items)) {
items.forEach(function (item) {
if (item.checked === true) delete item.checked;
if (Array.isArray(item.items)) {
item.items.forEach(function (item) {
if (item.checked === true) delete item.checked;
})
}
});
}
it.checked = true;
}
if (item.type == 'menu-check') {
if (!$.isArray(item.selected)) item.selected = [];
if (it.group == null) {
var ind = item.selected.indexOf(it.id);
if (ind == -1) {
item.selected.push(it.id);
it.checked = true;
} else {
item.selected.splice(ind, 1);
it.checked = false;
}
} else if (it.group === false) {
// if group is false, then it is not part of checkboxes
} else {
var unchecked = [];
// recursive
(function checkNested(items) {
items.forEach(function (sub) {
if (sub.group === it.group) {
var ind = item.selected.indexOf(sub.id);
if (ind != -1) {
if (sub.id != it.id) unchecked.push(sub.id);
item.selected.splice(ind, 1);
}
}
if (Array.isArray(sub.items)) checkNested(sub.items)
});
})(items);
var ind = item.selected.indexOf(it.id);
if (ind == -1) {
item.selected.push(it.id);
it.checked = true;
}
}
}
if (typeof it.route == 'string') {
var route = it.route !== '' ? String('/'+ it.route).replace(/\/{2,}/g, '/') : '';
var info = w2utils.parseRoute(route);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (obj.routeData[info.keys[k].name] == null) continue;
route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
}
}
setTimeout(function () { window.location.hash = route; }, 1);
}
this.refresh(event.item.id);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
},
colorClick: function (event) {
var obj = this;
if (event.item && !event.item.disabled) {
// event before
var edata = this.trigger({ phase: 'before', type: 'click', target: event.item.id, item: event.item,
color: event.color, final: event.final, originalEvent: event.originalEvent });
if (edata.isCancelled === true) return;
// default behavior
event.item.color = event.color;
obj.refresh(event.item.id);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
}
}
};
$.extend(w2toolbar.prototype, w2utils.event);
w2obj.toolbar = w2toolbar;
})(jQuery);
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2sidebar - sidebar widget
* - $().w2sidebar - jQuery wrapper
* - Dependencies: jQuery, w2utils
*
* == NICE TO HAVE ==
* - add find() method to find nodes by a specific criteria (I want all nodes for exampe)
* - dbl click should be like it is in grid (with timer not HTML dbl click event)
* - reorder with dgrag and drop
* - node.style is misleading - should be there to apply color for example
* - add multiselect
* - node.caption - deprecated
* - node.text - can be a function
* - node.icon - can be a function
*
* == 1.5 changes
* - node.class - ne property
* - sb.levelPadding
* - sb.handle (for debugger points)
* - node.style
* - sb.updte()
*
************************************************************************/
(function ($) {
var w2sidebar = function (options) {
this.name = null;
this.box = null;
this.sidebar = null;
this.parent = null;
this.nodes = []; // Sidebar child nodes
this.menu = [];
this.routeData = {}; // data for dynamic routes
this.selected = null; // current selected node (readonly)
this.img = null;
this.icon = null;
this.style = '';
this.topHTML = '';
this.bottomHTML = '';
this.flatButton = false;
this.keyboard = true;
this.flat = false;
this.hasFocus = false;
$.extend(true, this, w2obj.sidebar, options);
};
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2sidebar = function(method) {
if ($.isPlainObject(method)) {
// check name parameter
if (!w2utils.checkName(method, 'w2sidebar')) return;
// extend items
var nodes = method.nodes;
var object = new w2sidebar(method);
$.extend(object, { handlers: [], nodes: [] });
if (nodes != null) {
object.add(object, nodes);
}
// register new object
object.sidebar = object;
w2ui[object.name] = object;
// render
if ($(this).length !== 0) {
object.render($(this)[0]);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
};
// ====================================================
// -- Implementation of core functionality
w2sidebar.prototype = {
levelPadding : 12,
handle : { size: 0, style: '', content: '' },
onClick : null, // Fire when user click on Node Text
onDblClick : null, // Fire when user dbl clicks
onContextMenu : null,
onMenuClick : null, // when context menu item selected
onExpand : null, // Fire when node Expands
onCollapse : null, // Fire when node Colapses
onKeydown : null,
onRender : null,
onRefresh : null,
onResize : null,
onDestroy : null,
onFocus : null,
onBlur : null,
onFlat : null,
node: {
id : null,
text : '',
count : null,
img : null,
icon : null,
nodes : [],
style : '', // additional style for subitems
route : null,
selected : false,
expanded : false,
hidden : false,
disabled : false,
group : false, // if true, it will build as a group
groupShowHide : true,
collapsible : false,
plus : false, // if true, plus will be shown even if there is no sub nodes
// events
onClick : null,
onDblClick : null,
onContextMenu : null,
onExpand : null,
onCollapse : null,
// internal
parent : null, // node object
sidebar : null
},
add: function (parent, nodes) {
if (arguments.length == 1) {
// need to be in reverse order
nodes = arguments[0];
parent = this;
}
if (typeof parent == 'string') parent = this.get(parent);
return this.insert(parent, null, nodes);
},
insert: function (parent, before, nodes) {
var txt, ind, tmp, node, nd;
if (arguments.length == 2) {
// need to be in reverse order
nodes = arguments[1];
before = arguments[0];
if (before != null) {
ind = this.get(before);
if (ind == null) {
if (!$.isArray(nodes)) nodes = [nodes];
if (nodes[0].caption != null && nodes[0].text == null) {
console.log('NOTICE: sidebar node.caption property is deprecated, please use node.text. Node -> ', nodes[0]);
nodes[0].text = nodes[0].caption;
}
txt = nodes[0].text;
console.log('ERROR: Cannot insert node "'+ txt +'" because cannot find node "'+ before +'" to insert before.');
return null;
}
parent = this.get(before).parent;
} else {
parent = this;
}
}
if (typeof parent == 'string') parent = this.get(parent);
if (!$.isArray(nodes)) nodes = [nodes];
for (var o = 0; o < nodes.length; o++) {
node = nodes[o];
if (typeof node.id == null) {
if (node.caption != null && node.text == null) {
console.log('NOTICE: sidebar node.caption property is deprecated, please use node.text');
node.text = node.caption;
}
txt = node.text;
console.log('ERROR: Cannot insert node "'+ txt +'" because it has no id.');
continue;
}
if (this.get(this, node.id) != null) {
console.log('ERROR: Cannot insert node with id='+ node.id +' (text: '+ node.text + ') because another node with the same id already exists.');
continue;
}
tmp = $.extend({}, w2sidebar.prototype.node, node);
tmp.sidebar = this;
tmp.parent = parent;
nd = tmp.nodes || [];
tmp.nodes = []; // very important to re-init empty nodes array
if (before == null) { // append to the end
parent.nodes.push(tmp);
} else {
ind = this.get(parent, before, true);
if (ind == null) {
console.log('ERROR: Cannot insert node "'+ node.text +'" because cannot find node "'+ before +'" to insert before.');
return null;
}
parent.nodes.splice(ind, 0, tmp);
}
if (nd.length > 0) {
this.insert(tmp, null, nd);
}
}
this.refresh(parent.id);
return tmp;
},
remove: function () { // multiple arguments
var deleted = 0;
var tmp;
for (var a = 0; a < arguments.length; a++) {
tmp = this.get(arguments[a]);
if (tmp == null) continue;
if (this.selected != null && this.selected === tmp.id) {
this.selected = null;
}
var ind = this.get(tmp.parent, arguments[a], true);
if (ind == null) continue;
if (tmp.parent.nodes[ind].selected) tmp.sidebar.unselect(tmp.id);
tmp.parent.nodes.splice(ind, 1);
deleted++;
}
if (deleted > 0 && arguments.length == 1) this.refresh(tmp.parent.id); else this.refresh();
return deleted;
},
set: function (parent, id, node) {
if (arguments.length == 2) {
// need to be in reverse order
node = id;
id = parent;
parent = this;
}
// searches all nested nodes
if (typeof parent == 'string') parent = this.get(parent);
if (parent.nodes == null) return null;
for (var i = 0; i < parent.nodes.length; i++) {
if (parent.nodes[i].id === id) {
// see if quick update is possible
var res = this.update(id, node)
if (Object.keys(res).length != 0) {
// make sure nodes inserted correctly
var nodes = node.nodes;
$.extend(parent.nodes[i], node, { nodes: [] });
if (nodes != null) {
this.add(parent.nodes[i], nodes);
}
this.refresh(id);
}
return true;
} else {
var rv = this.set(parent.nodes[i], id, node);
if (rv) return true;
}
}
return false;
},
get: function (parent, id, returnIndex) { // can be just called get(id) or get(id, true)
if (arguments.length === 0) {
var all = [];
var tmp = this.find({});
for (var t = 0; t < tmp.length; t++) {
if (tmp[t].id != null) all.push(tmp[t].id);
}
return all;
} else {
if (arguments.length == 1 || (arguments.length == 2 && id === true) ) {
// need to be in reverse order
returnIndex = id;
id = parent;
parent = this;
}
// searches all nested nodes
if (typeof parent == 'string') parent = this.get(parent);
if (parent.nodes == null) return null;
for (var i = 0; i < parent.nodes.length; i++) {
if (parent.nodes[i].id == id) {
if (returnIndex === true) return i; else return parent.nodes[i];
} else {
var rv = this.get(parent.nodes[i], id, returnIndex);
if (rv || rv === 0) return rv;
}
}
return null;
}
},
find: function (parent, params, results) { // can be just called find({ selected: true })
if (arguments.length == 1) {
// need to be in reverse order
params = parent;
parent = this;
}
if (!results) results = [];
// searches all nested nodes
if (typeof parent == 'string') parent = this.get(parent);
if (parent.nodes == null) return results;
for (var i = 0; i < parent.nodes.length; i++) {
var match = true;
for (var prop in params) { // params is an object
if (parent.nodes[i][prop] != params[prop]) match = false;
}
if (match) results.push(parent.nodes[i]);
if (parent.nodes[i].nodes.length > 0) results = this.find(parent.nodes[i], params, results);
}
return results;
},
hide: function () { // multiple arguments
var effected = 0;
for (var a = 0; a < arguments.length; a++) {
var tmp = this.get(arguments[a]);
if (tmp == null || tmp.hidden === true) continue;
tmp.hidden = true;
effected++;
}
if (effected > 0) {
if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
}
return effected;
},
show: function () { // multiple arguments
var effected = 0;
for (var a = 0; a < arguments.length; a++) {
var tmp = this.get(arguments[a]);
if (tmp == null || tmp.hidden === false) continue;
tmp.hidden = false;
effected++;
}
if (effected > 0) {
if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
}
return effected;
},
disable: function () { // multiple arguments
var effected = 0;
for (var a = 0; a < arguments.length; a++) {
var tmp = this.get(arguments[a]);
if (tmp == null || tmp.disabled === true) continue;
tmp.disabled = true;
if (tmp.selected) this.unselect(tmp.id);
effected++;
}
if (effected > 0) {
if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
}
return effected;
},
enable: function () { // multiple arguments
var effected = 0;
for (var a = 0; a < arguments.length; a++) {
var tmp = this.get(arguments[a]);
if (tmp == null || tmp.disabled === false) continue;
tmp.disabled = false;
effected++;
}
if (effected > 0) {
if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
}
return effected;
},
select: function (id) {
// var obj = this;
var new_node = this.get(id);
if (!new_node) return false;
if (this.selected == id && new_node.selected) return false;
this.unselect(this.selected);
var $el = $(this.box).find('#node_'+ w2utils.escapeId(id));
$el.addClass('w2ui-selected')
.find('.w2ui-icon')
.addClass('w2ui-icon-selected')
if ($el.length > 0) {
this.scrollIntoView(id, true)
}
new_node.selected = true;
this.selected = id;
return true;
},
unselect: function (id) {
// if no arguments provided, unselect selected node
if (arguments.length === 0) {
id = this.selected;
}
var current = this.get(id);
if (!current) return false;
current.selected = false;
$(this.box).find('#node_'+ w2utils.escapeId(id))
.removeClass('w2ui-selected')
.find('.w2ui-icon').removeClass('w2ui-icon-selected');
if (this.selected == id) this.selected = null;
return true;
},
toggle: function(id) {
var nd = this.get(id);
if (nd == null) return false;
if (nd.plus) {
this.set(id, { plus: false });
this.expand(id);
this.refresh(id);
return;
}
if (nd.nodes.length === 0) return false;
if (!nd.collapsible) return false;
if (this.get(id).expanded) return this.collapse(id); else return this.expand(id);
},
collapse: function (id) {
var obj = this;
var nd = this.get(id);
if (nd == null) return false
// event before
var edata = this.trigger({ phase: 'before', type: 'collapse', target: id, object: nd });
if (edata.isCancelled === true) return;
// default action
$(this.box).find('#node_'+ w2utils.escapeId(id) +'_sub').slideUp(200);
$(this.box).find('#node_'+ w2utils.escapeId(id) +' .w2ui-expanded').removeClass('w2ui-expanded').addClass('w2ui-collapsed')
nd.expanded = false;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
setTimeout(function () { obj.refresh(id); }, 200);
return true;
},
collapseAll: function (parent) {
if (parent == null) parent = this;
if (typeof parent == 'string') parent = this.get(parent);
if (parent.nodes == null) return false;
for (var i = 0; i < parent.nodes.length; i++) {
if (parent.nodes[i].expanded === true) parent.nodes[i].expanded = false;
if (parent.nodes[i].nodes && parent.nodes[i].nodes.length > 0) this.collapseAll(parent.nodes[i]);
}
this.refresh(parent.id);
return true;
},
expand: function (id) {
var obj = this;
var nd = this.get(id);
// event before
var edata = this.trigger({ phase: 'before', type: 'expand', target: id, object: nd });
if (edata.isCancelled === true) return;
// default action
$(this.box).find('#node_'+ w2utils.escapeId(id) +'_sub').slideDown(200);
$(this.box).find('#node_'+ w2utils.escapeId(id) +' .w2ui-collapsed').removeClass('w2ui-collapsed').addClass('w2ui-expanded')
nd.expanded = true;
// event after
this.trigger($.extend(edata, { phase: 'after' }));
setTimeout(function () { obj.refresh(id); }, 200);
return true;
},
expandAll: function (parent) {
if (parent == null) parent = this;
if (typeof parent == 'string') parent = this.get(parent);
if (parent.nodes == null) return false;
for (var i = 0; i < parent.nodes.length; i++) {
if (parent.nodes[i].expanded === false) parent.nodes[i].expanded = true;
if (parent.nodes[i].nodes && parent.nodes[i].nodes.length > 0) this.expandAll(parent.nodes[i]);
}
this.refresh(parent.id);
},
expandParents: function (id) {
var node = this.get(id);
if (node == null) return false;
if (node.parent) {
if (!node.parent.expanded) {
node.parent.expanded = true;
this.refresh(node.parent.id);
}
this.expandParents(node.parent.id);
}
return true;
},
click: function (id, event) {
var obj = this;
var nd = this.get(id);
if (nd == null) return;
if (nd.disabled || nd.group) return; // should click event if already selected
// unselect all previsously
$(obj.box).find('.w2ui-node.w2ui-selected').each(function (index, el) {
var oldID = $(el).attr('id').replace('node_', '');
var oldNode = obj.get(oldID);
if (oldNode != null) oldNode.selected = false;
$(el).removeClass('w2ui-selected').find('.w2ui-icon').removeClass('w2ui-icon-selected');
});
// select new one
var newNode = $(obj.box).find('#node_'+ w2utils.escapeId(id));
var oldNode = $(obj.box).find('#node_'+ w2utils.escapeId(obj.selected));
newNode.addClass('w2ui-selected').find('.w2ui-icon').addClass('w2ui-icon-selected');
// need timeout to allow rendering
setTimeout(function () {
// event before
var edata = obj.trigger({ phase: 'before', type: 'click', target: id, originalEvent: event, node: nd, object: nd });
if (edata.isCancelled === true) {
// restore selection
newNode.removeClass('w2ui-selected').find('.w2ui-icon').removeClass('w2ui-icon-selected');
oldNode.addClass('w2ui-selected').find('.w2ui-icon').addClass('w2ui-icon-selected');
return;
}
// default action
if (oldNode != null) oldNode.selected = false;
obj.get(id).selected = true;
obj.selected = id;
// route processing
if (typeof nd.route == 'string') {
var route = nd.route !== '' ? String('/'+ nd.route).replace(/\/{2,}/g, '/') : '';
var info = w2utils.parseRoute(route);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (obj.routeData[info.keys[k].name] == null) continue;
route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
}
}
setTimeout(function () { window.location.hash = route; }, 1);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}, 1);
},
focus: function (event) {
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', type: 'focus', target: this.name, originalEvent: event });
if (edata.isCancelled === true) return false;
// default behaviour
this.hasFocus = true;
$(this.box).find('.w2ui-sidebar-body').addClass('w2ui-focus');
setTimeout(function () {
var $input = $(obj.box).find('#sidebar_'+ obj.name + '_focus');
if (!$input.is(':focus')) $input.focus();
}, 10);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
blur: function (event) {
// event before
var edata = this.trigger({ phase: 'before', type: 'blur', target: this.name, originalEvent: event });
if (edata.isCancelled === true) return false;
// default behaviour
this.hasFocus = false;
$(this.box).find('.w2ui-sidebar-body').removeClass('w2ui-focus');
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
keydown: function (event) {
var obj = this;
var nd = obj.get(obj.selected);
if (obj.keyboard !== true) return;
if (!nd) nd = obj.nodes[0];
// trigger event
var edata = obj.trigger({ phase: 'before', type: 'keydown', target: obj.name, originalEvent: event });
if (edata.isCancelled === true) return;
// default behaviour
if (event.keyCode == 13 || event.keyCode == 32) { // enter or space
if (nd.nodes.length > 0) obj.toggle(obj.selected);
}
if (event.keyCode == 37) { // left
if (nd.nodes.length > 0 && nd.expanded) {
obj.collapse(obj.selected);
} else {
selectNode(nd.parent);
if (!nd.parent.group) obj.collapse(nd.parent.id);
}
}
if (event.keyCode == 39) { // right
if ((nd.nodes.length > 0 || nd.plus) && !nd.expanded) obj.expand(obj.selected);
}
if (event.keyCode == 38) { // up
if (obj.get(obj.selected) == null) {
selectNode(this.nodes[0] || null);
} else {
selectNode(neighbor(nd, prev));
}
}
if (event.keyCode == 40) { // down
if (obj.get(obj.selected) == null) {
selectNode(this.nodes[0] || null);
} else {
selectNode(neighbor(nd, next));
}
}
// cancel event if needed
if ($.inArray(event.keyCode, [13, 32, 37, 38, 39, 40]) != -1) {
if (event.preventDefault) event.preventDefault();
if (event.stopPropagation) event.stopPropagation();
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
function selectNode (node, event) {
if (node != null && !node.hidden && !node.disabled && !node.group) {
obj.click(node.id, event);
setTimeout(function () { obj.scrollIntoView(); }, 50);
}
}
function neighbor (node, neighborFunc) {
node = neighborFunc(node);
while (node != null && (node.hidden || node.disabled)) {
if (node.group) break; else node = neighborFunc(node);
}
return node;
}
function next (node, noSubs) {
if (node == null) return null;
var parent = node.parent;
var ind = obj.get(node.id, true);
var nextNode = null;
// jump inside
if (node.expanded && node.nodes.length > 0 && noSubs !== true) {
var t = node.nodes[0];
if (t.hidden || t.disabled || t.group) nextNode = next(t); else nextNode = t;
} else {
if (parent && ind + 1 < parent.nodes.length) {
nextNode = parent.nodes[ind + 1];
} else {
nextNode = next(parent, true); // jump to the parent
}
}
if (nextNode != null && (nextNode.hidden || nextNode.disabled || nextNode.group)) nextNode = next(nextNode);
return nextNode;
}
function prev (node) {
if (node == null) return null;
var parent = node.parent;
var ind = obj.get(node.id, true);
var prevNode = (ind > 0) ? lastChild(parent.nodes[ind - 1]) : parent;
if (prevNode != null && (prevNode.hidden || prevNode.disabled || prevNode.group)) prevNode = prev(prevNode);
return prevNode;
}
function lastChild (node) {
if (node.expanded && node.nodes.length > 0) {
var t = node.nodes[node.nodes.length - 1];
if (t.hidden || t.disabled || t.group) return prev(t); else return lastChild(t);
}
return node;
}
},
scrollIntoView: function (id, instant) {
if (id == null) id = this.selected;
var nd = this.get(id);
if (nd == null) return;
var body = $(this.box).find('.w2ui-sidebar-body');
var item = $(this.box).find('#node_'+ w2utils.escapeId(id));
var offset = item.offset().top - body.offset().top;
if (offset + item.height() > body.height() || offset <= 0) {
body.animate({ 'scrollTop': body.scrollTop() + offset - body.height() / 2 + item.height() }, instant ? 0 : 250, 'linear');
}
},
dblClick: function (id, event) {
var nd = this.get(id);
// event before
var edata = this.trigger({ phase: 'before', type: 'dblClick', target: id, originalEvent: event, object: nd });
if (edata.isCancelled === true) return;
// default action
this.toggle(id);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
contextMenu: function (id, event) {
var obj = this;
var nd = obj.get(id);
if (id != obj.selected) obj.click(id);
// event before
var edata = obj.trigger({ phase: 'before', type: 'contextMenu', target: id, originalEvent: event, object: nd, allowOnDisabled: false });
if (edata.isCancelled === true) return;
// default action
if (nd.disabled && !edata.allowOnDisabled) return;
if (obj.menu.length > 0) {
$(obj.box).find('#node_'+ w2utils.escapeId(id))
.w2menu({
items: obj.menu,
contextMenu: true,
originalEvent: event,
onSelect: function (event) {
obj.menuClick(id, parseInt(event.index), event.originalEvent);
}
}
);
}
// cancel event
if (event.preventDefault) event.preventDefault();
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
},
menuClick: function (itemId, index, event) {
var obj = this;
// event before
var edata = obj.trigger({ phase: 'before', type: 'menuClick', target: itemId, originalEvent: event, menuIndex: index, menuItem: obj.menu[index] });
if (edata.isCancelled === true) return;
// default action
// -- empty
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
},
goFlat: function () {
// event before
var edata = this.trigger({ phase: 'before', type: 'flat', goFlat: !this.flat });
if (edata.isCancelled === true) return;
// default action
this.flat = !this.flat;
this.refresh();
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
render: function (box) {
var time = (new Date()).getTime();
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
if (edata.isCancelled === true) return;
// default action
if (box != null) {
if ($(this.box).find('> div > div.w2ui-sidebar-body').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-sidebar')
.html('');
}
this.box = box;
}
if (!this.box) return;
$(this.box)
.attr('name', this.name)
.addClass('w2ui-reset w2ui-sidebar')
.html('<div>'+
'<input id="sidebar_'+ this.name +'_focus" style="position: absolute; top: 0; right: 0; width: 1px; z-index: -1; opacity: 0" '+ (w2utils.isIOS ? 'readonly' : '') +'/>'+
'<div class="w2ui-sidebar-top"></div>' +
'<div class="w2ui-sidebar-body"></div>'+
'<div class="w2ui-sidebar-bottom"></div>'+
'</div>'
);
$(this.box).find('> div').css({
width : $(this.box).width() + 'px',
height : $(this.box).height() + 'px'
});
if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
// adjust top and bottom
var flatHTML = '';
if (this.flatButton == true) {
flatHTML = '<div class="w2ui-flat-'+ (this.flat ? 'right' : 'left') +'" onclick="w2ui[\''+ this.name +'\'].goFlat()"></div>';
}
if (this.topHTML !== '' || flatHTML !== '') {
$(this.box).find('.w2ui-sidebar-top').html(this.topHTML + flatHTML);
$(this.box).find('.w2ui-sidebar-body')
.css('top', $(this.box).find('.w2ui-sidebar-top').height() + 'px');
}
if (this.bottomHTML !== '') {
$(this.box).find('.w2ui-sidebar-bottom').html(this.bottomHTML);
$(this.box).find('.w2ui-sidebar-body')
.css('bottom', $(this.box).find('.w2ui-sidebar-bottom').height() + 'px');
}
// focus
var kbd_timer;
$(this.box).find('#sidebar_'+ this.name + '_focus')
.on('focus', function (event) {
clearTimeout(kbd_timer);
if (!obj.hasFocus) obj.focus(event);
})
.on('blur', function (event) {
kbd_timer = setTimeout(function () {
if (obj.hasFocus) { obj.blur(event); }
}, 100);
})
.on('keydown', function (event) {
if (event.keyCode != 9) { // not tab
w2ui[obj.name].keydown.call(w2ui[obj.name], event);
}
});
$(this.box).off('mousedown').on('mousedown', function (event) {
// set focus to grid
setTimeout(function () {
// if input then do not focus
if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(event.target.tagName.toUpperCase()) == -1) {
var $input = $(obj.box).find('#sidebar_'+ obj.name + '_focus');
if (!$input.is(':focus')) {
if ($(event.target).hasClass('w2ui-node')) {
var top = $(event.target).position().top + $(obj.box).find('.w2ui-sidebar-top').height() + event.offsetY;
$input.css({ top: top + 'px', left: '0px' });
}
$input.focus();
}
}
}, 1);
});
// event after
this.trigger($.extend(edata, { phase: 'after' }));
// ---
this.refresh();
return (new Date()).getTime() - time;
},
update: function (id, options) {
// quick function to refresh just this item (not sub nodes)
// - icon, class, style, text, count
var nd = this.get(id)
var level
if (nd) {
var $el = $(this.box).find('#node_'+ nd.id)
if (nd.group) {
if (options.text) {
nd.text = options.text
$el.find('.w2ui-group-text').replaceWith(typeof nd.text == 'function'
? nd.text.call(this, nd)
: '<span class="w2ui-group-text">'+ nd.text +'</span>')
delete options.text
}
if (options.class) {
nd.class = options.class
level = $el.data('level')
$el[0].className = 'w2ui-node-group w2ui-level-'+ level +(nd.class ? ' ' + nd.class : '')
delete options.class
}
if (options.style) {
nd.style = options.style
$el.next()[0].style = nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;')
delete options.style
}
} else {
if (options.icon) {
var $img = $el.find('.w2ui-node-image > span')
if ($img.length > 0) {
nd.icon = options.icon
$img[0].className = (typeof nd.icon == 'function' ? nd.icon.call(this, nd) : nd.icon)
delete options.icon
}
}
if (options.count) {
nd.count = options.count
$el.find('.w2ui-node-count').html(nd.count)
if ($el.find('.w2ui-node-count').length > 0) delete options.count
}
if (options.class && $el.length > 0) {
nd.class = options.class
level = $el.data('level')
$el[0].className = 'w2ui-node w2ui-level-'+ level + (nd.selected ? ' w2ui-selected' : '') + (nd.disabled ? ' w2ui-disabled' : '') + (nd.class ? ' ' + nd.class : '')
delete options.class
}
if (options.text) {
nd.text = options.text
$el.find('.w2ui-node-text').html(typeof nd.text == 'function' ? nd.text.call(this, nd) : nd.text)
delete options.text
}
if (options.style && $el.length > 0) {
var $txt = $el.find('.w2ui-node-text')
nd.style = options.style
$txt[0].style = nd.style
delete options.style
}
}
}
// return what was not set
return options
},
refresh: function (id) {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'refresh', target: (id != null ? id : this.name),
fullRefresh: (id != null ? false : true) });
if (edata.isCancelled === true) return;
// adjust top and bottom
var flatHTML = '';
if (this.flatButton == true) {
flatHTML = '<div class="w2ui-flat-'+ (this.flat ? 'right' : 'left') +'" onclick="w2ui[\''+ this.name +'\'].goFlat()"></div>';
}
if (this.topHTML !== '' || flatHTML !== '') {
$(this.box).find('.w2ui-sidebar-top').html(this.topHTML + flatHTML);
$(this.box).find('.w2ui-sidebar-body')
.css('top', $(this.box).find('.w2ui-sidebar-top').height() + 'px');
}
if (this.bottomHTML !== '') {
$(this.box).find('.w2ui-sidebar-bottom').html(this.bottomHTML);
$(this.box).find('.w2ui-sidebar-body')
.css('bottom', $(this.box).find('.w2ui-sidebar-bottom').height() + 'px');
}
// default action
$(this.box).find('> div').removeClass('w2ui-sidebar-flat').addClass(this.flat ? 'w2ui-sidebar-flat' : '').css({
width : $(this.box).width() + 'px',
height: $(this.box).height() + 'px'
});
// if no parent - reset nodes
if (this.nodes.length > 0 && this.nodes[0].parent == null) {
var tmp = this.nodes;
this.nodes = [];
this.add(this, tmp);
}
var obj = this;
var node, nd;
var nm;
if (id == null) {
node = this;
nm = '.w2ui-sidebar-body';
} else {
node = this.get(id);
if (node == null) return;
nm = '#node_'+ w2utils.escapeId(node.id) + '_sub';
}
var nodeHTML;
if (node !== this) {
var tmp = '#node_'+ w2utils.escapeId(node.id);
nodeHTML = getNodeHTML(node);
$(this.box).find(tmp).before('<div id="sidebar_'+ this.name + '_tmp"></div>');
$(this.box).find(tmp).remove();
$(this.box).find(nm).remove();
$('#sidebar_'+ this.name + '_tmp').before(nodeHTML);
$('#sidebar_'+ this.name + '_tmp').remove();
}
// remember scroll position
var scroll = {
top: $(this.box).find(nm).scrollTop(),
left: $(this.box).find(nm).scrollLeft()
}
// refresh sub nodes
$(this.box).find(nm).html('');
for (var i = 0; i < node.nodes.length; i++) {
nd = node.nodes[i];
nodeHTML = getNodeHTML(nd);
$(this.box).find(nm).append(nodeHTML);
if (nd.nodes.length !== 0) {
this.refresh(nd.id);
} else {
// trigger event
var edata2 = this.trigger({ phase: 'before', type: 'refresh', target: nd.id });
if (edata2.isCancelled === true) return;
// event after
this.trigger($.extend(edata2, { phase: 'after' }));
}
}
// reset scroll
$(this.box).find(nm).scrollLeft(scroll.left).scrollTop(scroll.top)
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
function getNodeHTML(nd) {
var html = '';
var img = nd.img;
var icon = nd.icon;
if (icon == null && img == null) {
if (icon == null) icon = obj.icon;
if (img == null) img = obj.img;
}
// -- find out level
var tmp = nd.parent;
var level = 0;
while (tmp && tmp.parent != null) {
// if (tmp.group) level--;
tmp = tmp.parent;
level++;
}
if (nd.caption != null && nd.text == null) nd.text = nd.caption;
if (nd.caption != null) {
console.log('NOTICE: sidebar node.caption property is deprecated, please use node.text. Node -> ', nd)
nd.text = nd.caption;
}
if (Array.isArray(nd.nodes) && nd.nodes.length > 0) nd.collapsible = true
if (nd.group) {
html =
'<div class="w2ui-node-group w2ui-level-'+ level + (nd.class ? ' ' + nd.class : '') +'" id="node_'+ nd.id +'" data-level="'+ level + '"'+
' style="'+ (nd.hidden ? 'display: none' : '') +'" onclick="w2ui[\''+ obj.name +'\'].toggle(\''+ nd.id +'\')"'+
' oncontextmenu="w2ui[\''+ obj.name +'\'].contextMenu(\''+ nd.id +'\', event);"'+
' onmouseout="jQuery(this).find(\'span:nth-child(1)\').css(\'color\', \'transparent\')" '+
' onmouseover="jQuery(this).find(\'span:nth-child(1)\').css(\'color\', \'inherit\')">'+
((nd.groupShowHide && nd.collapsible) ? '<span>'+ (!nd.hidden && nd.expanded ? w2utils.lang('Hide') : w2utils.lang('Show')) +'</span>' : '<span></span>') +
(typeof nd.text == 'function' ? nd.text.call(obj, nd) : '<span class="w2ui-group-text">'+ nd.text +'</span>') +
'</div>'+
'<div class="w2ui-node-sub" id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
if (obj.flat) {
html = '<div class="w2ui-node-group" id="node_'+ nd.id +'"><span>&#160;</span></div>'+
'<div id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
}
} else {
if (nd.selected && !nd.disabled) obj.selected = nd.id;
tmp = '';
if (img) tmp = '<div class="w2ui-node-image w2ui-icon '+ img + (nd.selected && !nd.disabled ? " w2ui-icon-selected" : "") +'"></div>';
if (icon) {
tmp = '<div class="w2ui-node-image"><span class="' + (typeof icon == 'function' ? icon.call(obj, nd) : icon) + '"></span></div>';
}
var text = nd.text;
var expand = '';
var counts = (nd.count != null ? '<div class="w2ui-node-count">'+ nd.count +'</div>' : '');
if (nd.collapsible === true) {
expand = '<div class="w2ui-' + (nd.expanded ? 'expanded' : 'collapsed') + '"><span></span></div>';
}
if (typeof nd.text == 'function') text = nd.text.call(obj, nd);
html = '<div class="w2ui-node w2ui-level-'+ level + (nd.selected ? ' w2ui-selected' : '') + (nd.disabled ? ' w2ui-disabled' : '') + (nd.class ? ' ' + nd.class : '') +'"'+
' id="node_'+ nd.id +'" data-level="'+ level +'" style="position: relative; '+ (nd.hidden ? 'display: none;' : '') +'"'+
' ondblclick="w2ui[\''+ obj.name +'\'].dblClick(\''+ nd.id +'\', event);"'+
' oncontextmenu="w2ui[\''+ obj.name +'\'].contextMenu(\''+ nd.id +'\', event);"'+
' onClick="w2ui[\''+ obj.name +'\'].click(\''+ nd.id +'\', event); ">'+
(obj.handle.content
? '<div class="w2ui-node-handle" style="width: '+ obj.handle.size +'px; '+ obj.handle.style + '">'+
obj.handle.content +
'</div>'
: ''
) +
' <div class="w2ui-node-data" style="margin-left:'+ (level * obj.levelPadding + obj.handle.size) +'px">'+
expand + tmp + counts +
'<div class="w2ui-node-text w2ui-node-caption" style="'+ (nd.style || '') +'">'+ text +'</div>'+
' </div>'+
'</div>'+
'<div class="w2ui-node-sub" id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
if (obj.flat) {
html = '<div class="w2ui-node w2ui-level-'+ level +' '+ (nd.selected ? 'w2ui-selected' : '') +' '+ (nd.disabled ? 'w2ui-disabled' : '') + (nd.class ? ' ' + nd.class : '') +'" id="node_'+ nd.id +'" style="'+ (nd.hidden ? 'display: none;' : '') +'"'+
' onmouseover="jQuery(this).find(\'.w2ui-node-data\').w2tag(w2utils.base64decode(\''+
w2utils.base64encode(text + (nd.count || nd.count === 0 ? ' - <span class="w2ui-node-count">'+ nd.count +'</span>' : '')) + '\'), '+
' { id: \'' + nd.id + '\', left: -5 })"'+
' onmouseout="jQuery(this).find(\'.w2ui-node-data\').w2tag(null, { id: \'' + nd.id + '\' })"'+
' ondblclick="w2ui[\''+ obj.name +'\'].dblClick(\''+ nd.id +'\', event);"'+
' oncontextmenu="w2ui[\''+ obj.name +'\'].contextMenu(\''+ nd.id +'\', event);"'+
' onClick="w2ui[\''+ obj.name +'\'].click(\''+ nd.id +'\', event); ">'+
'<div class="w2ui-node-data w2ui-node-flat">'+ tmp +'</div>'+
'</div>'+
'<div class="w2ui-node-sub" id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
}
}
return html;
}
},
resize: function () {
var time = (new Date()).getTime();
// event before
var edata = this.trigger({ phase: 'before', type: 'resize', target: this.name });
if (edata.isCancelled === true) return;
// default action
$(this.box).css('overflow', 'hidden'); // container should have no overflow
$(this.box).find('> div').css({
width : $(this.box).width() + 'px',
height : $(this.box).height() + 'px'
});
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return (new Date()).getTime() - time;
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
if (edata.isCancelled === true) return;
// clean up
if ($(this.box).find('> div > div.w2ui-sidebar-body').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-sidebar')
.html('');
}
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
lock: function (msg, showSpinner) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(this.box);
w2utils.lock.apply(window, args);
},
unlock: function (speed) {
w2utils.unlock(this.box, speed);
}
};
$.extend(w2sidebar.prototype, w2utils.event);
w2obj.sidebar = w2sidebar;
})(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)
* - options.items - can be an array
* - options.msgSearch - message to search for user
* - options.msgNoItems - can be a function
*
************************************************************************/
(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 };
}
if (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) {
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;
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
};
this.options = $.extend(true, {}, defaults, options);
options = this.options; // since object is re-created, need to re-assign
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 : '',
align : 'both', // same width as control
altRows : true, // alternate row color
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
};
if (typeof options.items == 'function') {
options._items_fun = options.items
}
// need to be first
options.items = w2obj.field.prototype.normMenu.call(this, options.items);
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) && Array.isArray(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);
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('autocapitalize', 'off')
.attr('autocomplete', 'off')
.attr('autocorrect', 'off')
.attr('spellcheck', 'false')
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,
align : 'both', // same width as control
altRows : true, // alternate row color
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, { suffix: '' });
if (typeof options.items == 'function') {
options._items_fun = options.items
}
options.items = w2obj.field.prototype.normMenu.call(this, options.items);
options.selected = w2obj.field.prototype.normMenu.call(this, 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,
align : 'both', // same width as control
altRows : true, // alternate row color
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);
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).trigger('input').change();
} else {
var it = (this.type === 'enum' ? [val] : val);
$(this.el).data('selected', it).trigger('input').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).trigger('input').change();
} else {
var it = (this.type === 'enum' ? [items[ind]] : items[ind]);
$(this.el).data('selected', it).trigger('input').change();
}
this.refresh();
return true;
}
}
return false;
},
clear: function () {
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('input').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 + 0) + '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).trigger('input').change();
// cancel event
event.stopPropagation();
event.preventDefault();
return false;
}
}
// color
if (this.type === 'color') {
var color = $(this.el).val();
if (color.substr(0, 3).toLowerCase() !== 'rgb') {
color = '#' + color;
var len = $(this.el).val().length
if (len !== 8 && len !== 6 && len !== 3) color = '';
}
$(this.el).next().find('div').css('background-color', color);
if ($(this.el).hasClass('has-focus') && $(this.el).data('skipInit') !== true) {
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)).trigger('input').change();
if (this.type === 'date') $(obj.el).val(w2utils.formatDate(new Date(tmp), options.format)).trigger('input').change();
if (this.type === 'datetime') $(obj.el).val(w2utils.formatDateTime(new Date(tmp), options.format)).trigger('input').change();
}
}
},
click: function (event) {
event.stopPropagation();
// lists
if (['list', 'combo', 'enum'].indexOf(this.type) !== -1) {
if (!$(this.el).hasClass('has-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;
$(obj.el).addClass('has-focus')
// 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')) { // need to stay .is(':focus')
$(obj.helpers.focus).find('input').focus();
return;
}
obj.search();
setTimeout(function () { obj.updateOverlay(); }, 1);
}, 1);
// regenerat items
if (typeof obj.options._items_fun == 'function') {
obj.options.items = w2obj.field.prototype.normMenu.call(this, obj.options._items_fun);
}
}
// 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();
var $overlay = $("#w2ui-overlay");
$(obj.el).removeClass('has-focus')
// hide overlay
if (['color', 'date', 'time', 'list', 'combo', 'enum', 'datetime'].indexOf(obj.type) !== -1) {
var closeTimeout = window.setTimeout(function() {
if ($overlay.data('keepOpen') !== true) $overlay.hide();
}, 0);
$(".menu", $overlay).one('focus', function() {
clearTimeout(closeTimeout);
$(this).one('focusout', function(event) {
$overlay.hide();
});
});
}
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) !== -1) {
if (val !== '' && !obj.checkType(val)) {
$(obj.el).val('').trigger('input').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').trigger('input').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').trigger('input').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').trigger('input').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').trigger('input').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);
var cancel = false;
// numeric
if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) !== -1) {
if (!options.keyboard || $(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
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
var newValue = (val + inc <= options.max || options.max == null ? Number((val + inc).toFixed(12)) : options.max);
$(obj.el).val(newValue).trigger('input').change();
cancel = true;
break;
case 40: // down
if (event.shiftKey) break; // no action if shift key is pressed
var newValue = (val - inc >= options.min || options.min == null ? Number((val - inc).toFixed(12)) : options.min);
$(obj.el).val(newValue).trigger('input').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 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).trigger('input').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).trigger('input').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 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)).trigger('input').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 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).trigger('input').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).trigger('input').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).trigger('input').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).trigger('input').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).trigger('input').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).trigger('input').change();
if ($(obj.el).val() === '' && $(obj.el).data('selected')) $(obj.el).removeData('selected').val('').trigger('input').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('input').trigger('change');
obj.refresh();
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}
}
if (obj.type === 'list' && focus.val() === '') {
$(obj.el).data('selected', {}).trigger('input').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.items[options.index].disabled)) options.index--;
if (options.index === 0 && (options.items[options.index].hidden || options.items[options.index].disabled)) {
while (options.items[options.index] && (options.items[options.index].hidden || options.items[options.index].disabled)) 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.items[options.index].disabled)) options.index++;
if (options.index == options.items.length-1 && (options.items[options.index].hidden || options.items[options.index].disabled)) {
while (options.items[options.index] && (options.items[options.index].hidden || options.items[options.index].disabled)) 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 refresh 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 && (
(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) try { obj.tmp.xhr.abort(); } catch (e) {}
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' && data.records == null) { data.records = []; } // handles Golang marshal of empty arrays to null
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;
obj.tmp.lastError = '';
options.items = obj.normMenu(data.records);
if (search === '' && data.records.length === 0) obj.tmp.emptySet = true; else obj.tmp.emptySet = false;
// preset item
var find_selected = $(obj.el).data('find_selected');
if (find_selected) {
var sel;
if (Array.isArray(find_selected)) {
sel = [];
find_selected.forEach(function (find) {
var isFound = false
options.items.forEach(function (item) {
if (item.id == find || (find && find.id == item.id)) {
sel.push($.extend(true, {}, item));
isFound = true
}
})
if (!isFound) sel.push(find)
})
} else {
sel = find_selected;
options.items.forEach(function (item) {
if (item.id == find_selected || (find_selected && find_selected.id == item.id)) {
sel = item
}
})
}
$(obj.el).data('selected', sel).removeData('find_selected').trigger('input').change();
}
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.tmp.xhr_loading = false;
obj.tmp.xhr_search = search;
obj.tmp.xhr_total = 0;
obj.tmp.emptySet = true;
obj.tmp.lastError = (edata2.error || 'Server communication failed');
options.items = [];
obj.clearCache();
obj.search();
obj.updateOverlay(false);
// 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); }
}
var items = options.items;
if (obj.tmp.xhr_loading !== true) {
var shown = 0;
for (var i = 0; i < items.length; i++) {
var item = 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 (items[options.index] && 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 {
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).trigger('input').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"></div>', {
css: { "background-color": "#f5f5f5" },
onShow: function (event) {
// this needed for IE 11 compatibility
if (w2utils.isIE) {
$('.w2ui-calendar').on('mousedown', function (event) {
var $tg = $(event.target);
if ($tg.length === 1 && $tg[0].id === 'w2ui-jump-year') {
$('#w2ui-overlay').data('keepOpen', true);
}
});
}
}
});
}
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).trigger('input').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') : '')).trigger('input').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') : '')).trigger('input').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" },
onShow: function (event) {
// this needed for IE 11 compatibility
if (w2utils.isIE) {
$('.w2ui-calendar').on('mousedown', function (event) {
var $tg = $(event.target);
if ($tg.length === 1 && $tg[0].id === 'w2ui-jump-year') {
$('#w2ui-overlay').data('keepOpen', true);
}
});
}
}
});
}
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).trigger('input').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).trigger('input').change();
//$(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':00' + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).trigger('input').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).trigger('input').change();
//$(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':' + (min < 10 ? 0 : '') + min + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).trigger('input').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).trigger('input').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 ($(this.el).hasClass('has-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 && String($(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(options.msgSearch || 'Type to search...');
}
if (options.url == null && options.items.length === 0) msgNoItems = w2utils.lang('Empty list');
if (options.msgNoItems != null) {
var eventData = {
search: $(input).val(),
options: $.extend(true, {}, options)
}
if (options.url) {
eventData.remote = {
url: options.url,
empty: obj.tmp.emptySet ? true : false,
error: obj.tmp.lastError,
minLength: options.minLength
}
}
msgNoItems = (typeof options.msgNoItems === 'function'
? options.msgNoItems(eventData)
: options.msgNoItems)
}
if (obj.tmp.lastError) {
msgNoItems = obj.tmp.lastError;
}
if (msgNoItems) {
msgNoItems = '<div style="white-space: normal; line-height: 1.3">' + msgNoItems + '</div>';
}
var params = $.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).trigger('input').change();
$(obj.helpers.multi).find('input').val('').width(20);
obj.refresh();
if (event.keepOpen !== true) {
if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
} else {
var ind;
params.items.forEach(function (item, i) { if (item.id == event.item.id) ind = i })
if (ind != null) params.items.splice(ind, 1)
params.selected = selected
$(el).w2menu('refresh', params);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}
} else {
$(obj.el).data('selected', event.item).val(event.item.text).trigger('input').change();
if (obj.helpers.focus) obj.helpers.focus.find('input').val('');
}
}
});
$(el).w2menu((!indexOnly ? 'refresh' : 'refresh-index'), params);
}
}
},
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 displayed
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>');
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 = parseInt($(obj.el).attr('tabIndex'));
if (!isNaN(tabIndex) && tabIndex !== -1) obj.el._tabIndex = tabIndex;
if (obj.el._tabIndex) tabIndex = obj.el._tabIndex;
if (tabIndex == null) tabIndex = -1;
if (isNaN(tabIndex)) tabIndex = 0;
// if there is id, add to search with "_search"
var searchId = '';
if ($(obj.el).attr('id') != null) {
searchId = 'id="' + $(obj.el).attr('id') + '_search"'
}
// build helper
var html =
'<div class="w2ui-field-helper">'+
' <div class="w2ui-icon icon-search" style="opacity: 0; display: none"></div>'+
' <input '+ searchId +' type="text" tabIndex="'+ tabIndex +'" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"/>'+
'</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 there is id, add to search with "_search"
var searchId = '';
if ($(obj.el).attr('id') != null) {
searchId = 'id="' + $(obj.el).attr('id') + '_search" '
}
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 = 0; // default tabindex
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 '+ searchId +' type="text" style="width: 20px; margin: -3px 0 0; padding: 2px 0; border-color: white" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"' +
($(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 '+ searchId +' name="attachment" class="file-input" type="file" style="width: 100%; height: 100%; opacity: 0;" tabindex="-1"' + (obj.options.max !== 1 ? ' multiple="multiple"': '') + ($(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.find('input')
.off('.drag')
.on('click.drag', function (event) {
event.stopPropagation();
if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
$(obj.el).focus();
})
.on('dragenter.drag', function (event) {
if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
$(div).addClass('w2ui-file-dragover');
})
.on('dragleave.drag', function (event) {
if ($(obj.el).prop('readonly') || $(obj.el).prop('disabled')) return;
$(div).removeClass('w2ui-file-dragover');
})
.on('drop.drag', 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]);
$(obj.el).focus();
// cancel to stop browser behaviour
event.preventDefault();
event.stopPropagation();
})
.on('dragover.drag', function (event) {
// cancel to stop browser behaviour
event.preventDefault();
event.stopPropagation();
})
.on('change.drag', function () {
$(obj.el).focus();
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 = w2utils.lang('Maximum total size is') + ' ' + w2utils.formatSize(options.maxSize);
if (options.silent === false) {
$(obj.el).w2tag(err);
} else {
console.log('ERROR: '+ err);
}
return;
}
if (options.max !== 0 && cnt >= options.max) {
err = w2utils.lang('Maximum number of files is') + ' '+ options.max;
if (options.silent === false) {
$(obj.el).w2tag(err);
} else {
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('input').trigger('change');
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
};
})();
reader.readAsDataURL(file);
} else {
obj.refresh();
$(obj.el).trigger('input').trigger('change');
obj.trigger($.extend(edata, { phase: 'after' }));
}
},
normMenu: function (menu, el) {
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] != null) {
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;
} else {
menu[m] = { id: null, text: 'null' }
}
}
return menu;
} else if (typeof menu === 'function') {
return w2obj.field.prototype.normMenu.call(this, menu.call(this, el));
} 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 id="w2ui-jump-month">'+ mhtml +'</div><div id="w2ui-jump-year">'+ 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);
/************************************************************************
* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
* - Following objects defined
* - w2form - form widget
* - $().w2form - jQuery wrapper
* - Dependencies: jQuery, w2utils, w2fields, w2tabs, w2toolbar
*
* == NICE TO HAVE ==
* - include delta on save
* - form should read <select> <options> into items
* - two way data bindings
* - verify validation of fields
* - nested record object
* - formHTML --> template
*
* == 1.5 changes
* - $('#form').w2form() - if called w/o argument then it returns form object
* - added onProgress
* - added field.html.style (for the whole field)
* - added enable/disable, show/hide
* - added field.disabled, field.hidden
* - when field is blank, set record.field = null
* - action: { caption: 'Limpiar', style: '', class: '', onClick: function () {} }
* - added ability to generate radio and select html in generateHTML()
* - refresh(field) - would refresh only one field
* - form.message
* - added field.html.column
* - added field types html, empty, custom
* - httpHeaders
* - method
* - onInput
* - added field.html.groupStyle, field.html.groupTitleStyle
* - added field.html.column = 'before' && field.html.column = 'after'
* - added field.html.anchor
* - changed this.clear(field1, field2,...)
* - added nestedFields: use field name containing dots as separator to look into objects
* - added getValue(), setValue()
* - added getChanges()
* - added getCleanRecord(strict)
* - added applyFocus()
* - deprecated field.name -> field.field
* - options.items - can be an array
* - added form.pageStyle
* - added html.span -1 - then label is displayed on top
* - added field.options.minLength, min/max for numebrs can be done with int/float - min/max
* - field.html.groupCollapsable, form.toggleGroup
* - added showErrors
* - added field.type = 'check'
* - new field type 'map', 'array' - same thing but map has unique keys also html: { key: { text: '111', attr: '222' }, value: {...}}
* - updateEmptyGroups
*
************************************************************************/
(function ($) {
var w2form = function(options) {
// public properties
this.name = null;
this.header = '';
this.box = null; // HTML element that hold this element
this.url = '';
this.routeData = {}; // data for dynamic routes
this.formURL = ''; // url where to get form HTML
this.formHTML = ''; // form HTML (might be loaded from the url)
this.page = 0; // current page
this.recid = 0; // can be null or 0
this.fields = [];
this.actions = {};
this.record = {};
this.original = null;
this.postData = {};
this.httpHeaders = {};
this.method = null; // only used when not null, otherwise set based on w2utils.settings.dataType
this.toolbar = {}; // if not empty, then it is toolbar
this.tabs = {}; // if not empty, then it is tabs object
this.style = '';
this.focus = 0; // focus first or other element
this.autosize = true; // autosize
this.nestedFields= true; // use field name containing dots as separator to look into object
// When w2utils.settings.dataType is JSON, then we can convert the save request to multipart/form-data. So we can upload large files with the form
// The original body is JSON.stringified to __body
this.multipart = false;
this.tabindexBase= 0; // this will be added to the auto numbering
// internal
this.isGenerated = false;
this.last = {
xhr: null, // jquery xhr requests
errors: []
}
$.extend(true, this, w2obj.form, options);
}
// ====================================================
// -- Registers as a jQuery plugin
$.fn.w2form = function(method) {
if ($.isPlainObject(method)) {
var obj = this;
// check name parameter
if (!w2utils.checkName(method, 'w2form')) return;
// remember items
var record = method.record;
var original = method.original;
var fields = method.fields;
var toolbar = method.toolbar;
var tabs = method.tabs;
// extend items
var object = new w2form(method);
$.extend(object, { record: {}, original: null, fields: [], tabs: {}, toolbar: {}, handlers: [] });
if ($.isArray(tabs)) {
$.extend(true, object.tabs, { tabs: [] });
for (var t = 0; t < tabs.length; t++) {
var tmp = tabs[t];
if (typeof tmp === 'object') {
object.tabs.tabs.push(tmp);
if(tmp.active === true) {
object.tabs.active = tmp.id;
}
} else {
object.tabs.tabs.push({ id: tmp, text: tmp });
}
}
} else {
$.extend(true, object.tabs, tabs);
}
$.extend(true, object.toolbar, toolbar);
// reassign variables
if (fields) for (var p = 0; p < fields.length; p++) {
var field = $.extend(true, {}, fields[p]);
if (field.field == null && field.name != null) {
console.log('NOTICE: form field.name property is deprecated, please use field.field. Field ->', field);
field.field = field.name;
}
object.fields[p] = field;
}
for (var p in record) { // it is an object
if ($.isPlainObject(record[p])) {
object.record[p] = $.extend(true, {}, record[p]);
} else {
object.record[p] = record[p];
}
}
for (var p in original) { // it is an object
if ($.isPlainObject(original[p])) {
object.original[p] = $.extend(true, {}, original[p]);
} else {
object.original[p] = original[p];
}
}
if (obj.length > 0) object.box = obj[0];
// render if necessary
if (object.formURL !== '') {
$.get(object.formURL, function (data) { // should always be $.get as it is template
object.formHTML = data;
object.isGenerated = true;
if ($(object.box).length !== 0 || data.length !== 0) {
$(object.box).html(data);
object.render(object.box);
}
});
} else if (object.formHTML !== '') {
// it is already loaded into formHTML
} else if ($(this).length !== 0 && $.trim($(this).html()) !== '') {
object.formHTML = $(this).html();
} else { // try to generate it
object.formHTML = object.generateHTML();
}
// register new object
w2ui[object.name] = object;
// render if not loaded from url
if (object.formURL === '') {
if (String(object.formHTML).indexOf('w2ui-page') === -1) {
object.formHTML = '<div class="w2ui-page page-0" style="'+ (object.pageStyle || '') +'">'+ object.formHTML +'</div>';
}
$(object.box).html(object.formHTML);
object.isGenerated = true;
object.render(object.box);
}
return object;
} else {
var obj = w2ui[$(this).attr('name')];
if (!obj) return null;
if (arguments.length > 0) {
if (obj[method]) obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
return this;
} else {
return obj;
}
}
};
// ====================================================
// -- Implementation of core functionality
w2form.prototype = {
// events
onRequest : null,
onLoad : null,
onValidate : null,
onSubmit : null,
onProgress : null,
onSave : null,
onChange : null,
onInput : null,
onRender : null,
onRefresh : null,
onResize : null,
onDestroy : null,
onAction : null,
onToolbar : null,
onError : null,
msgNotJSON : 'Returned data is not in valid JSON format.',
msgAJAXerror : 'AJAX error. See console for more details.',
msgRefresh : 'Loading...',
msgSaving : 'Saving...',
get: function (field, returnIndex) {
if (arguments.length === 0) {
var all = [];
for (var f1 = 0; f1 < this.fields.length; f1++) {
if (this.fields[f1].field != null) all.push(this.fields[f1].field);
}
return all;
} else {
for (var f2 = 0; f2 < this.fields.length; f2++) {
if (this.fields[f2].field == field) {
if (returnIndex === true) return f2; else return this.fields[f2];
}
}
return null;
}
},
set: function (field, obj) {
for (var f = 0; f < this.fields.length; f++) {
if (this.fields[f].field == field) {
$.extend(this.fields[f] , obj);
this.refresh(field);
return true;
}
}
return false;
},
getValue: function (field) {
if (this.nestedFields) {
var val = undefined;
try { // need this to make sure no error in fields
var rec = this.record;
val = String(field).split('.').reduce(function(rec, i) { return rec[i] }, rec)
} catch (event) {
}
return val;
} else {
return this.record[field];
}
},
setValue: function (field, value) {
if (this.nestedFields) {
try { // need this to make sure no error in fields
var rec = this.record;
String(field).split('.').map(function (fld, i, arr) {
if (arr.length - 1 !== i) {
if (rec[fld]) rec = rec[fld]; else { rec[fld] = {}; rec = rec[fld] }
} else {
rec[fld] = value;
}
});
return true;
} catch (event) {
return false;
}
} else {
this.record[field] = value;
return true;
}
},
show: function () {
var affected = [];
for (var a = 0; a < arguments.length; a++) {
var fld = this.get(arguments[a]);
if (fld && fld.hidden) {
fld.hidden = false;
affected.push(fld.field);
}
}
if (affected.length > 0) this.refresh.apply(this, affected);
this.updateEmptyGroups();
return affected.length;
},
hide: function () {
var affected = [];
for (var a = 0; a < arguments.length; a++) {
var fld = this.get(arguments[a]);
if (fld && !fld.hidden) {
fld.hidden = true;
affected.push(fld.field);
}
}
if (affected.length > 0) this.refresh.apply(this, affected);
this.updateEmptyGroups();
return affected.length;
},
updateEmptyGroups: function() {
// hide empty groups
$(this.box).find('.w2ui-group').each(function(ind, group){
if (isHidden($(group).find('.w2ui-field'))) {
$(group).hide()
} else {
$(group).show()
}
})
function isHidden($els) {
var flag = true
$els.each(function(ind, el) {
if (el.style.display != 'none') flag = false
})
return flag
}
},
enable: function () {
var affected = [];
for (var a = 0; a < arguments.length; a++) {
var fld = this.get(arguments[a]);
if (fld && fld.disabled) {
fld.disabled = false;
affected.push(fld.field);
}
}
if (affected.length > 0) this.refresh.apply(this, affected);
return affected.length;
},
disable: function () {
var affected = [];
for (var a = 0; a < arguments.length; a++) {
var fld = this.get(arguments[a]);
if (fld && !fld.disabled) {
fld.disabled = true;
affected.push(fld.field);
}
}
if (affected.length > 0) this.refresh.apply(this, affected);
return affected.length;
},
change: function () {
Array.from(arguments).forEach(function(field) {
var tmp = this.get(field);
if (tmp.$el) tmp.$el.change();
}.bind(this));
},
reload: function (callBack) {
var url = (typeof this.url !== 'object' ? this.url : this.url.get);
if (url && this.recid !== 0 && this.recid != null) {
// this.clear();
this.request(callBack);
} else {
// this.refresh(); // no need to refresh
if (typeof callBack === 'function') callBack();
}
},
clear: function () {
if (arguments.length != 0) {
Array.from(arguments).forEach(function(field) {
var rec = this.record
String(field).split('.').map(function (fld, i, arr) {
if (arr.length - 1 !== i) rec = rec[fld]; else delete rec[fld]
})
this.refresh(field);
}.bind(this));
} else {
this.recid = 0;
this.record = {};
this.original = null;
this.refresh();
}
$().w2tag();
},
error: function (msg) {
var obj = this;
// let the management of the error outside of the grid
var edata = this.trigger({ target: this.name, type: 'error', message: msg , xhr: this.last.xhr });
if (edata.isCancelled === true) {
if (typeof callBack === 'function') callBack();
return;
}
// need a time out because message might be already up)
setTimeout(function () { obj.message(msg); }, 1);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
message: function(options) {
if (typeof options === 'string') {
options = {
width : (options.length < 300 ? 350 : 550),
height : (options.length < 300 ? 170: 250),
body : '<div class="w2ui-centered">' + options + '</div>',
buttons : '<button class="w2ui-btn" onclick="w2ui[\''+ this.name +'\'].message()">Ok</button>',
onOpen : function (event) {
setTimeout(function () {
$(event.box).find('.w2ui-btn').focus();
}, 25);
}
};
}
w2utils.message.call(this, {
box : this.box,
path : 'w2ui.' + this.name,
title : '.w2ui-form-header:visible',
body : '.w2ui-form-box'
}, options);
},
validate: function (showErrors) {
if (showErrors == null) showErrors = true;
$().w2tag(); // hide all tags before validating
// validate before saving
var errors = [];
for (var f = 0; f < this.fields.length; f++) {
var field = this.fields[f];
if (this.getValue(field.field) == null) this.setValue(field.field, '');
switch (field.type) {
case 'int':
if (this.getValue(field.field) && !w2utils.isInt(this.getValue(field.field))) {
errors.push({ field: field, error: w2utils.lang('Not an integer') });
}
break;
case 'float':
if (this.getValue(field.field) && !w2utils.isFloat(this.getValue(field.field))) {
errors.push({ field: field, error: w2utils.lang('Not a float') });
}
break;
case 'money':
if (this.getValue(field.field) && !w2utils.isMoney(this.getValue(field.field))) {
errors.push({ field: field, error: w2utils.lang('Not in money format') });
}
break;
case 'color':
case 'hex':
if (this.getValue(field.field) && !w2utils.isHex(this.getValue(field.field))) {
errors.push({ field: field, error: w2utils.lang('Not a hex number') });
}
break;
case 'email':
if (this.getValue(field.field) && !w2utils.isEmail(this.getValue(field.field))) {
errors.push({ field: field, error: w2utils.lang('Not a valid email') });
}
break;
case 'checkbox':
// convert true/false
if (this.getValue(field.field) == true) this.setValue(field.field, 1); else this.setValue(field.field, 0);
break;
case 'date':
// format date before submit
if (!field.options.format) field.options.format = w2utils.settings.dateFormat;
if (this.getValue(field.field) && !w2utils.isDate(this.getValue(field.field), field.options.format)) {
errors.push({ field: field, error: w2utils.lang('Not a valid date') + ': ' + field.options.format });
}
break;
case 'list':
case 'combo':
break;
case 'enum':
break;
}
// === check required - if field is '0' it should be considered not empty
var val = this.getValue(field.field);
if (field.required && field.hidden !== true && ['div', 'custom', 'html', 'empty'].indexOf(field.type) == -1
&& (val === '' || ($.isArray(val) && val.length === 0) || ($.isPlainObject(val) && $.isEmptyObject(val)))) {
errors.push({ field: field, error: w2utils.lang('Required field') });
}
if (field.options && field.hidden !== true && field.options.minLength > 0
&& ['enum', 'list', 'combo'].indexOf(field.type) == -1 // since minLength is used there too
&& this.getValue(field.field).length < field.options.minLength) {
errors.push({ field: field, error: w2utils.lang('Field should be at least '+ field.options.minLength +' characters.') });
}
}
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'validate', errors: errors });
if (edata.isCancelled === true) return;
// show error
this.last.errors = errors
if (showErrors) this.showErrors()
// event after
this.trigger($.extend(edata, { phase: 'after' }));
return errors;
},
showErrors: function () {
var errors = this.last.errors
if (errors.length > 0) {
var err = errors[0];
// scroll into view
this.goto(errors[0].field.page)
$(err.field.$el).parents('.w2ui-field')[0].scrollIntoView(true)
// show errors
for (var i = 0; i < errors.length; i++) {
err = errors[i];
var opt = $.extend({ "class": 'w2ui-error', hideOnFocus: true }, err.options);
if (err.field == null) continue;
if (err.field.type === 'radio') { // for radio and checkboxes
$($(err.field.el).closest('div')[0]).w2tag(err.error, opt);
} else if (['enum', 'file'].indexOf(err.field.type) !== -1) {
(function (err) {
setTimeout(function () {
var fld = $(err.field.el).data('w2field').helpers.multi;
$(err.field.el).w2tag(err.error, err.options);
$(fld).addClass('w2ui-error');
}, 1);
})(err);
} else {
$(err.field.el).w2tag(err.error, opt);
}
}
// hide errors on scroll
setTimeout(function () {
var err = errors[0];
$(err.field.$el).parents('.w2ui-page').off('.hideErrors').on('scroll.hideErrors', function (event) {
for (var i = 0; i < errors.length; i++) {
err = errors[i];
$(err.field.el).w2tag()
}
$(err.field.$el).parents('.w2ui-page').off('.hideErrors')
})
}, 300);
}
},
getChanges: function () {
var diff = {};
if (this.original != null && typeof this.original == 'object' && !$.isEmptyObject(this.record)) {
diff = doDiff(this.record, this.original, {});
}
return diff;
function doDiff(record, original, result) {
for (var i in record) {
if (record[i] != null && typeof record[i] === "object") {
result[i] = doDiff(record[i], original[i] || {}, {});
if (!result[i] || ($.isEmptyObject(result[i]) && $.isEmptyObject(original[i]))) delete result[i];
} else if (record[i] != original[i] || (record[i] == null && original[i] != null)) { // also catch field clear
result[i] = record[i];
}
}
return !$.isEmptyObject(result) ? result : null;
}
},
getCleanRecord: function (strict) {
var data = $.extend(true, {}, this.record);
this.fields.forEach(function (fld) {
if (['list', 'combo', 'enum'].indexOf(fld.type) != -1) {
var tmp = { nestedFields: true, record: data };
var val = this.getValue.call(tmp, fld.field);
if ($.isPlainObject(val) && val.id != null) { // should be tru if val.id === ''
this.setValue.call(tmp, fld.field, val.id)
}
if (Array.isArray(val)) {
val.forEach(function (item, ind) {
if ($.isPlainObject(item) && item.id) {
val[ind] = item.id;
}
})
}
}
if (fld.type == 'map') {
var tmp = { nestedFields: true, record: data };
var val = this.getValue.call(tmp, fld.field);
if (val._order) delete val._order
}
}.bind(this))
// return only records presend in description
if (strict === true) {
Object.keys(data).forEach(function (key) {
if (!this.get(key)) delete data[key]
}.bind(this));
}
return data;
},
request: function (postData, callBack) { // if (1) param then it is call back if (2) then postData and callBack
var obj = this;
// check for multiple params
if (typeof postData === 'function') {
callBack = postData;
postData = null;
}
if (postData == null) postData = {};
if (!this.url || (typeof this.url === 'object' && !this.url.get)) return;
if (this.recid == null) this.recid = 0;
// build parameters list
var params = {};
// add list params
params['cmd'] = 'get';
params['recid'] = this.recid;
params['name'] = this.name;
// append other params
$.extend(params, this.postData);
$.extend(params, postData);
// event before
var edata = this.trigger({ phase: 'before', type: 'request', target: this.name, url: this.url, postData: params, httpHeaders: this.httpHeaders });
if (edata.isCancelled === true) { if (typeof callBack === 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
// default action
this.record = {};
this.original = null;
// call server to get data
this.lock(w2utils.lang(this.msgRefresh));
var url = edata.url;
if (typeof edata.url === 'object' && edata.url.get) url = edata.url.get;
if (this.last.xhr) try { this.last.xhr.abort(); } catch (e) {}
// process url with routeData
if (!$.isEmptyObject(obj.routeData)) {
var info = w2utils.parseRoute(url);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (obj.routeData[info.keys[k].name] == null) continue;
url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
}
}
}
var ajaxOptions = {
type : 'POST',
url : url,
data : edata.postData,
headers : edata.httpHeaders,
dataType : 'json' // expected from server
}
var dataType = obj.dataType || w2utils.settings.dataType;
if (edata.dataType) dataType = edata.dataType;
switch (dataType) {
case 'HTTP':
ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
break
case 'HTTPJSON':
ajaxOptions.data = { request: JSON.stringify(ajaxOptions.data) };
break;
case 'RESTFULL':
ajaxOptions.type = 'GET';
ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
break;
case 'RESTFULLJSON':
ajaxOptions.type = 'GET';
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
ajaxOptions.contentType = 'application/json';
break;
case 'JSON':
ajaxOptions.type = 'POST';
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
ajaxOptions.contentType = 'application/json';
break;
}
if (this.method) ajaxOptions.type = this.method;
if (edata.method) ajaxOptions.type = edata.method;
this.last.xhr = $.ajax(ajaxOptions)
.done(function (data, status, xhr) {
obj.unlock();
// prepare record
var data = xhr.responseJSON;
if (data == null) {
data = {
status : 'error',
message : w2utils.lang(obj.msgNotJSON),
responseText : xhr.responseText
}
}
// event before
var edata = obj.trigger({ phase: 'before', target: obj.name, type: 'load', data: data, xhr: xhr });
if (edata.isCancelled === true) {
if (typeof callBack === 'function') callBack({ status: 'error', message: 'Request aborted.' });
return;
}
// parse server response
if (edata.data.status === 'error') {
obj.error(w2utils.lang(edata.data['message']));
} else {
obj.record = $.extend({}, edata.data.record);
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
obj.refresh();
obj.applyFocus();
// call back
if (typeof callBack === 'function') callBack(edata.data);
})
.fail(function (xhr, status, error) {
// trigger event
var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
var edata2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
if (edata2.isCancelled === true) return;
// default behavior
if (status !== 'abort') {
var data;
try { data = typeof xhr.responseJSON === 'object' ? xhr.responseJSON : $.parseJSON(xhr.responseText); } catch (e) {}
console.log('ERROR: Server communication failed.',
'\n EXPECTED:', { status: 'success', items: [{ id: 1, text: 'item' }] },
'\n OR:', { status: 'error', message: 'error message' },
'\n RECEIVED:', typeof data === 'object' ? data : xhr.responseText);
obj.unlock();
}
// event after
obj.trigger($.extend(edata2, { phase: 'after' }));
});
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
submit: function (postData, callBack) {
return this.save(postData, callBack);
},
save: function (postData, callBack) {
var obj = this;
$(this.box).find(':focus').change(); // trigger onchange
// check for multiple params
if (typeof postData === 'function') {
callBack = postData;
postData = null;
}
// validation
var errors = obj.validate(true);
if (errors.length !== 0) return;
// submit save
if (postData == null) postData = {};
if (!obj.url || (typeof obj.url === 'object' && !obj.url.save)) {
console.log('ERROR: Form cannot be saved because no url is defined.');
return;
}
obj.lock(w2utils.lang(obj.msgSaving) + ' <span id="'+ obj.name +'_progress"></span>');
// need timer to allow to lock
setTimeout(function () {
// build parameters list
var params = {};
// add list params
params['cmd'] = 'save';
params['recid'] = obj.recid;
params['name'] = obj.name;
// append other params
$.extend(params, obj.postData);
$.extend(params, postData);
// clear up files
if (!obj.multipart)
obj.fields.forEach(function (item) {
if (item.type === 'file' && Array.isArray(obj.getValue(item.field))) {
obj.getValue(item.field).forEach(function (fitem) {
delete fitem.file;
});
}
});
params.record = $.extend(true, {}, obj.record);
// event before
var edata = obj.trigger({ phase: 'before', type: 'submit', target: obj.name, url: obj.url, postData: params, httpHeaders: obj.httpHeaders });
if (edata.isCancelled === true) return;
// default action
var url = edata.url;
if (typeof edata.url === 'object' && edata.url.save) url = edata.url.save;
if (obj.last.xhr) try { obj.last.xhr.abort(); } catch (e) {}
// process url with routeData
if (!$.isEmptyObject(obj.routeData)) {
var info = w2utils.parseRoute(url);
if (info.keys.length > 0) {
for (var k = 0; k < info.keys.length; k++) {
if (obj.routeData[info.keys[k].name] == null) continue;
url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
}
}
}
var ajaxOptions = {
type : 'POST',
url : url,
data : edata.postData,
headers : edata.httpHeaders,
dataType : 'json', // expected from server
xhr : function() {
var xhr = new window.XMLHttpRequest();
// upload
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var edata3 = obj.trigger({ phase: 'before', type: 'progress', total: evt.total, loaded: evt.loaded, originalEvent: evt });
if (edata3.isCancelled === true) return;
// only show % if it takes time
var percent = Math.round(evt.loaded / evt.total * 100);
if ((percent && percent != 100) || $('#'+ obj.name + '_progress').text() != '') {
$('#'+ obj.name + '_progress').text(''+ percent + '%');
}
// event after
obj.trigger($.extend(edata3, { phase: 'after' }));
}
}, false);
return xhr;
}
};
var dataType = obj.dataType || w2utils.settings.dataType;
if (edata.dataType) dataType = edata.dataType;
switch (dataType) {
case 'HTTP':
ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
break;
case 'HTTPJSON':
ajaxOptions.data = { request: JSON.stringify(ajaxOptions.data) };
break;
case 'RESTFULL':
if (obj.recid !== 0 && obj.recid != null) ajaxOptions.type = 'PUT';
ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
break;
case 'RESTFULLJSON':
if (obj.recid !== 0 && obj.recid != null) ajaxOptions.type = 'PUT';
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
ajaxOptions.contentType = 'application/json';
break;
case 'JSON':
ajaxOptions.type = 'POST';
ajaxOptions.contentType = 'application/json';
if (!obj.multipart) {
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
} else {
function apend(fd, dob, fob, p){
if (p == null) p = '';
function isObj(dob, fob, p){
if (typeof dob === 'object' && dob instanceof File) fd.append(p, dob);
if (typeof dob === 'object'){
if (!!dob && dob.constructor === Array) {
for (var i = 0; i < dob.length; i++) {
var aux_fob = !!fob ? fob[i] : fob;
isObj(dob[i], aux_fob, p+'['+i+']');
}
} else {
apend(fd, dob, fob, p);
}
}
}
for(var prop in dob){
var aux_p = p == '' ? prop : '${p}[${prop}]';
var aux_fob = !!fob ? fob[prop] : fob;
isObj(dob[prop], aux_fob, aux_p);
}
}
var fdata = new FormData();
fdata.append('__body', JSON.stringify(ajaxOptions.data));
apend(fdata,ajaxOptions.data)
ajaxOptions.data = fdata;
ajaxOptions.contentType = false;
ajaxOptions.processData = false;
}
break;
}
if (this.method) ajaxOptions.type = this.method;
if (edata.method) ajaxOptions.type = edata.method;
obj.last.xhr = $.ajax(ajaxOptions)
.done(function (data, status, xhr) {
obj.unlock();
// event before
var edata = obj.trigger({ phase: 'before', target: obj.name, type: 'save', xhr: xhr, status: status, data: data });
if (edata.isCancelled === true) return;
// parse server response
var data = xhr.responseJSON;
// default action
if (data == null) {
data = {
status : 'error',
message : w2utils.lang(obj.msgNotJSON),
responseText : xhr.responseText
};
}
if (data.status === 'error') {
obj.error(w2utils.lang(data.message));
} else {
obj.original = null;
}
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
obj.refresh();
// call back
if (typeof callBack === 'function') callBack(data, xhr);
})
.fail(function (xhr, status, error) {
// trigger event
var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
var edata2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
if (edata2.isCancelled === true) return;
// default behavior
console.log('ERROR: server communication failed. The server should return',
{ status: 'success' }, 'OR', { status: 'error', message: 'error message' },
', instead the AJAX request produced this: ', errorObj);
obj.unlock();
// event after
obj.trigger($.extend(edata2, { phase: 'after' }));
});
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
}, 50);
},
lock: function (msg, showSpinner) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(this.box);
setTimeout(function () { w2utils.lock.apply(window, args); }, 10);
},
unlock: function (speed) {
var box = this.box;
setTimeout(function () { w2utils.unlock(box, speed); }, 25); // needed timer so if server fast, it will not flash
},
lockPage: function(page, msg){
var $page = $(this.box).find(".page-" + page);
if($page.length){
// page found
w2utils.lock($page, msg);
return true;
}
// page with this id not found!
return false;
},
unlockPage: function(page, speed){
var $page = $(this.box).find(".page-" + page);
if($page.length){
// page found
w2utils.unlock($page, speed);
return true;
}
// page with this id not found!
return false;
},
goto: function (page) {
if (this.page === page) return; // already on this page
if (page != null) this.page = page;
// if it was auto size, resize it
if ($(this.box).data('auto-size') === true) $(this.box).height(0);
this.refresh();
},
generateHTML: function () {
var pages = []; // array for each page
var group = '';
var page;
var column;
var html;
var tabindex;
var tabindex_str;
for (var f = 0; f < this.fields.length; f++) {
html = '';
tabindex = this.tabindexBase + f + 1;
tabindex_str = ' tabindex="'+ tabindex +'"';
var field = this.fields[f];
if (field.html == null) field.html = {};
if (field.options == null) field.options = {};
if (field.html.caption != null && field.html.label == null) {
console.log('NOTICE: form field.html.caption property is deprecated, please use field.html.label. Field ->', field)
field.html.label = field.html.caption;
}
if (field.html.label == null) field.html.label = field.field;
field.html = $.extend(true, { label: '', span: 6, attr: '', text: '', style: '', page: 0, column: 0 }, field.html);
if (page == null) page = field.html.page;
if (column == null) column = field.html.column;
// input control
var input = '<input id="'+ field.field +'" name="'+ field.field +'" class="w2ui-input" type="text" '+ field.html.attr + tabindex_str + '>';
switch (field.type) {
case 'pass':
case 'password':
input = '<input id="' + field.field + '" name="' + field.field + '" class="w2ui-input" type = "password" ' + field.html.attr + tabindex_str + '>';
break;
case 'check':
case 'checks':
if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
var items = field.options.items;
input = '';
// normalized options
if (!$.isArray(items)) items = [];
if (items.length > 0) {
items = w2obj.field.prototype.normMenu.call(this, items, field);
}
// generate
for (var i = 0; i < items.length; i++) {
input += '<label class="w2ui-box-label">'+
' <input id="' + field.field + i +'" name="' + field.field + '" class="w2ui-input" type="checkbox" ' +
field.html.attr + (i === 0 ? tabindex_str : '') + ' data-value="'+ items[i].id +'" data-index="'+ i +'">' +
'<span>&#160;' + items[i].text + '</span>' +
'</label><br>';
}
break;
case 'checkbox':
input = '<label class="w2ui-box-label">'+
' <input id="'+ field.field +'" name="'+ field.field +'" class="w2ui-input" type="checkbox" '+ field.html.attr + tabindex_str + '>'+
' <span>'+ field.html.label +'</span>'+
'</label>';
break;
case 'radio':
input = '';
// normalized options
if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
var items = field.options.items;
if (!$.isArray(items)) items = [];
if (items.length > 0) {
items = w2obj.field.prototype.normMenu.call(this, items, field);
}
// generate
for (var i = 0; i < items.length; i++) {
input += '<label class="w2ui-box-label">'+
' <input id="' + field.field + i + '" name="' + field.field + '" class="w2ui-input" type = "radio" ' +
field.html.attr + (i === 0 ? tabindex_str : '') + ' value="'+ items[i].id + '">' +
'<span>&#160;' + items[i].text + '</span>' +
'</label><br>';
}
break;
case 'select':
input = '<select id="' + field.field + '" name="' + field.field + '" class="w2ui-input" ' + field.html.attr + tabindex_str + '>';
// normalized options
if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
var items = field.options.items;
if (!$.isArray(items)) items = [];
if (items.length > 0) {
items = w2obj.field.prototype.normMenu.call(this, items, field);
}
// generate
for (var i = 0; i < items.length; i++) {
input += '<option value="'+ items[i].id + '">' + items[i].text + '</option>';
}
input += '</select>';
break;
case 'textarea':
input = '<textarea id="'+ field.field +'" name="'+ field.field +'" class="w2ui-input" '+ field.html.attr + tabindex_str + '></textarea>';
break;
case 'toggle':
input = '<input id="'+ field.field +'" name="'+ field.field +'" type="checkbox" '+ field.html.attr + tabindex_str + ' class="w2ui-input w2ui-toggle"><div><div></div></div>';
break;
case 'map':
case 'array':
field.html.key = field.html.key || {};
field.html.value = field.html.value || {};
field.html.tabindex_str = tabindex_str
input = '<span style="float: right">' + (field.html.text || '') + '</span>' +
'<input id="'+ field.field +'" name="'+ field.field +'" type="hidden" '+ field.html.attr + tabindex_str + '>'+
'<div class="w2ui-map-container"></div>';
break;
case 'html':
case 'div':
case 'custom':
input = '<div id="'+ field.field +'" name="'+ field.field +'" '+ field.html.attr + tabindex_str + '>'+
(field && field.html && field.html.html ? field.html.html : '') +
'</div>';
break;
case 'empty':
input = (field && field.html ? (field.html.html || '') + (field.html.text || '') : '');
break;
}
if (group !== '') {
if(page != field.html.page || column != field.html.column || (field.html.group && (group != field.html.group))){
pages[page][column] += '\n </div>\n </div>';
group = '';
}
}
if (field.html.group && (group != field.html.group)) {
var collapsable = '';
if (field.html.groupCollapsable) {
collapsable = '<span class="w2ui-icon-collapse" style="width: 15px; display: inline-block; position: relative; top: -2px;"></span>'
}
html += '\n <div class="w2ui-group">'
+ '\n <div class="w2ui-group-title" style="'+ (field.html.groupTitleStyle || '')
+ (collapsable != '' ? 'cursor: pointer; user-select: none' : '') + '"'
+ (collapsable != '' ? 'data-group="' + w2utils.base64encode(field.html.group) + '"' : '')
+ (collapsable != ''
? 'onclick="w2ui[\'' + this.name + '\'].toggleGroup(\'' + field.html.group + '\')"'
: '')
+ '>'
+ collapsable + field.html.group + '</div>\n'
+ ' <div class="w2ui-group-fields" style="'+ (field.html.groupStyle || '') +'">';
group = field.html.group;
}
if (field.html.anchor == null) {
var span = (field.html.span != null ? 'w2ui-span'+ field.html.span : '')
if (field.html.span == -1) span = 'w2ui-span-none';
var label = '<label'+ (span == 'none' ? ' style="display: none"' : '') +'>' + w2utils.lang(field.type != 'checkbox' ? field.html.label : field.html.text) +'</label>'
if (!field.html.label) label = ''
html += '\n <div class="w2ui-field '+ span +'" style="'+ (field.hidden ? 'display: none;' : '') + field.html.style +'">'+
'\n '+ label +
((field.type === 'empty') ? input : '\n <div>'+ input + (field.type != 'array' && field.type != 'map' ? w2utils.lang(field.type != 'checkbox' ? field.html.text : '') : '') + '</div>') +
'\n </div>';
} else {
pages[field.html.page].anchors = pages[field.html.page].anchors || {};
pages[field.html.page].anchors[field.html.anchor] = '<div class="w2ui-field w2ui-field-inline" style="'+ (field.hidden ? 'display: none;' : '') + field.html.style +'">'+
((field.type === 'empty') ? input : '<div>'+ w2utils.lang(field.type != 'checkbox' ? field.html.label : field.html.text) + input + w2utils.lang(field.type != 'checkbox' ? field.html.text : '') + '</div>') +
'</div>';
}
if (pages[field.html.page] == null) pages[field.html.page] = {};
if (pages[field.html.page][field.html.column] == null) pages[field.html.page][field.html.column] = '';
pages[field.html.page][field.html.column] += html;
page = field.html.page;
column = field.html.column;
}
if (group !== '') pages[page][column] += '\n </div>\n </div>';
if (this.tabs.tabs) {
for (var i = 0; i < this.tabs.tabs.length; i++) if (pages[i] == null) pages[i] = [];
}
// buttons if any
var buttons = '';
if (!$.isEmptyObject(this.actions)) {
var addClass = '';
buttons += '\n<div class="w2ui-buttons">';
tabindex = this.tabindexBase + this.fields.length + 1;
for (var a in this.actions) { // it is an object
var act = this.actions[a];
var info = { text: '', style: '', "class": '' };
if ($.isPlainObject(act)) {
if (act.text == null && act.caption != null) {
console.log('NOTICE: form action.caption property is deprecated, please use action.text. Action ->', act);
act.text = act.caption;
}
if (act.text) info.text = act.text;
if (act.style) info.style = act.style;
if (act["class"]) info['class'] = act['class'];
} else {
info.text = a;
if (['save', 'update', 'create'].indexOf(a.toLowerCase()) !== -1) info['class'] = 'w2ui-btn-blue'; else info['class'] = '';
}
buttons += '\n <button name="'+ a +'" class="w2ui-btn '+ info['class'] +'" style="'+ info.style +'" tabindex="'+ tabindex +'">'+
w2utils.lang(info.text) +'</button>';
tabindex++;
}
buttons += '\n</div>';
}
html = '';
for (var p = 0; p < pages.length; p++){
html += '<div class="w2ui-page page-'+ p +'" style="' + (p !== 0 ? 'display: none;' : '') + this.pageStyle + '">';
if (pages[p].before) {
html += pages[p].before;
}
html += '<div class="w2ui-column-container">';
Object.keys(pages[p]).sort().forEach(function (c, ind) {
if (c == parseInt(c)) {
html += '<div class="w2ui-column col-'+ c +'">' + (pages[p][c] || '') + '\n</div>';
}
})
html += '\n</div>';
if (pages[p].after) {
html += pages[p].after;
}
html += '\n</div>';
// process page anchors
if (pages[p].anchors) {
Object.keys(pages[p].anchors).forEach(function (key, ind) {
html = html.replace(key, pages[p].anchors[key]);
});
}
}
html += buttons;
return html;
},
toggleGroup: function (groupName, show) {
var el = $(this.box).find('.w2ui-group-title[data-group="' + w2utils.base64encode(groupName) + '"]')
if (el.next().css('display') == 'none' && show !== true) {
el.next().slideDown(300);
el.next().next().remove()
el.find('span').addClass('w2ui-icon-collapse').removeClass('w2ui-icon-expand');
} else {
el.next().slideUp(300);
var css = 'width: ' + el.next().css('width') + ';'
+ 'padding-left: ' + el.next().css('padding-left') + ';'
+ 'padding-right: ' + el.next().css('padding-right') + ';'
+ 'margin-left: ' + el.next().css('margin-left') + ';'
+ 'margin-right: ' + el.next().css('margin-right') + ';'
setTimeout(function () { el.next().after('<div style="height: 5px;'+ css +'"></div>') }, 100)
el.find('span').addClass('w2ui-icon-expand').removeClass('w2ui-icon-collapse');
}
},
action: function (action, event) {
var act = this.actions[action];
var click = act;
if ($.isPlainObject(act) && act.onClick) click = act.onClick;
// event before
var edata = this.trigger({ phase: 'before', target: action, type: 'action', action: act, originalEvent: event });
if (edata.isCancelled === true) return;
// default actions
if (typeof click === 'function') click.call(this, event);
// event after
this.trigger($.extend(edata, { phase: 'after' }));
},
resize: function () {
var obj = this;
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'resize' });
if (edata.isCancelled === true) return;
// default behaviour
var main = $(this.box).find('> div.w2ui-form-box');
var header = $(this.box).find('> div .w2ui-form-header');
var toolbar = $(this.box).find('> div .w2ui-form-toolbar');
var tabs = $(this.box).find('> div .w2ui-form-tabs');
var page = $(this.box).find('> div .w2ui-page');
var cpage = $(this.box).find('> div .w2ui-page.page-'+ this.page);
var dpage = $(this.box).find('> div .w2ui-page.page-'+ this.page + ' > div');
var buttons = $(this.box).find('> div .w2ui-buttons');
// if no height, calculate it
resizeElements();
if (this.autosize) { //we don't need auto-size every time
if (parseInt($(this.box).height()) === 0 || $(this.box).data('auto-size') === true) {
$(this.box).height(
(header.length > 0 ? w2utils.getSize(header, 'height') : 0) +
((typeof this.tabs === 'object' && $.isArray(this.tabs.tabs) && this.tabs.tabs.length > 0) ? w2utils.getSize(tabs, 'height') : 0) +
((typeof this.toolbar === 'object' && $.isArray(this.toolbar.items) && this.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') : 0) +
(page.length > 0 ? w2utils.getSize(dpage, 'height') + w2utils.getSize(cpage, '+height') + 12 : 0) + // why 12 ???
(buttons.length > 0 ? w2utils.getSize(buttons, 'height') : 0)
);
$(this.box).data('auto-size', true);
}
resizeElements();
}
if (this.toolbar && this.toolbar.resize) this.toolbar.resize();
if (this.tabs && this.tabs.resize) this.tabs.resize();
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
function resizeElements() {
// resize elements
main.width($(obj.box).width()).height($(obj.box).height());
toolbar.css('top', (obj.header !== '' ? w2utils.getSize(header, 'height') : 0));
tabs.css('top', (obj.header !== '' ? w2utils.getSize(header, 'height') : 0)
+ ((typeof obj.toolbar === 'object' && $.isArray(obj.toolbar.items) && obj.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') : 0));
page.css('top', (obj.header !== '' ? w2utils.getSize(header, 'height') : 0)
+ ((typeof obj.toolbar === 'object' && $.isArray(obj.toolbar.items) && obj.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') + 5 : 0)
+ ((typeof obj.tabs === 'object' && $.isArray(obj.tabs.tabs) && obj.tabs.tabs.length > 0) ? w2utils.getSize(tabs, 'height') + 5 : 0));
page.css('bottom', (buttons.length > 0 ? w2utils.getSize(buttons, 'height') : 0));
}
},
refresh: function () {
var time = (new Date()).getTime();
var obj = this;
if (!this.box) return;
if (!this.isGenerated || $(this.box).html() == null) return;
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'refresh', page: this.page, field: field, fields: arguments });
if (edata.isCancelled === true) return;
var fields = Array.from(this.fields.keys());
if (arguments.length > 0) {
fields = Array.from(arguments)
.map(function (fld, ind) {
if (typeof fld != 'string') console.log('ERROR: Arguments in refresh functions should be field names')
return this.get(fld, true); // get index of field
}.bind(this))
.filter(function (fld, ind) {
if (fld != null) return true; else return false;
});
} else {
// update what page field belongs
$(this.box).find('input, textarea, select').each(function (index, el) {
var name = ($(el).attr('name') != null ? $(el).attr('name') : $(el).attr('id'));
var field = obj.get(name);
if (field) {
// find page
var div = $(el).closest('.w2ui-page');
if (div.length > 0) {
for (var i = 0; i < 100; i++) {
if (div.hasClass('page-'+i)) { field.page = i; break; }
}
}
}
});
// default action
$(this.box).find('.w2ui-page').hide();
$(this.box).find('.w2ui-page.page-' + this.page).show();
$(this.box).find('.w2ui-form-header').html(this.header);
// refresh tabs if needed
if (typeof this.tabs === 'object' && $.isArray(this.tabs.tabs) && this.tabs.tabs.length > 0) {
$('#form_'+ this.name +'_tabs').show();
this.tabs.active = this.tabs.tabs[this.page].id;
this.tabs.refresh();
} else {
$('#form_'+ this.name +'_tabs').hide();
}
// refresh tabs if needed
if (typeof this.toolbar === 'object' && $.isArray(this.toolbar.items) && this.toolbar.items.length > 0) {
$('#form_'+ this.name +'_toolbar').show();
this.toolbar.refresh();
} else {
$('#form_'+ this.name +'_toolbar').hide();
}
}
// refresh values of fields
for (var f = 0; f < fields.length; f++) {
var field = this.fields[fields[f]];
if (field.name == null && field.field != null) field.name = field.field;
if (field.field == null && field.name != null) field.field = field.name;
field.$el = $(this.box).find('[name="'+ String(field.name).replace(/\\/g, '\\\\') +'"]');
field.el = field.$el[0];
if (field.el) field.el.id = field.name;
var tmp = $(field).data('w2field');
if (tmp) tmp.clear();
$(field.$el)
.off('.w2form')
.on('change.w2form', function (event) {
var that = this;
var field = obj.get(this.name);
if (field == null) return;
if ($(this).data('skip_change') == true) {
$(this).data('skip_change', false)
return
}
var value_new = this.value;
var value_previous = obj.getValue(this.name);
if (value_previous == null) value_previous = ''
if (['list', 'enum', 'file'].indexOf(field.type) !== -1 && $(this).data('selected')) {
var nv = $(this).data('selected');
var cv = obj.getValue(this.name);
if ($.isArray(nv)) {
value_new = [];
for (var i = 0; i < nv.length; i++) value_new[i] = $.extend(true, {}, nv[i]); // clone array
}
if ($.isPlainObject(nv)) {
value_new = $.extend(true, {}, nv); // clone object
}
if ($.isArray(cv)) {
value_previous = [];
for (var i = 0; i < cv.length; i++) value_previous[i] = $.extend(true, {}, cv[i]); // clone array
}
if ($.isPlainObject(cv)) {
value_previous = $.extend(true, {}, cv); // clone object
}
}
if (['toggle', 'checkbox'].indexOf(field.type) !== -1) {
value_new = ($(this).prop('checked') ? ($(this).prop('value') === 'on' ? true : $(this).prop('value')) : false);
}
if (['check', 'checks'].indexOf(field.type) !== -1) {
if (!Array.isArray(value_previous)) value_previous = [];
value_new = value_previous.slice();
var tmp = field.options.items[$(this).attr('data-index')];
if ($(this).prop('checked')) {
value_new.push(tmp.id)
} else {
value_new.splice(value_new.indexOf(tmp.id), 1)
}
}
// clean extra chars
if (['int', 'float', 'percent', 'money', 'currency'].indexOf(field.type) !== -1) {
value_new = $(this).data('w2field').clean(value_new);
}
if (value_new === value_previous) return;
// event before
var edata2 = obj.trigger({ phase: 'before', target: this.name, type: 'change', value_new: value_new, value_previous: value_previous, originalEvent: event });
if (edata2.isCancelled === true) {
edata2.value_new = obj.getValue(this.name)
if ($(this).val() !== edata2.value_new) {
$(this).data('skip_change', true)
// if not immediate, then ignore it
setTimeout(function () { $(that).data('skip_change', false) }, 10)
}
$(this).val(edata2.value_new); // return previous value
}
// default action
var val = edata2.value_new;
if (['enum', 'file'].indexOf(field.type) !== -1) {
if (val.length > 0) {
var fld = $(field.el).data('w2field').helpers.multi;
$(fld).removeClass('w2ui-error');
}
}
if (val === '' || val == null
|| ($.isArray(val) && val.length === 0) || ($.isPlainObject(val) && $.isEmptyObject(val))) {
val = null;
}
obj.setValue(this.name, val);
// event after
obj.trigger($.extend(edata2, { phase: 'after' }));
})
.on('input.w2form', function (event) {
var val = this.value;
if (event.target.type == 'checkbox') {
val = event.target.checked;
}
// remember original
if (obj.original == null) {
if (!$.isEmptyObject(obj.record)) {
obj.original = $.extend(true, {}, obj.record);
} else {
obj.original = {};
}
}
// event before
var edata2 = obj.trigger({ phase: 'before', target: this.name, type: 'input', value_new: val, originalEvent: event });
if (edata2.isCancelled === true) return;
// event after
obj.trigger($.extend(edata2, { phase: 'after' }));
});
// required
if (field.required) {
$(field.el).parent().parent().addClass('w2ui-required');
} else {
$(field.el).parent().parent().removeClass('w2ui-required');
}
// disabled
if (field.disabled != null) {
var $fld = $(field.el);
if (field.disabled) {
if ($fld.data('w2ui-tabIndex') == null) {
$fld.data('w2ui-tabIndex', $fld.prop('tabIndex'));
}
$(field.el)
.prop('readonly', true)
.prop('tabindex', -1)
.closest('.w2ui-field')
.addClass('w2ui-disabled');
} else {
$(field.el)
.prop('readonly', false)
.prop('tabIndex', $fld.data('w2ui-tabIndex'))
.closest('.w2ui-field')
.removeClass('w2ui-disabled');
}
}
// hidden
var tmp = field.el;
if (!tmp) tmp = $(this.box).find('#' + field.field)
if (field.hidden) {
$(tmp).closest('.w2ui-field').hide();
} else {
$(tmp).closest('.w2ui-field').show();
}
}
// attach actions on buttons
$(this.box).find('button, input[type=button]').each(function (index, el) {
$(el).off('click').on('click', function (event) {
var action = this.value;
if (this.id) action = this.id;
if (this.name) action = this.name;
obj.action(action, event);
});
});
// init controls with record
for (var f = 0; f < fields.length; f++) {
var field = this.fields[fields[f]];
var value = (this.getValue(field.name) != null ? this.getValue(field.name) : '');
if (!field.el) continue;
if (!$(field.el).hasClass('w2ui-input')) $(field.el).addClass('w2ui-input');
field.type = String(field.type).toLowerCase();
if (!field.options) field.options = {};
switch (field.type) {
case 'text':
case 'textarea':
case 'email':
case 'pass':
case 'password':
field.el.value = value;
break;
case 'int':
case 'float':
case 'money':
case 'currency':
case 'percent':
// issue #761
field.el.value = value;
$(field.el).w2field($.extend({}, field.options, { type: field.type }));
break;
case 'hex':
case 'alphanumeric':
case 'color':
case 'date':
case 'time':
field.el.value = value;
$(field.el).w2field($.extend({}, field.options, { type: field.type }));
break;
case 'toggle':
if (w2utils.isFloat(value)) value = parseFloat(value);
$(field.el).prop('checked', (value ? true : false));
this.setValue(field.name, (value ? value : false));
break;
case 'radio':
$(field.$el).prop('checked', false).each(function (index, el) {
if ($(el).val() == value) $(el).prop('checked', true);
});
break;
case 'checkbox':
$(field.el).prop('checked', value ? true : false);
if (field.disabled === true || field.disabled === false) {
$(field.el).prop('disabled', field.disabled ? true : false)
}
break;
case 'check':
case 'checks':
if (Array.isArray(value)) {
value.forEach(function (val) {
$(field.el).closest('div').find('[data-value="' + val + '"]').prop('checked', true)
})
}
if (field.disabled) {
$(field.el).closest('div').find('input[type=checkbox]').prop('disabled', true)
} else {
$(field.el).closest('div').find('input[type=checkbox]').removeProp('disabled')
}
break;
// enums
case 'list':
case 'combo':
if (field.type === 'list') {
var tmp_value = ($.isPlainObject(value) ? value.id : ($.isPlainObject(field.options.selected) ? field.options.selected.id : value));
// normalized options
if (!field.options.items) field.options.items = [];
var items = field.options.items;
if (typeof items == 'function') items = items();
// find value from items
var isFound = false;
if (Array.isArray(items)) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.id == tmp_value) {
value = $.extend(true, {}, item);
obj.setValue(field.name, value);
isFound = true;
break;
}
}
}
if (!isFound && value != null && value !== '') {
field.$el.data('find_selected', value);
}
} else if (field.type === 'combo' && !$.isPlainObject(value)) {
field.el.value = value;
} else if ($.isPlainObject(value) && value.text != null) {
field.el.value = value.text;
} else {
field.el.value = '';
}
if (!$.isPlainObject(value)) value = {};
$(field.el).w2field($.extend({}, field.options, { type: field.type, selected: value }));
break;
case 'enum':
case 'file':
var sel = [];
var isFound = false;
if (!$.isArray(value)) value = [];
if (typeof field.options.items != 'function') {
if (!$.isArray(field.options.items)) {
field.options.items = [];
}
// find value from items
value.forEach(function (val) {
field.options.items.forEach(function (it) {
if (it && (it.id == val || ($.isPlainObject(val) && it.id == val.id))) {
sel.push($.isPlainObject(it) ? $.extend(true, {}, it) : it);
isFound = true
}
})
})
}
if (!isFound && value != null && value.length !== 0) {
field.$el.data('find_selected', value);
sel = value
}
var opt = $.extend({}, field.options, { type: field.type, selected: sel })
Object.keys(field.options).forEach(function(key) {
if (typeof field.options[key] == 'function') {
opt[key] = field.options[key]
}
})
$(field.el).w2field(opt);
break;
// standard HTML
case 'select':
// generate options
var items = field.options.items;
if (items != null && items.length > 0) {
items = w2obj.field.prototype.normMenu.call(this, items, field);
$(field.el).html('');
for (var it = 0; it < items.length; it++) {
$(field.el).append('<option value="'+ items[it].id +'">' + items[it].text + '</option');
}
}
$(field.el).val(value);
break;
case 'map':
case 'array':
// init map
if (field.type == 'map' && (value == null || !$.isPlainObject(value))) {
this.setValue(field.field, {})
value = this.getValue(field.field)
}
if (field.type == 'array' && (value == null || !Array.isArray(value))) {
this.setValue(field.field, [])
value = this.getValue(field.field)
}
// need closure
(function (obj, field) {
field.el.mapAdd = function (field, div, cnt) {
var attr = (field.disabled ? ' readOnly ' : '') + (field.html.tabindex_str || '');
var html = '<div class="w2ui-map-field" style="margin-bottom: 5px">'+
'<input id="'+ field.field +'_key_'+ cnt +'" data-cnt="'+ cnt +'" type="text" '+ field.html.key.attr + attr +' class="w2ui-input w2ui-map key">'+
(field.html.key.text || '') +
'<input id="'+ field.field +'_value_'+ cnt +'" data-cnt="'+ cnt +'" type="text" '+ field.html.value.attr + attr +' class="w2ui-input w2ui-map value">'+
(field.html.value.text || '') +
'</div>';
div.append(html)
}
field.el.mapRefresh = function (map, div) {
// generate options
var cnt = 1;
var names;
if (field.type == 'map') {
if (!$.isPlainObject(map)) map = {}
if (map._order == null) map._order = Object.keys(map)
names = map._order
}
if (field.type == 'array') {
if (!Array.isArray(map)) map = []
names = map.map(function (item) { return item.key })
}
var $k, $v;
names.forEach(function (item) {
$k = div.find('#' + w2utils.escapeId(field.name) + '_key_' + cnt)
$v = div.find('#' + w2utils.escapeId(field.name) + '_value_' + cnt)
if ($k.length == 0 || $v.length == 0) {
field.el.mapAdd(field, div, cnt)
$k = div.find('#' + w2utils.escapeId(field.name) + '_key_' + cnt)
$v = div.find('#' + w2utils.escapeId(field.name) + '_value_' + cnt)
}
var val = map[item];
if (field.type == 'array') {
var tmp = map.filter(function(it) { return it.key == item ? true : false})
if (tmp.length > 0) val = tmp[0].value
}
$k.val(item);
$v.val(val);
if (field.disabled === true || field.disabled === false) {
$k.prop('readOnly', field.disabled ? true : false)
$v.prop('readOnly', field.disabled ? true : false)
}
$k.parents('.w2ui-map-field').attr('data-key', item)
cnt++
})
var curr = div.find('#' + w2utils.escapeId(field.name) + '_key_' + cnt).parent()
var next = div.find('#' + w2utils.escapeId(field.name) + '_key_' + (cnt + 1)).parent()
// if not disabled - show next
if (curr.length === 0 && !($k && ($k.prop('readOnly') === true || $k.prop('disabled') === true))) {
field.el.mapAdd(field, div, cnt);
}
if (curr.length == 1 && next.length == 1) {
curr.removeAttr('data-key')
curr.find('.key').val(next.find('.key').val());
curr.find('.value').val(next.find('.value').val());
next.remove()
}
if (field.disabled === true || field.disabled === false) {
curr.find('.key').prop('readOnly', field.disabled ? true : false)
curr.find('.value').prop('readOnly', field.disabled ? true : false)
}
// attach events
$(field.el).next().find('input.w2ui-map')
.off('.mapChange')
.on('keyup.mapChange', function (event) {
var $div = $(event.target).parents('.w2ui-map-field')
if (event.keyCode == 13) {
$div.next().find('input.key').focus()
}
})
.on('change.mapChange', function () {
var $div = $(event.target).parents('.w2ui-map-field')
var old = $div.attr('data-key')
var key = $div.find('.key').val()
var value = $div.find('.value').val()
// event before
var value_new = {}
var value_previous = {}
var aMap = null
var aIndex = null
value_new[key] = value
if (field.type == 'array') {
map.forEach(function (it, ind) {
if (it.key == old) aIndex = ind
})
aMap = map[aIndex]
}
if (old != null && field.type == 'map') {
value_previous[old] = map[old]
}
if (old != null && field.type == 'array') {
value_previous[old] = aMap.value
}
var edata = obj.trigger({ phase: 'before', target: field.field, type: 'change', originalEvent: event, value_new: value_new, value_previous: value_previous })
if (edata.isCancelled === true) {
return;
}
if (field.type == 'map') {
delete map[old];
var ind = map._order.indexOf(old)
if (key != '') {
if (map[key] != null) {
var newKey, more = 0
do { more++; newKey = key + more } while (map[newKey] != null)
key = newKey
$div.find('.key').val(newKey)
}
map[key] = value;
$div.attr('data-key', key)
if (ind != -1) {
map._order[ind] = key
} else {
map._order.push(key)
}
} else {
map._order.splice(ind, 1)
$div.find('.value').val('')
}
} else if (field.type == 'array') {
if (key != '') {
if (aMap == null) {
map.push({ key: key, value: value })
} else {
aMap.key = key
aMap.value = value
}
} else {
map.splice(aIndex, 1)
}
}
obj.setValue(field.field, map)
field.el.mapRefresh(map, div)
// event after
obj.trigger($.extend(edata, { phase: 'after' }));
})
}
field.el.mapRefresh(value, $(field.el).parent().find('.w2ui-map-container'))
})(this, field)
break;
case 'div':
case 'custom':
$(field.el).html(value)
break;
case 'html':
case 'empty':
break;
default:
$(field.el).val(value);
$(field.el).w2field($.extend({}, field.options, { type: field.type }));
break;
}
}
// wrap pages in div
var tmp = $(this.box).find('.w2ui-page');
for (var i = 0; i < tmp.length; i++) {
if ($(tmp[i]).find('> *').length > 1) $(tmp[i]).wrapInner('<div></div>');
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
this.resize();
return (new Date()).getTime() - time;
},
render: function (box) {
var time = (new Date()).getTime();
var obj = this;
if (typeof box === 'object') {
// remove from previous box
if ($(this.box).find('#form_'+ this.name +'_tabs').length > 0) {
$(this.box).removeAttr('name')
.removeClass('w2ui-reset w2ui-form')
.html('');
}
this.box = box;
}
if (!this.isGenerated) return;
if (!this.box) return;
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'render', box: (box != null ? box : this.box) });
if (edata.isCancelled === true) return;
var html = '<div class="w2ui-form-box">' +
(this.header !== '' ? '<div class="w2ui-form-header">' + this.header + '</div>' : '') +
' <div id="form_'+ this.name +'_toolbar" class="w2ui-form-toolbar" style="display: none"></div>' +
' <div id="form_'+ this.name +'_tabs" class="w2ui-form-tabs" style="display: none"></div>' +
this.formHTML +
'</div>';
$(this.box).attr('name', this.name)
.addClass('w2ui-reset w2ui-form')
.html(html);
if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
// init toolbar regardless it is defined or not
if (typeof this.toolbar.render !== 'function') {
this.toolbar = $().w2toolbar($.extend({}, this.toolbar, { name: this.name +'_toolbar', owner: this }));
this.toolbar.on('click', function (event) {
var edata = obj.trigger({ phase: 'before', type: 'toolbar', target: event.target, originalEvent: event });
if (edata.isCancelled === true) return;
// no default action
obj.trigger($.extend(edata, { phase: 'after' }));
});
}
if (typeof this.toolbar === 'object' && typeof this.toolbar.render === 'function') {
this.toolbar.render($('#form_'+ this.name +'_toolbar')[0]);
}
// init tabs regardless it is defined or not
if (typeof this.tabs.render !== 'function') {
this.tabs = $().w2tabs($.extend({}, this.tabs, { name: this.name +'_tabs', owner: this, active: this.tabs.active }));
this.tabs.on('click', function (event) {
obj.goto(this.get(event.target, true));
});
}
if (typeof this.tabs === 'object' && typeof this.tabs.render === 'function') {
this.tabs.render($('#form_'+ this.name +'_tabs')[0]);
if(this.tabs.active) this.tabs.click(this.tabs.active);
}
// event after
this.trigger($.extend(edata, { phase: 'after' }));
// after render actions
this.resize();
var url = (typeof this.url !== 'object' ? this.url : this.url.get);
if (url && this.recid !== 0 && this.recid != null) {
this.request();
} else {
this.refresh();
}
// attach to resize event
if ($('.w2ui-layout').length === 0) { // if there is layout, it will send a resize event
this.tmp_resize = function (event) {
if (w2ui[obj.name] == null) {
$(window).off('resize.w2uiResize', obj.tmp_resize);
} else {
w2ui[obj.name].resize();
}
}
$(window).off('resize.w2uiResize').on('resize.w2uiResize', obj.tmp_resize);
}
if (this.focus != -1) {
setTimeout(function () {
// if not rendered in 10ms, then wait 500ms
if ($(obj.box).find('input, select, textarea').length === 0) {
setTimeout(focusEl, 500); // need timeout to allow form to render
} else {
obj.applyFocus();
}
}, 50);
}
return (new Date()).getTime() - time;
},
applyFocus: function() {
var focus = this.focus;
var inputs = $(this.box).find('div:not(.w2ui-field-helper) > input, select, textarea, div > label:nth-child(1) > :radio').not('.file-input');
// find visible
while ($(inputs[focus]).is(':hidden') && inputs.length >= focus) {
focus++
}
if (inputs[focus]) inputs[focus].focus();
},
destroy: function () {
// event before
var edata = this.trigger({ phase: 'before', target: this.name, type: 'destroy' });
if (edata.isCancelled === true) return;
// clean up
if (typeof this.toolbar === 'object' && this.toolbar.destroy) this.toolbar.destroy();
if (typeof this.tabs === 'object' && this.tabs.destroy) this.tabs.destroy();
if ($(this.box).find('#form_'+ this.name +'_tabs').length > 0) {
$(this.box)
.removeAttr('name')
.removeClass('w2ui-reset w2ui-form')
.html('');
}
delete w2ui[this.name];
// event after
this.trigger($.extend(edata, { phase: 'after' }));
$(window).off('resize', 'body')
}
};
$.extend(w2form.prototype, w2utils.event);
w2obj.form = w2form;
})(jQuery);
/***********************************************************
* Compatibility with CommonJS and AMD modules
*
*********************************************************/
(function(global, w2ui) {
if (typeof define=='function' && define.amd) {
return define(function(){ return w2ui; });
}
if (typeof exports!='undefined') {
if (typeof module!='undefined' && module.exports)
return exports = module.exports = w2ui;
global = exports;
}
for (var m in w2ui) {
global[m] = w2ui[m];
}
})(this, {
w2ui: w2ui,
w2obj: w2obj,
w2utils: w2utils,
w2popup: w2popup,
w2alert: w2alert,
w2confirm: w2confirm,
w2prompt: w2prompt
});
//# sourceMappingURL=w2ui-1.5.js.map