jquery-mobile/js/jquery.mobile.forms.slider.js
Kin Blas 48389d1ee1 Fixed bad variable name in teardown method of jquery.mobile.vmouse.js. It was causing an error that made one() bindings fail to fire off their handlers.
Modified select and slider code to use virtual events.

Modified tap and taphold code to make use of the virtual events.
2011-02-18 17:39:04 -08:00

274 lines
7.8 KiB
JavaScript

/*
* jQuery Mobile Framework : "slider" plugin
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function($, undefined ) {
$.widget( "mobile.slider", $.mobile.widget, {
options: {
theme: null,
trackTheme: null,
disabled: false
},
_create: function(){
var self = this,
control = this.element,
parentTheme = control.parents('[class*=ui-bar-],[class*=ui-body-]').eq(0),
parentTheme = parentTheme.length ? parentTheme.attr('class').match(/ui-(bar|body)-([a-z])/)[2] : 'c',
theme = this.options.theme ? this.options.theme : parentTheme,
trackTheme = this.options.trackTheme ? this.options.trackTheme : parentTheme,
cType = control[0].nodeName.toLowerCase(),
selectClass = (cType == 'select') ? 'ui-slider-switch' : '',
controlID = control.attr('id'),
labelID = controlID + '-label',
label = $('[for='+ controlID +']').attr('id',labelID),
val = function(){
return (cType == 'input') ? parseFloat(control.val()) : control[0].selectedIndex;
},
min = (cType == 'input') ? parseFloat(control.attr('min')) : 0,
max = (cType == 'input') ? parseFloat(control.attr('max')) : control.find('option').length-1,
step = window.parseFloat(control.attr('step') || 1),
slider = $('<div class="ui-slider '+ selectClass +' ui-btn-down-'+ trackTheme+' ui-btn-corner-all" role="application"></div>'),
handle = $('<a href="#" class="ui-slider-handle"></a>')
.appendTo(slider)
.buttonMarkup({corners: true, theme: theme, shadow: true})
.attr({
'role': 'slider',
'aria-valuemin': min,
'aria-valuemax': max,
'aria-valuenow': val(),
'aria-valuetext': val(),
'title': val(),
'aria-labelledby': labelID
});
$.extend(this, {
slider: slider,
handle: handle,
dragging: false,
beforeStart: null
});
if(cType == 'select'){
slider.wrapInner('<div class="ui-slider-inneroffset"></div>');
var options = control.find('option');
control.find('option').each(function(i){
var side = (i==0) ?'b':'a',
corners = (i==0) ? 'right' :'left',
theme = (i==0) ? ' ui-btn-down-' + trackTheme :' ui-btn-active';
$('<div class="ui-slider-labelbg ui-slider-labelbg-'+ side + theme +' ui-btn-corner-'+ corners+'"></div>').prependTo(slider);
$('<span class="ui-slider-label ui-slider-label-'+ side + theme +' ui-btn-corner-'+ corners+'" role="img">'+$(this).text()+'</span>').prependTo(handle);
});
}
label.addClass('ui-slider');
// monitor the input for updated values
control
.addClass((cType == 'input') ? 'ui-slider-input' : 'ui-slider-switch')
.change(function(){
self.refresh( val(), true );
})
.keyup(function(){ // necessary?
self.refresh( val(), true, true );
})
.blur(function(){
self.refresh( val(), true );
});
// prevent screen drag when slider activated
$(document).bind( "vmousemove", function(event){
if ( self.dragging ) {
self.refresh( event );
return false;
}
});
slider
.bind( "vmousedown", function(event){
self.dragging = true;
if ( cType === "select" ) {
self.beforeStart = control[0].selectedIndex;
}
self.refresh( event );
return false;
});
slider
.add(document)
.bind( "vmouseup", function(){
if ( self.dragging ) {
self.dragging = false;
if ( cType === "select" ) {
if ( self.beforeStart === control[0].selectedIndex ) {
//tap occurred, but value didn't change. flip it!
self.refresh( self.beforeStart === 0 ? 1 : 0 );
}
var curval = val();
var snapped = Math.round( curval / (max - min) * 100 );
handle
.addClass("ui-slider-handle-snapping")
.css("left", snapped + "%")
.animationComplete(function(){
handle.removeClass("ui-slider-handle-snapping");
});
}
return false;
}
});
slider.insertAfter(control);
// NOTE force focus on handle
this.handle
.bind( "vmousedown", function(){
$(this).focus();
});
this.handle
.bind( "keydown", function( event ) {
var index = val();
if ( self.options.disabled ) {
return;
}
// In all cases prevent the default and mark the handle as active
switch ( event.keyCode ) {
case $.mobile.keyCode.HOME:
case $.mobile.keyCode.END:
case $.mobile.keyCode.PAGE_UP:
case $.mobile.keyCode.PAGE_DOWN:
case $.mobile.keyCode.UP:
case $.mobile.keyCode.RIGHT:
case $.mobile.keyCode.DOWN:
case $.mobile.keyCode.LEFT:
event.preventDefault();
if ( !self._keySliding ) {
self._keySliding = true;
$( this ).addClass( "ui-state-active" );
}
break;
}
// move the slider according to the keypress
switch ( event.keyCode ) {
case $.mobile.keyCode.HOME:
self.refresh(min);
break;
case $.mobile.keyCode.END:
self.refresh(max);
break;
case $.mobile.keyCode.PAGE_UP:
case $.mobile.keyCode.UP:
case $.mobile.keyCode.RIGHT:
self.refresh(index + step);
break;
case $.mobile.keyCode.PAGE_DOWN:
case $.mobile.keyCode.DOWN:
case $.mobile.keyCode.LEFT:
self.refresh(index - step);
break;
}
}) // remove active mark
.keyup(function( event ) {
if ( self._keySliding ) {
self._keySliding = false;
$( this ).removeClass( "ui-state-active" );
}
});
this.refresh();
},
refresh: function(val, isfromControl, preventInputUpdate){
if ( this.options.disabled ) { return; }
var control = this.element, percent,
cType = control[0].nodeName.toLowerCase(),
min = (cType === "input") ? parseFloat(control.attr("min")) : 0,
max = (cType === "input") ? parseFloat(control.attr("max")) : control.find("option").length - 1;
if ( typeof val === "object" ) {
var data = val,
// a slight tolerance helped get to the ends of the slider
tol = 8;
if ( !this.dragging
|| data.pageX < this.slider.offset().left - tol
|| data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
return;
}
percent = Math.round( ((data.pageX - this.slider.offset().left) / this.slider.width() ) * 100 );
} else {
if ( val == null ) {
val = (cType === "input") ? parseFloat(control.val()) : control[0].selectedIndex;
}
percent = (parseFloat(val) - min) / (max - min) * 100;
}
if ( isNaN(percent) ) { return; }
if ( percent < 0 ) { percent = 0; }
if ( percent > 100 ) { percent = 100; }
var newval = Math.round( (percent / 100) * (max - min) ) + min;
if ( newval < min ) { newval = min; }
if ( newval > max ) { newval = max; }
//flip the stack of the bg colors
if ( percent > 60 && cType === "select" ) {
}
this.handle.css("left", percent + "%");
this.handle.attr({
"aria-valuenow": (cType === "input") ? newval : control.find("option").eq(newval).attr("value"),
"aria-valuetext": (cType === "input") ? newval : control.find("option").eq(newval).text(),
title: newval
});
// add/remove classes for flip toggle switch
if ( cType === "select" ) {
if ( newval === 0 ) {
this.slider.addClass("ui-slider-switch-a")
.removeClass("ui-slider-switch-b");
} else {
this.slider.addClass("ui-slider-switch-b")
.removeClass("ui-slider-switch-a");
}
}
if(!preventInputUpdate){
// update control's value
if ( cType === "input" ) {
control.val(newval);
} else {
control[ 0 ].selectedIndex = newval;
}
if (!isfromControl) { control.trigger("change"); }
}
},
enable: function(){
this.element.attr("disabled", false);
this.slider.removeClass("ui-disabled").attr("aria-disabled", false);
return this._setOption("disabled", false);
},
disable: function(){
this.element.attr("disabled", true);
this.slider.addClass("ui-disabled").attr("aria-disabled", true);
return this._setOption("disabled", true);
}
});
})( jQuery );