text: //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
  //>>description: Slider form widget
  //>>label: Slider
  //>>group: Forms
  //>>css.structure: ../css/structure/jquery.mobile.forms.slider.css
  //>>css.theme: ../css/themes/default/jquery.mobile.theme.css
  
  define( [ "jquery", "../../jquery.mobile.core", "../../jquery.mobile.widget", "./textinput", "../../jquery.mobile.buttonMarkup" ], function( $ ) {
  //>>excludeEnd("jqmBuildExclude");
  (function( $, undefined ) {
  
  $.widget( "mobile.slider", $.mobile.widget, {
  	widgetEventPrefix: "slide",
  
  	options: {
  		theme: null,
  		trackTheme: null,
  		disabled: false,
  		initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
  		mini: false
  	},
  
  	_create: function() {
  
  		// TODO: Each of these should have comments explain what they're for
  		var self = this,
  
  			control = this.element,
  
  			parentTheme = $.mobile.getInheritedTheme( control, "c" ),
  
  			theme = this.options.theme || parentTheme,
  
  			trackTheme = this.options.trackTheme || parentTheme,
  
  			cType = control[ 0 ].nodeName.toLowerCase(),
  
  			selectClass = ( cType === "select" ) ? "ui-slider-switch" : "",
  
  			controlID = control.attr( "id" ),
  
  			$label = $( "[for='" + controlID + "']" ),
  
  			labelID = $label.attr( "id" ) || controlID + "-label",
  
  			label = $label.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 ),
  
  			inlineClass = ( this.options.inline || control.jqmData( "inline" ) === true ) ? " ui-slider-inline" : "",
  
  			miniClass = ( this.options.mini || control.jqmData( "mini" ) ) ? " ui-slider-mini" : "",
  
  
  			domHandle = document.createElement( 'a' ),
  			handle = $( domHandle ),
  			domSlider = document.createElement( 'div' ),
  			slider = $( domSlider ),
  
  			valuebg = control.jqmData( "highlight" ) && cType !== "select" ? (function() {
  				var bg = document.createElement('div');
  				bg.className = 'ui-slider-bg ' + $.mobile.activeBtnClass + ' ui-btn-corner-all';
  				return $( bg ).prependTo( slider );
  			})() : false,
  
  			options;
  
  		this._type = cType;
  
  		domHandle.setAttribute( 'href', "#" );
  		domSlider.setAttribute('role','application');
  		domSlider.className = ['ui-slider ',selectClass," ui-btn-down-",trackTheme,' ui-btn-corner-all', inlineClass, miniClass].join( "" );
  		domHandle.className = 'ui-slider-handle';
  		domSlider.appendChild( domHandle );
  
  		handle.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,
  			valuebg: valuebg,
  			dragging: false,
  			beforeStart: null,
  			userModified: false,
  			mouseMoved: false
  		});
  
  		if ( cType === "select" ) {
  			var wrapper = document.createElement('div');
  			wrapper.className = 'ui-slider-inneroffset';
  
  			for ( var j = 0,length = domSlider.childNodes.length;j < length;j++ ) {
  				wrapper.appendChild( domSlider.childNodes[j] );
  			}
  
  			domSlider.appendChild( wrapper );
  
  			// slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
  
  			// make the handle move with a smooth transition
  			handle.addClass( "ui-slider-handle-snapping" );
  
  			options = control.find( "option" );
  
  			for ( var i = 0, optionsCount = options.length; i < optionsCount; i++ ) {
  				var side = !i ? "b" : "a",
  					sliderTheme = !i ? " ui-btn-down-" + trackTheme : ( " " + $.mobile.activeBtnClass ),
  					sliderLabel = document.createElement( 'div' ),
  					sliderImg = document.createElement( 'span' );
  
  				sliderImg.className = ['ui-slider-label ui-slider-label-',side,sliderTheme," ui-btn-corner-all"].join( "" );
  				sliderImg.setAttribute('role','img');
  				sliderImg.appendChild( document.createTextNode( options[i].innerHTML ) );
  				$(sliderImg).prependTo( slider );
  			}
  
  			self._labels = $( ".ui-slider-label", slider );
  
  		}
  
  		label.addClass( "ui-slider" );
  
  		// monitor the input for updated values
  		control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
  			.change(function() {
  				// if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
  				if ( !self.mouseMoved ) {
  					self.refresh( val(), true );
  				}
  			})
  			.keyup(function() { // necessary?
  				self.refresh( val(), true, true );
  			})
  			.blur(function() {
  				self.refresh( val(), true );
  			});
  
  		this._preventDocumentDrag = function( event ) {
  			// NOTE: we don't do this in refresh because we still want to
  			//       support programmatic alteration of disabled inputs
  			if ( self.dragging && !self.options.disabled ) {
  
  				// self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
  				self.mouseMoved = true;
  
  				if ( cType === "select" ) {
  					// make the handle move in sync with the mouse
  					handle.removeClass( "ui-slider-handle-snapping" );
  				}
  
  				self.refresh( event );
  
  				// only after refresh() you can calculate self.userModified
  				self.userModified = self.beforeStart !== control[0].selectedIndex;
  				return false;
  			}
  		}
  
  		this._on( $( document ), { "vmousemove": this._preventDocumentDrag });
  
  		// it appears the clicking the up and down buttons in chrome on
  		// range/number inputs doesn't trigger a change until the field is
  		// blurred. Here we check thif the value has changed and refresh
  		control.bind( "vmouseup", $.proxy( self._checkedRefresh, self));
  
  		slider.bind( "vmousedown", function( event ) {
  			// NOTE: we don't do this in refresh because we still want to
  			//       support programmatic alteration of disabled inputs
  			if ( self.options.disabled ) {
  				return false;
  			}
  
  			self.dragging = true;
  			self.userModified = false;
  			self.mouseMoved = false;
  
  			if ( cType === "select" ) {
  				self.beforeStart = control[0].selectedIndex;
  			}
  
  			self.refresh( event );
  			self._trigger( "start" );
  			return false;
  		})
  		.bind( "vclick", false );
  
  		this._sliderMouseUp = function() {
  			if ( self.dragging ) {
  				self.dragging = false;
  
  				if ( cType === "select") {
  					// make the handle move with a smooth transition
  					handle.addClass( "ui-slider-handle-snapping" );
  
  					if ( self.mouseMoved ) {
  						// this is a drag, change the value only if user dragged enough
  						if ( self.userModified ) {
  						    self.refresh( self.beforeStart === 0 ? 1 : 0 );
  						}
  						else {
  						    self.refresh( self.beforeStart );
  						}
  					}
  					else {
  						// this is just a click, change the value
  						self.refresh( self.beforeStart === 0 ? 1 : 0 );
  					}
  				}
  
  				self.mouseMoved = false;
  				self._trigger( "stop" );
  				return false;
  			}
  		};
  
  		this._on( slider.add( document ), { "vmouseup": this._sliderMouseUp });
  		slider.insertAfter( control );
  
  		// Only add focus class to toggle switch, sliders get it automatically from ui-btn
  		if ( cType === 'select' ) {
  			this.handle.bind({
  				focus: function() {
  					slider.addClass( $.mobile.focusClass );
  				},
  
  				blur: function() {
  					slider.removeClass( $.mobile.focusClass );
  				}
  			});
  		}
  
  		this.handle.bind({
  			// NOTE force focus on handle
  			vmousedown: function() {
  				$( this ).focus();
  			},
  
  			vclick: false,
  
  			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( undefined, undefined, true );
  	},
  
  	_checkedRefresh: function() {
  		if( this.value != this._value() ){
  			this.refresh( this._value() );
  		}
  	},
  
  	_value: function() {
  		return  this._type === "input" ?
  			parseFloat( this.element.val() ) : this.element[0].selectedIndex;
  	},
  
  	refresh: function( val, isfromControl, preventInputUpdate ) {
  
  		// NOTE: we don't return here because we want to support programmatic
  		//       alteration of the input value, which should still update the slider
  		if ( this.options.disabled || this.element.attr('disabled')) {
  			this.disable();
  		}
  
  		// set the stored value for comparison later
  		this.value = this._value();
  
  		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,
  			step = ( cType === "input" && parseFloat( control.attr( "step" ) ) > 0 ) ? parseFloat( control.attr( "step" ) ) : 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() || 0 ) : 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 = ( percent / 100 ) * ( max - min ) + min;
  
  		//from jQuery UI slider, the following source will round to the nearest step
  		var valModStep = ( newval - min ) % step;
  		var alignValue = newval - valModStep;
  
  		if ( Math.abs( valModStep ) * 2 >= step ) {
  			alignValue += ( valModStep > 0 ) ? step : ( -step );
  		}
  		// Since JavaScript has problems with large floats, round
  		// the final value to 5 digits after the decimal point (see jQueryUI: #4124)
  		newval = parseFloat( alignValue.toFixed(5) );
  
  		if ( newval < min ) {
  			newval = min;
  		}
  
  		if ( newval > max ) {
  			newval = max;
  		}
  
  		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 ).getEncodedText(),
  				title: cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText()
  			});
  
  		if ( this.valuebg ) {
  			this.valuebg.css( "width", percent + "%" );
  		}
  
  		// drag the label widths
  		if ( this._labels ) {
  			var handlePercent = this.handle.width() / this.slider.width() * 100,
  				aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100,
  				bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 );
  
  			this._labels.each(function() {
  				var ab = $( this ).is( ".ui-slider-label-a" );
  				$( this ).width( ( ab ? aPercent : bPercent  ) + "%" );
  			});
  		}
  
  		if ( !preventInputUpdate ) {
  			var valueChanged = false;
  
  			// update control"s value
  			if ( cType === "input" ) {
  				valueChanged = control.val() !== newval;
  				control.val( newval );
  			} else {
  				valueChanged = control[ 0 ].selectedIndex !== newval;
  				control[ 0 ].selectedIndex = newval;
  			}
  			if ( !isfromControl && valueChanged ) {
  				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 );
  	}
  
  });
  
  //auto self-init widgets
  $( document ).bind( "pagecreate create", function( e ) {
  	$.mobile.slider.prototype.enhanceWithin( e.target, true );
  });
  
  })( jQuery );
  //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
  });
  //>>excludeEnd("jqmBuildExclude");
  