/*!
* Mobiscroll v2.14.4
* http://mobiscroll.com
*
* Copyright 2010-2014, Acid Media
* Licensed under the MIT license.
*
*/
//core
(function ($, undefined) {
function testProps(props) {
var i;
for (i in props) {
if (mod[props[i]] !== undefined) {
return true;
}
}
return false;
}
function testPrefix() {
var prefixes = ['Webkit', 'Moz', 'O', 'ms'],
p;
for (p in prefixes) {
if (testProps([prefixes[p] + 'Transform'])) {
return '-' + prefixes[p].toLowerCase() + '-';
}
}
return '';
}
function init(that, options, args) {
var ret = that;
// Init
if (typeof options === 'object') {
return that.each(function () {
if (!this.id) {
this.id = 'mobiscroll' + (++id);
}
if (instances[this.id]) {
instances[this.id].destroy();
}
new $.mobiscroll.classes[options.component || 'Scroller'](this, options);
});
}
// Method call
if (typeof options === 'string') {
that.each(function () {
var r,
inst = instances[this.id];
if (inst && inst[options]) {
r = inst[options].apply(this, Array.prototype.slice.call(args, 1));
if (r !== undefined) {
ret = r;
return false;
}
}
});
}
return ret;
}
var id = +new Date(),
instances = {},
extend = $.extend,
mod = document.createElement('modernizr').style,
has3d = testProps(['perspectiveProperty', 'WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']),
hasFlex = testProps(['flex', 'msFlex', 'WebkitBoxDirection']),
prefix = testPrefix(),
pr = prefix.replace(/^\-/, '').replace(/\-$/, '').replace('moz', 'Moz');
$.fn.mobiscroll = function (method) {
extend(this, $.mobiscroll.components);
return init(this, method, arguments);
};
$.mobiscroll = $.mobiscroll || {
version: '2.14.4',
util: {
prefix: prefix,
jsPrefix: pr,
has3d: has3d,
hasFlex: hasFlex,
testTouch: function (e, elm) {
if (e.type == 'touchstart') {
$(elm).attr('data-touch', '1');
} else if ($(elm).attr('data-touch')) {
$(elm).removeAttr('data-touch');
return false;
}
return true;
},
objectToArray: function (obj) {
var arr = [],
i;
for (i in obj) {
arr.push(obj[i]);
}
return arr;
},
arrayToObject: function (arr) {
var obj = {},
i;
if (arr) {
for (i = 0; i < arr.length; i++) {
obj[arr[i]] = arr[i];
}
}
return obj;
},
isNumeric: function (a) {
return a - parseFloat(a) >= 0;
},
isString: function (s) {
return typeof s === 'string';
},
getCoord: function (e, c) {
var ev = e.originalEvent || e;
return ev.changedTouches ? ev.changedTouches[0]['page' + c] : e['page' + c];
},
getPosition: function (t, vertical) {
var style = window.getComputedStyle ? getComputedStyle(t[0]) : t[0].style,
matrix,
px;
if (has3d) {
$.each(['t', 'webkitT', 'MozT', 'OT', 'msT'], function (i, v) {
if (style[v + 'ransform'] !== undefined) {
matrix = style[v + 'ransform'];
return false;
}
});
matrix = matrix.split(')')[0].split(', ');
px = vertical ? (matrix[13] || matrix[5]) : (matrix[12] || matrix[4]);
} else {
px = vertical ? style.top.replace('px', '') : style.left.replace('px', '');
}
return px;
},
constrain: function (val, min, max) {
return Math.max(min, Math.min(val, max));
}
},
tapped: false,
presets: {
scroller: {},
numpad: {}
},
themes: {
listview: {},
menustrip: {}
},
i18n: {},
instances: instances,
classes: {},
components: {},
defaults: {
theme: 'mobiscroll',
context: 'body'
},
userdef: {},
setDefaults: function (o) {
extend(this.userdef, o);
},
presetShort: function (name, c, p) {
this.components[name] = function (s) {
return init(this, extend(s, { component: c, preset: p === false ? undefined : name }), arguments);
};
}
};
})(jQuery);
//widget
(function ($, window, document, undefined) {
var $activeElm,
preventShow,
extend = $.extend,
ms = $.mobiscroll,
instances = ms.instances,
userdef = ms.userdef,
util = ms.util,
pr = util.jsPrefix,
has3d = util.has3d,
getCoord = util.getCoord,
constrain = util.constrain,
isString = util.isString,
isOldAndroid = /android [1-3]/i.test(navigator.userAgent),
animEnd = 'webkitAnimationEnd animationend',
empty = function () { },
prevdef = function (ev) { ev.preventDefault(); };
ms.classes.Widget = function (el, settings, inherit) {
var $ariaDiv,
$ctx,
$header,
$markup,
$overlay,
$persp,
$popup,
$wnd,
$wrapper,
buttons,
btn,
doAnim,
hasButtons,
isModal,
lang,
modalWidth,
modalHeight,
posEvents,
preset,
preventPos,
s,
scrollLock,
setReadOnly,
theme,
wasReadOnly,
wndWidth,
wndHeight,
that = this,
$elm = $(el),
elmList = [],
posDebounce = {};
function onBtnStart(ev) {
// Can't call preventDefault here, it kills page scroll
if (btn) {
btn.removeClass('dwb-a');
}
btn = $(this);
// Active button
if (!btn.hasClass('dwb-d') && !btn.hasClass('dwb-nhl')) {
btn.addClass('dwb-a');
}
if (ev.type === 'mousedown') {
$(document).on('mouseup', onBtnEnd);
}
}
function onBtnEnd(ev) {
if (btn) {
btn.removeClass('dwb-a');
btn = null;
}
if (ev.type === 'mouseup') {
$(document).off('mouseup', onBtnEnd);
}
}
function onWndKeyDown(ev) {
if (ev.keyCode == 13) {
that.select();
} else if (ev.keyCode == 27) {
that.cancel();
}
}
function onShow(prevFocus) {
if (!prevFocus) {
$popup.focus();
}
that.ariaMessage(s.ariaMessage);
}
function onHide(prevAnim) {
var activeEl,
value,
type,
focus = s.focusOnClose;
$markup.remove();
if ($activeElm && !prevAnim) {
setTimeout(function () {
if (focus === undefined || focus === true) {
preventShow = true;
activeEl = $activeElm[0];
type = activeEl.type;
value = activeEl.value;
try {
activeEl.type = 'button';
} catch (ex) { }
$activeElm.focus();
activeEl.type = type;
activeEl.value = value;
} else if (focus) {
// If a mobiscroll field is focused, allow show
if (instances[$(focus).attr('id')]) {
ms.tapped = false;
}
$(focus).focus();
}
}, 200);
}
that._isVisible = false;
event('onHide', []);
}
function onPosition(ev) {
clearTimeout(posDebounce[ev.type]);
posDebounce[ev.type] = setTimeout(function () {
var isScroll = ev.type == 'scroll';
if (isScroll && !scrollLock) {
return;
}
that.position(!isScroll);
}, 200);
}
function show(beforeShow) {
if (!ms.tapped) {
if (beforeShow) {
beforeShow();
}
// Hide virtual keyboard
if ($(document.activeElement).is('input,textarea')) {
$(document.activeElement).blur();
}
$activeElm = $elm;
that.show();
}
setTimeout(function () {
preventShow = false;
}, 300); // With jQuery < 1.9 focus is fired twice in IE
}
function event(name, args) {
var ret;
args.push(that);
$.each([userdef, theme, preset, settings], function (i, v) {
if (v && v[name]) { // Call preset event
ret = v[name].apply(el, args);
}
});
return ret;
}
/**
* Positions the scroller on the screen.
*/
that.position = function (check) {
var w,
l,
t,
anchor,
aw, // anchor width
ah, // anchor height
ap, // anchor position
at, // anchor top
al, // anchor left
arr, // arrow
arrw, // arrow width
arrl, // arrow left
dh,
scroll,
sl, // scroll left
st, // scroll top
totalw = 0,
minw = 0,
css = {},
nw = Math.min($wnd[0].innerWidth || $wnd.innerWidth(), $persp.width()), //$persp.width(), // To get the width without scrollbar
nh = $wnd[0].innerHeight || $wnd.innerHeight();
if ((wndWidth === nw && wndHeight === nh && check) || preventPos) {
return;
}
if (that._isFullScreen || /top|bottom/.test(s.display)) {
// Set width, if document is larger than viewport, needs to be set before onPosition (for calendar)
$popup.width(nw);
}
if (event('onPosition', [$markup, nw, nh]) === false || !isModal) {
return;
}
sl = $wnd.scrollLeft();
st = $wnd.scrollTop();
anchor = s.anchor === undefined ? $elm : $(s.anchor);
// Set / unset liquid layout based on screen width, but only if not set explicitly by the user
if (that._isLiquid && s.layout !== 'liquid') {
if (nw < 400) {
$markup.addClass('dw-liq');
} else {
$markup.removeClass('dw-liq');
}
}
if (!that._isFullScreen && /modal|bubble/.test(s.display)) {
$wrapper.width('');
$('.mbsc-w-p', $markup).each(function () {
w = $(this).outerWidth(true);
totalw += w;
minw = (w > minw) ? w : minw;
});
w = totalw > nw ? minw : totalw;
$wrapper.width(w).css('white-space', totalw > nw ? '' : 'nowrap');
}
modalWidth = that._isFullScreen ? nw : $popup.outerWidth();
modalHeight = that._isFullScreen ? nh : $popup.outerHeight(true);
scrollLock = modalHeight <= nh && modalWidth <= nw;
that.scrollLock = scrollLock;
if (s.display == 'modal') {
l = Math.max(0, sl + (nw - modalWidth) / 2);
t = st + (nh - modalHeight) / 2;
} else if (s.display == 'bubble') {
scroll = true;
arr = $('.dw-arrw-i', $markup);
ap = anchor.offset();
at = Math.abs($ctx.offset().top - ap.top);
al = Math.abs($ctx.offset().left - ap.left);
// horizontal positioning
aw = anchor.outerWidth();
ah = anchor.outerHeight();
l = constrain(al - ($popup.outerWidth(true) - aw) / 2, sl + 3, sl + nw - modalWidth - 3);
// vertical positioning
t = at - modalHeight; // above the input
if ((t < st) || (at > st + nh)) { // if doesn't fit above or the input is out of the screen
$popup.removeClass('dw-bubble-top').addClass('dw-bubble-bottom');
t = at + ah; // below the input
} else {
$popup.removeClass('dw-bubble-bottom').addClass('dw-bubble-top');
}
// Calculate Arrow position
arrw = arr.outerWidth();
arrl = constrain(al + aw / 2 - (l + (modalWidth - arrw) / 2), 0, arrw);
// Limit Arrow position
$('.dw-arr', $markup).css({ left: arrl });
} else {
l = sl;
if (s.display == 'top') {
t = st;
} else if (s.display == 'bottom') {
t = st + nh - modalHeight;
}
}
t = t < 0 ? 0 : t;
css.top = t;
css.left = l;
$popup.css(css);
// If top + modal height > doc height, increase doc height
$persp.height(0);
dh = Math.max(t + modalHeight, s.context == 'body' ? $(document).height() : $ctx[0].scrollHeight);
$persp.css({ height: dh });
// Scroll needed
if (scroll && ((t + modalHeight > st + nh) || (at > st + nh))) {
preventPos = true;
setTimeout(function () { preventPos = false; }, 300);
$wnd.scrollTop(Math.min(t + modalHeight - nh, dh - nh));
}
wndWidth = nw;
wndHeight = nh;
};
/**
* Show mobiscroll on focus and click event of the parameter.
* @param {jQuery} $elm - Events will be attached to this element.
* @param {Function} [beforeShow=undefined] - Optional function to execute before showing mobiscroll.
*/
that.attachShow = function ($elm, beforeShow) {
elmList.push($elm);
if (s.display !== 'inline') {
if (setReadOnly) {
$elm.on('mousedown.dw', function (ev) {
// Prevent input to get focus on tap (virtual keyboard pops up on some devices)
ev.preventDefault();
});
}
if (s.showOnFocus) {
$elm.on('focus.dw', function () {
if (!preventShow) {
show(beforeShow);
}
});
}
if (s.showOnTap) {
that.tap($elm, function () {
show(beforeShow);
});
}
}
};
/**
* Set button handler.
*/
that.select = function () {
if (!isModal || that.hide(false, 'set') !== false) {
that._fillValue();
event('onSelect', [that._value]);
}
};
/**
* Cancel and hide the scroller instance.
*/
that.cancel = function () {
if (!isModal || that.hide(false, 'cancel') !== false) {
event('onCancel', [that._value]);
}
};
/**
* Clear button handler.
*/
that.clear = function () {
event('onClear', [$markup]);
if (isModal && !that.live) {
that.hide(false, 'clear');
}
that.setValue(null, true);
};
/**
* Enables the scroller and the associated input.
*/
that.enable = function () {
s.disabled = false;
if (that._isInput) {
$elm.prop('disabled', false);
}
};
/**
* Disables the scroller and the associated input.
*/
that.disable = function () {
s.disabled = true;
if (that._isInput) {
$elm.prop('disabled', true);
}
};
/**
* Shows the scroller instance.
* @param {Boolean} prevAnim - Prevent animation if true
* @param {Boolean} prevFocus - Prevent focusing if true
*/
that.show = function (prevAnim, prevFocus) {
// Create wheels
var html;
if (s.disabled || that._isVisible) {
return;
}
if (doAnim !== false) {
if (s.display == 'top') {
doAnim = 'slidedown';
}
if (s.display == 'bottom') {
doAnim = 'slideup';
}
}
// Parse value from input
that._readValue();
event('onBeforeShow', []);
// Create wheels containers
html = '
';
$markup = $(html);
$persp = $('.dw-persp', $markup);
$overlay = $('.dwo', $markup);
$wrapper = $('.dwwr', $markup);
$header = $('.dwv', $markup);
$popup = $('.dw', $markup);
$ariaDiv = $('.dw-aria', $markup);
that._markup = $markup;
that._header = $header;
that._isVisible = true;
posEvents = 'orientationchange resize';
that._markupReady();
event('onMarkupReady', [$markup]);
// Show
if (isModal) {
// Enter / ESC
$(window).on('keydown', onWndKeyDown);
// Prevent scroll if not specified otherwise
if (s.scrollLock) {
$markup.on('touchmove mousewheel DOMMouseScroll', function (ev) {
if (scrollLock) {
ev.preventDefault();
}
});
}
// Disable inputs to prevent bleed through (Android bug)
if (pr !== 'Moz') {
$('input,select,button', $ctx).each(function () {
if (!this.disabled) {
$(this).addClass('dwtd').prop('disabled', true);
}
});
}
posEvents += ' scroll';
ms.activeInstance = that;
$markup.appendTo($ctx);
if (has3d && doAnim && !prevAnim) {
$markup.addClass('dw-in dw-trans').on(animEnd, function () {
$markup.off(animEnd).removeClass('dw-in dw-trans').find('.dw').removeClass('dw-' + doAnim);
onShow(prevFocus);
}).find('.dw').addClass('dw-' + doAnim);
}
} else if ($elm.is('div')) {
$elm.html($markup);
} else {
$markup.insertAfter($elm);
}
event('onMarkupInserted', [$markup]);
// Set position
that.position();
$wnd.on(posEvents, onPosition);
// Events
$markup
.on('selectstart mousedown', prevdef) // Prevents blue highlight on Android and text selection in IE
.on('click', '.dwb-e', prevdef)
.on('keydown', '.dwb-e', function (ev) {
if (ev.keyCode == 32) { // Space
ev.preventDefault();
ev.stopPropagation();
$(this).click();
}
});
setTimeout(function () {
// Init buttons
$.each(buttons, function (i, b) {
that.tap($('.dwb' + i, $markup), function (ev) {
b = isString(b) ? that.buttons[b] : b;
b.handler.call(this, ev, that);
}, true);
});
if (s.closeOnOverlay) {
that.tap($overlay, function () {
that.cancel();
});
}
if (isModal && !doAnim) {
onShow(prevFocus);
}
$markup
.on('touchstart mousedown', '.dwb-e', onBtnStart)
.on('touchend', '.dwb-e', onBtnEnd);
that._attachEvents($markup);
}, 300);
event('onShow', [$markup, that._tempValue]);
};
/**
* Hides the scroller instance.
*/
that.hide = function (prevAnim, btn, force) {
// If onClose handler returns false, prevent hide
if (!that._isVisible || (!force && !that._isValid && btn == 'set') || (!force && event('onClose', [that._tempValue, btn]) === false)) {
return false;
}
// Hide wheels and overlay
if ($markup) {
// Re-enable temporary disabled fields
if (pr !== 'Moz') {
$('.dwtd', $ctx).each(function () {
$(this).prop('disabled', false).removeClass('dwtd');
});
}
if (has3d && isModal && doAnim && !prevAnim && !$markup.hasClass('dw-trans')) { // If dw-trans class was not removed, means that there was no animation
$markup.addClass('dw-out dw-trans').find('.dw').addClass('dw-' + doAnim).on(animEnd, function () {
onHide(prevAnim);
});
} else {
onHide(prevAnim);
}
// Stop positioning on window resize
$wnd.off(posEvents, onPosition);
}
if (isModal) {
$(window).off('keydown', onWndKeyDown);
delete ms.activeInstance;
}
};
that.ariaMessage = function (txt) {
$ariaDiv.html('');
setTimeout(function () {
$ariaDiv.html(txt);
}, 100);
};
/**
* Return true if the scroller is currently visible.
*/
that.isVisible = function () {
return that._isVisible;
};
// Protected functions to override
that.setValue = empty;
that._generateContent = empty;
that._attachEvents = empty;
that._readValue = empty;
that._fillValue = empty;
that._markupReady = empty;
that._processSettings = empty;
// Generic widget functions
/**
* Attach tap event to the given element.
*/
that.tap = function (el, handler, prevent) {
var startX,
startY,
moved;
if (s.tap) {
el.on('touchstart.dw', function (ev) {
// Can't always call preventDefault here, it kills page scroll
if (prevent) {
ev.preventDefault();
}
startX = getCoord(ev, 'X');
startY = getCoord(ev, 'Y');
moved = false;
}).on('touchmove.dw', function (ev) {
// If movement is more than 20px, don't fire the click event handler
if (Math.abs(getCoord(ev, 'X') - startX) > 20 || Math.abs(getCoord(ev, 'Y') - startY) > 20) {
moved = true;
}
}).on('touchend.dw', function (ev) {
var that = this;
if (!moved) {
// preventDefault and setTimeout are needed by iOS
ev.preventDefault();
//setTimeout(function () {
handler.call(that, ev);
//}, isOldAndroid ? 400 : 10);
}
// Prevent click events to happen
ms.tapped = true;
setTimeout(function () {
ms.tapped = false;
}, 500);
});
}
el.on('click.dw', function (ev) {
if (!ms.tapped) {
// If handler was not called on touchend, call it on click;
handler.call(this, ev);
}
ev.preventDefault();
});
};
/**
* Sets one ore more options.
*/
that.option = function (opt, value) {
var obj = {};
if (typeof opt === 'object') {
obj = opt;
} else {
obj[opt] = value;
}
that.init(obj);
};
/**
* Destroys the mobiscroll instance.
*/
that.destroy = function () {
// Force hide without animation
that.hide(true, false, true);
// Remove all events from elements
$.each(elmList, function (i, v) {
v.off('.dw');
});
// Reset original readonly state
if (that._isInput && setReadOnly) {
el.readOnly = wasReadOnly;
}
event('onDestroy', []);
// Delete scroller instance
delete instances[el.id];
that = null;
};
/**
* Returns the mobiscroll instance.
*/
that.getInst = function () {
return that;
};
/**
* Triggers a mobiscroll event.
*/
that.trigger = event;
/**
* Scroller initialization.
*/
that.init = function (ss) {
that.settings = s = {};
// Update original user settings
extend(settings, ss);
extend(s, ms.defaults, that._defaults, userdef, settings);
// Get theme defaults
theme = ms.themes[s.theme] || ms.themes.mobiscroll;
// Get language defaults
lang = ms.i18n[s.lang];
event('onThemeLoad', [lang, settings]);
extend(s, theme, lang, userdef, settings);
preset = ms.presets[that._class][s.preset];
// Add default buttons
s.buttons = s.buttons || (s.display !== 'inline' ? ['set', 'cancel'] : []);
// Hide header text in inline mode by default
s.headerText = s.headerText === undefined ? (s.display !== 'inline' ? '{value}' : false) : s.headerText;
if (preset) {
preset = preset.call(el, that);
extend(s, preset, settings); // Load preset settings
}
if (!ms.themes[s.theme]) {
s.theme = 'mobiscroll';
}
that._isLiquid = (s.layout || (/top|bottom/.test(s.display) ? 'liquid' : '')) === 'liquid';
that._processSettings();
// Unbind all events (if re-init)
$elm.off('.dw');
doAnim = isOldAndroid ? false : s.animate;
buttons = s.buttons;
isModal = s.display !== 'inline';
setReadOnly = s.showOnFocus || s.showOnTap;
$wnd = $(s.context == 'body' ? window : s.context);
$ctx = $(s.context);
that.context = $wnd;
that.live = true;
// If no set button is found, live mode is activated
$.each(buttons, function (i, b) {
if (b === 'set' || b.handler === 'set') {
that.live = false;
return false;
}
});
that.buttons.set = { text: s.setText, handler: 'set' };
that.buttons.cancel = { text: (that.live) ? s.closeText : s.cancelText, handler: 'cancel' };
that.buttons.clear = { text: s.clearText, handler: 'clear' };
that._isInput = $elm.is('input');
hasButtons = buttons.length > 0;
if (that._isVisible) {
that.hide(true, false, true);
}
if (isModal) {
that._readValue();
if (that._isInput && setReadOnly) {
// Set element readonly, save original state
if (wasReadOnly === undefined) {
wasReadOnly = el.readOnly;
}
el.readOnly = true;
}
that.attachShow($elm);
} else {
that.show();
}
//if (that._isInput) {
$elm.on('change.dw', function () {
if (!that._preventChange) {
that.setVal($elm.val(), true, false);
}
that._preventChange = false;
});
//}
event('onInit', []);
};
that.buttons = {};
that.handlers = {
set: that.select,
cancel: that.cancel,
clear: that.clear
};
that._value = null;
that._isValid = true;
that._isVisible = false;
// Constructor
if (!inherit) {
instances[el.id] = that;
that.init(settings);
}
};
ms.classes.Widget.prototype._defaults = {
// Localization
lang: 'en',
setText: 'Set',
selectedText: 'Selected',
closeText: 'Close',
cancelText: 'Cancel',
clearText: 'Clear',
// Options
disabled: false,
closeOnOverlay: true,
showOnFocus: true,
showOnTap: true,
display: 'modal',
scrollLock: true,
tap: true,
btnClass: 'dwb',
btnWidth: true,
focusOnClose: false // Temporary for iOS8
};
ms.themes.mobiscroll = {
rows: 5,
showLabel: false,
headerText: false,
btnWidth: false,
selectedLineHeight: true,
selectedLineBorder: 1,
dateOrder: 'MMddyy',
weekDays: 'min',
checkIcon: 'ion-ios7-checkmark-empty',
btnPlusClass: 'mbsc-ic mbsc-ic-arrow-down5',
btnMinusClass: 'mbsc-ic mbsc-ic-arrow-up5',
btnCalPrevClass: 'mbsc-ic mbsc-ic-arrow-left5',
btnCalNextClass: 'mbsc-ic mbsc-ic-arrow-right5'
};
// Prevent re-show on window focus
$(window).on('focus', function () {
if ($activeElm) {
preventShow = true;
}
});
// Prevent standard behaviour on body click
$(document).on('mouseover mouseup mousedown click', function (ev) {
if (ms.tapped) {
ev.stopPropagation();
ev.preventDefault();
return false;
}
});
})(jQuery, window, document);
//scroller
(function ($, window, document, undefined) {
var move,
ms = $.mobiscroll,
classes = ms.classes,
instances = ms.instances,
util = ms.util,
pr = util.jsPrefix,
has3d = util.has3d,
hasFlex = util.hasFlex,
getCoord = util.getCoord,
constrain = util.constrain,
testTouch = util.testTouch;
classes.Scroller = function (el, settings, inherit) {
var $markup,
btn,
isScrollable,
itemHeight,
s,
trigger,
click,
moved,
start,
startTime,
stop,
p,
min,
max,
target,
index,
lines,
timer,
that = this,
$elm = $(el),
iv = {},
pos = {},
pixels = {},
wheels = [];
// Event handlers
function onStart(ev) {
// Scroll start
if (testTouch(ev, this) && !move && !click && !btn && !isReadOnly(this)) {
// Prevent touch highlight
ev.preventDefault();
// Better performance if there are tap events on document
ev.stopPropagation();
move = true;
isScrollable = s.mode != 'clickpick';
target = $('.dw-ul', this);
setGlobals(target);
moved = iv[index] !== undefined; // Don't allow tap, if still moving
p = moved ? getCurrentPosition(target) : pos[index];
start = getCoord(ev, 'Y');
startTime = new Date();
stop = start;
scroll(target, index, p, 0.001);
if (isScrollable) {
target.closest('.dwwl').addClass('dwa');
}
if (ev.type === 'mousedown') {
$(document).on('mousemove', onMove).on('mouseup', onEnd);
}
}
}
function onMove(ev) {
if (move) {
if (isScrollable) {
// Prevent scroll
ev.preventDefault();
ev.stopPropagation();
stop = getCoord(ev, 'Y');
if (Math.abs(stop - start) > 3 || moved) {
scroll(target, index, constrain(p + (start - stop) / itemHeight, min - 1, max + 1));
moved = true;
}
}
}
}
function onEnd(ev) {
if (move) {
var time = new Date() - startTime,
val = constrain(p + (start - stop) / itemHeight, min - 1, max + 1),
speed,
dist,
tindex,
ttop = target.offset().top;
// Better performance if there are tap events on document
ev.stopPropagation();
if (has3d && time < 300) {
speed = (stop - start) / time;
dist = (speed * speed) / s.speedUnit;
if (stop - start < 0) {
dist = -dist;
}
} else {
dist = stop - start;
}
tindex = Math.round(p - dist / itemHeight);
if (!moved) { // this is a "tap"
var idx = Math.floor((stop - ttop) / itemHeight),
li = $($('.dw-li', target)[idx]),
valid = li.hasClass('dw-v'),
hl = isScrollable;
if (trigger('onValueTap', [li]) !== false && valid) {
tindex = idx;
} else {
hl = true;
}
if (hl && valid) {
li.addClass('dw-hl'); // Highlight
setTimeout(function () {
li.removeClass('dw-hl');
}, 100);
}
}
if (isScrollable) {
calc(target, tindex, 0, true, Math.round(val));
}
if (ev.type === 'mouseup') {
$(document).off('mousemove', onMove).off('mouseup', onEnd);
}
move = false;
}
}
function onBtnStart(ev) {
btn = $(this);
// +/- buttons
if (testTouch(ev, this)) {
step(ev, btn.closest('.dwwl'), btn.hasClass('dwwbp') ? plus : minus);
}
if (ev.type === 'mousedown') {
$(document).on('mouseup', onBtnEnd);
}
}
function onBtnEnd(ev) {
btn = null;
if (click) {
clearInterval(timer);
click = false;
}
if (ev.type === 'mouseup') {
$(document).off('mouseup', onBtnEnd);
}
}
function onKeyDown(ev) {
if (ev.keyCode == 38) { // up
step(ev, $(this), minus);
} else if (ev.keyCode == 40) { // down
step(ev, $(this), plus);
}
}
function onKeyUp() {
if (click) {
clearInterval(timer);
click = false;
}
}
function onScroll(ev) {
if (!isReadOnly(this)) {
ev.preventDefault();
ev = ev.originalEvent || ev;
var delta = ev.wheelDelta ? (ev.wheelDelta / 120) : (ev.detail ? (-ev.detail / 3) : 0),
t = $('.dw-ul', this);
setGlobals(t);
calc(t, Math.round(pos[index] - delta), delta < 0 ? 1 : 2);
}
}
// Private functions
function step(ev, w, func) {
ev.stopPropagation();
ev.preventDefault();
if (!click && !isReadOnly(w) && !w.hasClass('dwa')) {
click = true;
// + Button
var t = w.find('.dw-ul');
setGlobals(t);
clearInterval(timer);
timer = setInterval(function () { func(t); }, s.delay);
func(t);
}
}
function isReadOnly(wh) {
if ($.isArray(s.readonly)) {
var i = $('.dwwl', $markup).index(wh);
return s.readonly[i];
}
return s.readonly;
}
function generateWheelItems(i) {
var html = '',
w = wheels[i],
l = 1,
labels = w.labels || [],
values = w.values,
keys = w.keys || values;
$.each(values, function (j, v) {
if (l % 20 === 0) {
html += '
';
}
html += '
' +
'
1 ? ' style="line-height:' + Math.round(itemHeight / lines) + 'px;font-size:' + Math.round(itemHeight / lines * 0.8) + 'px;"' : '') + '>' + v + '
';
l++;
});
html += '
';
return html;
}
function setGlobals(t) {
var multiple = t.closest('.dwwl').hasClass('dwwms');
min = $('.dw-li', t).index($(multiple ? '.dw-li' : '.dw-v', t).eq(0));
max = Math.max(min, $('.dw-li', t).index($(multiple ? '.dw-li' : '.dw-v', t).eq(-1)) - (multiple ? s.rows - 1 : 0));
index = $('.dw-ul', $markup).index(t);
}
function formatHeader(v) {
var t = s.headerText;
return t ? (typeof t === 'function' ? t.call(el, v) : t.replace(/\{value\}/i, v)) : '';
}
function getCurrentPosition(t) {
return Math.round(-util.getPosition(t, true) / itemHeight);
}
function ready(t, i) {
clearTimeout(iv[i]);
delete iv[i];
t.closest('.dwwl').removeClass('dwa');
}
function scroll(t, index, val, time, active) {
var px = -val * itemHeight,
style = t[0].style;
if (px == pixels[index] && iv[index]) {
return;
}
//if (time && px != pixels[index]) {
// Trigger animation start event
//trigger('onAnimStart', [$markup, index, time]);
//}
pixels[index] = px;
if (has3d) {
style[pr + 'Transition'] = util.prefix + 'transform ' + (time ? time.toFixed(3) : 0) + 's ease-out';
style[pr + 'Transform'] = 'translate3d(0,' + px + 'px,0)';
} else {
style.top = px + 'px';
}
if (iv[index]) {
ready(t, index);
}
if (time && active) {
t.closest('.dwwl').addClass('dwa');
iv[index] = setTimeout(function () {
ready(t, index);
}, time * 1000);
}
pos[index] = val;
}
function getValid(val, t, dir, multiple) {
var cell = $('.dw-li[data-val="' + val + '"]', t),
cells = $('.dw-li', t),
v = cells.index(cell),
l = cells.length;
if (multiple) {
setGlobals(t);
} else if (!cell.hasClass('dw-v')) { // Scroll to a valid cell
var cell1 = cell,
cell2 = cell,
dist1 = 0,
dist2 = 0;
while (v - dist1 >= 0 && !cell1.hasClass('dw-v')) {
dist1++;
cell1 = cells.eq(v - dist1);
}
while (v + dist2 < l && !cell2.hasClass('dw-v')) {
dist2++;
cell2 = cells.eq(v + dist2);
}
// If we have direction (+/- or mouse wheel), the distance does not count
if (((dist2 < dist1 && dist2 && dir !== 2) || !dist1 || (v - dist1 < 0) || dir == 1) && cell2.hasClass('dw-v')) {
cell = cell2;
v = v + dist2;
} else {
cell = cell1;
v = v - dist1;
}
}
return {
cell: cell,
v: multiple ? constrain(v, min, max) : v,
val: cell.hasClass('dw-v') ? cell.attr('data-val') : null
};
}
function scrollToPos(time, index, manual, dir, active) {
// Call validation event
if (trigger('validate', [$markup, index, time, dir]) !== false) {
// Set scrollers to position
$('.dw-ul', $markup).each(function (i) {
var t = $(this),
multiple = t.closest('.dwwl').hasClass('dwwms'),
sc = i == index || index === undefined,
res = getValid(that._tempWheelArray[i], t, dir, multiple),
cell = res.cell;
if (!(cell.hasClass('dw-sel')) || sc) {
// Set valid value
that._tempWheelArray[i] = res.val;
if (!multiple) {
$('.dw-sel', t).removeAttr('aria-selected');
cell.attr('aria-selected', 'true');
}
// Add selected class to cell
$('.dw-sel', t).removeClass('dw-sel');
cell.addClass('dw-sel');
// Scroll to position
scroll(t, i, res.v, sc ? time : 0.1, sc ? active : false);
}
});
trigger('onValidated', []);
// Reformat value if validation changed something
that._tempValue = s.formatResult(that._tempWheelArray);
if (that.live) {
that._hasValue = manual || that._hasValue;
setValue(manual, manual, 0, true);
}
that._header.html(formatHeader(that._tempValue));
if (manual) {
trigger('onChange', [that._tempValue]);
}
}
}
function calc(t, val, dir, anim, orig) {
val = constrain(val, min, max);
var cell = $('.dw-li', t).eq(val),
o = orig === undefined ? val : orig,
active = orig !== undefined,
idx = index,
dist = Math.abs(val - o),
time = anim ? (val == o ? 0.1 : dist * s.timeUnit * Math.max(0.5, (100 - dist) / 100)) : 0;
// Set selected scroller value
that._tempWheelArray[idx] = cell.attr('data-val');
scroll(t, idx, val, time, active);
setTimeout(function () {
// Validate
scrollToPos(time, idx, true, dir, active);
}, 10);
}
function plus(t) {
var val = pos[index] + 1;
calc(t, val > max ? min : val, 1, true);
}
function minus(t) {
var val = pos[index] - 1;
calc(t, val < min ? max : val, 2, true);
}
function setValue(fill, change, time, noscroll, temp) {
if (that._isVisible && !noscroll) {
scrollToPos(time);
}
that._tempValue = s.formatResult(that._tempWheelArray);
if (!temp) {
that._wheelArray = that._tempWheelArray.slice(0);
that._value = that._hasValue ? that._tempValue : null;
}
if (fill) {
trigger('onValueFill', [that._hasValue ? that._tempValue : '', change]);
if (that._isInput) {
$elm.val(that._hasValue ? that._tempValue : '');
}
if (change) {
that._preventChange = true;
$elm.change();
}
}
}
// Call the parent constructor
classes.Widget.call(this, el, settings, true);
// Public functions
/**
* Gets the selected wheel values, formats it, and set the value of the scroller instance.
* If input parameter is true, populates the associated input element.
* @param {Array} values Wheel values.
* @param {Boolean} [fill=false] Also set the value of the associated input element.
* @param {Number} [time=0] Animation time
* @param {Boolean} [temp=false] If true, then only set the temporary value.(only scroll there but not set the value)
* @param {Boolean} [change=false] Trigger change on the input element
*/
that.setVal = that._setVal = function (val, fill, change, temp, time) {
that._hasValue = val !== null && val !== undefined;
that._tempWheelArray = $.isArray(val) ? val.slice(0) : s.parseValue.call(el, val, that) || [];
setValue(fill, change === undefined ? fill : change, time, false, temp);
};
/**
* Returns the selected value
*/
that.getVal = that._getVal = function (temp) {
var val = that._hasValue ? that[temp ? '_tempValue' : '_value'] : null;
return util.isNumeric(val) ? +val : val;
};
/*
* Sets the wheel values (passed as an array)
*/
that.setArrayVal = that.setVal;
/*
* Returns the selected wheel values as an array
*/
that.getArrayVal = function (temp) {
return temp ? that._tempWheelArray : that._wheelArray;
};
// @deprecated since 2.14.0, backward compatibility code
// ---
that.setValue = function (val, fill, time, temp, change) {
that.setVal(val, fill, change, temp, time);
};
/**
* Return the selected wheel values.
*/
that.getValue = that.getArrayVal;
// ---
/**
* Changes the values of a wheel, and scrolls to the correct position
* @param {Array} idx Indexes of the wheels to change.
* @param {Number} [time=0] Animation time when scrolling to the selected value on the new wheel.
* @param {Boolean} [manual=false] Indicates that the change was triggered by the user or from code.
*/
that.changeWheel = function (idx, time, manual) {
if ($markup) {
var i = 0,
nr = idx.length;
$.each(s.wheels, function (j, wg) {
$.each(wg, function (k, w) {
if ($.inArray(i, idx) > -1) {
wheels[i] = w;
$('.dw-ul', $markup).eq(i).html(generateWheelItems(i));
nr--;
if (!nr) {
that.position();
scrollToPos(time, undefined, manual);
return false;
}
}
i++;
});
if (!nr) {
return false;
}
});
}
};
/**
* Returns the closest valid cell.
*/
that.getValidCell = getValid;
// Protected overrides
that._generateContent = function () {
var lbl,
html = '',
l = 0;
$.each(s.wheels, function (i, wg) { // Wheel groups
html += '';
});
return html;
};
that._attachEvents = function ($markup) {
$markup
.on('DOMMouseScroll mousewheel', '.dwwl', onScroll)
.on('keydown', '.dwwl', onKeyDown)
.on('keyup', '.dwwl', onKeyUp)
.on('touchstart mousedown', '.dwwl', onStart)
.on('touchmove', '.dwwl', onMove)
.on('touchend', '.dwwl', onEnd)
.on('touchstart mousedown', '.dwwb', onBtnStart)
.on('touchend', '.dwwb', onBtnEnd);
};
that._markupReady = function () {
$markup = that._markup;
scrollToPos();
};
that._fillValue = function () {
that._hasValue = true;
setValue(true, true, 0, true);
};
that._readValue = function () {
var v = $elm.val() || '';
that._hasValue = v !== '';
that._tempWheelArray = that._wheelArray ? that._wheelArray.slice(0) : s.parseValue(v, that) || [];
setValue();
};
that._processSettings = function () {
s = that.settings;
trigger = that.trigger;
itemHeight = s.height;
lines = s.multiline;
that._isLiquid = (s.layout || (/top|bottom/.test(s.display) && s.wheels.length == 1 ? 'liquid' : '')) === 'liquid';
//that._wheelArray = null;
//that._tempWheelArray = null;
if (lines > 1) {
s.cssClass = (s.cssClass || '') + ' dw-ml';
}
};
// Properties
that._selectedValues = {};
// Constructor
if (!inherit) {
instances[el.id] = that;
that.init(settings);
}
};
// Extend defaults
classes.Scroller.prototype._class = 'scroller';
classes.Scroller.prototype._defaults = $.extend({}, classes.Widget.prototype._defaults, {
// Options
minWidth: 80,
height: 40,
rows: 3,
multiline: 1,
delay: 300,
readonly: false,
showLabel: true,
wheels: [],
mode: 'scroller',
preset: '',
speedUnit: 0.0012,
timeUnit: 0.08,
formatResult: function (d) {
return d.join(' ');
},
parseValue: function (value, inst) {
var val = [],
ret = [],
i = 0,
keys;
if (value !== null && value !== undefined) {
val = (value + '').split(' ');
}
$.each(inst.settings.wheels, function (j, wg) {
$.each(wg, function (k, w) {
keys = w.keys || w.values;
if ($.inArray(val[i], keys) !== -1) {
ret.push(val[i]);
} else {
ret.push(keys[0]);
}
i++;
});
});
return ret;
}
});
})(jQuery, window, document);