blob: a8f00825f4df9fc26446668b0fcb0e2734801b7c [file] [log] [blame]
/*
* "checkboxradio" plugin
*/
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Consistent styling for checkboxes/radio buttons.
//>>label: Checkboxes & Radio Buttons
//>>group: Forms
//>>css.structure: ../css/structure/jquery.mobile.forms.checkboxradio.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
define( [ "jquery", "../../jquery.mobile.core", "../../jquery.mobile.widget", "../../jquery.mobile.buttonMarkup" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {
$.widget( "mobile.checkboxradio", $.mobile.widget, {
options: {
theme: null,
initSelector: "input[type='checkbox'],input[type='radio']"
},
_create: function() {
var self = this,
input = this.element,
inheritAttr = function( input, dataAttr ) {
return input.jqmData( dataAttr ) || input.closest( "form, fieldset" ).jqmData( dataAttr );
},
// NOTE: Windows Phone could not find the label through a selector
// filter works though.
parentLabel = $( input ).closest( "label" ),
label = parentLabel.length ? parentLabel : ( input[0].id ? $( input ).closest( "form, fieldset, :jqmData(role='page'), :jqmData(role='dialog')" ).find( "label" ).filter( "[for='" + input[0].id + "']" ) : [ ] ),
inputtype = input[0].type,
mini = inheritAttr( input, "mini" ),
checkedState = inputtype + "-on",
uncheckedState = inputtype + "-off",
icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
iconpos = inheritAttr( input, "iconpos" ),
activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
checkedClass = "ui-" + checkedState + activeBtn,
uncheckedClass = "ui-" + uncheckedState,
checkedicon = "ui-icon-" + checkedState,
uncheckedicon = "ui-icon-" + uncheckedState,
ariaCheckedAttr = "";
if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
return;
}
ariaCheckedAttr = ( inputtype === "checkbox" ) ? "aria-checked" : "aria-selected";
// Support fake label
if ( label.length == 0 ) {
label = $( "<label for='" + input[ 0 ].id +
"'></label>" );
}
// Expose for other methods
$.extend( this, {
label: label,
inputtype: inputtype,
checkedClass: checkedClass,
uncheckedClass: uncheckedClass,
checkedicon: checkedicon,
uncheckedicon: uncheckedicon,
ariaCheckedAttr : ariaCheckedAttr
});
// If there's no selected theme check the data attr
if ( !this.options.theme ) {
this.options.theme = $.mobile.getInheritedTheme( this.element, "c" );
}
label.buttonMarkup({
theme: this.options.theme,
icon: icon,
shadow: false,
mini: mini,
iconpos: iconpos
});
// Wrap the input + label in a div
var wrapper = document.createElement('div');
wrapper.className = 'ui-' + inputtype;
wrapper.setAttribute( "role", inputtype );
if ( input.hasClass( "favorite" ) ) {
wrapper.className += ' favorite';
}
input.add( label ).wrapAll( wrapper );
label.bind({
vmouseover: function( event ) {
if ( $( this ).parent().is( ".ui-disabled" ) ) {
event.stopPropagation();
}
},
vclick: function( event ) {
if ( input.is( ":disabled" ) ) {
event.preventDefault();
return;
}
self._cacheVals();
input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
// trigger click handler's bound directly to the input as a substitute for
// how label clicks behave normally in the browsers
// TODO: it would be nice to let the browser's handle the clicks and pass them
// through to the associate input. we can swallow that click at the parent
// wrapper element level
input.triggerHandler( 'click' );
// Input set for common radio buttons will contain all the radio
// buttons, but will not for checkboxes. clearing the checked status
// of other radios ensures the active button state is applied properly
self._getInputSet().not( input ).prop( "checked", false );
self._updateAll();
return false;
}
});
input
.bind({
vmousedown: function() {
self._cacheVals();
},
vclick: function() {
var $this = $( this );
// Adds checked attribute to checked input when keyboard is used
if ( $this.is( ":checked" ) ) {
$this.prop( "checked", true);
self._getInputSet().not( $this ).prop( "checked", false );
} else {
$this.prop( "checked", false );
}
self._updateAll();
},
focus: function() {
label.addClass( $.mobile.focusClass );
},
blur: function() {
label.removeClass( $.mobile.focusClass );
}
});
this.refresh();
},
_cacheVals: function() {
this._getInputSet().each(function() {
$( this ).jqmData( "cacheVal", this.checked );
});
},
//returns either a set of radios with the same name attribute, or a single checkbox
_getInputSet: function() {
if ( this.inputtype === "checkbox" ) {
return this.element;
}
return this.element.closest( "form, fieldset, :jqmData(role='page'), :jqmData(role='dialog')" )
.find( "input[name='" + this.element[0].name + "'][type='" + this.inputtype + "']" );
},
_updateAll: function() {
var self = this;
this._getInputSet().each(function() {
var $this = $( this );
if ( this.checked || self.inputtype === "checkbox" ) {
$this.trigger( "change" );
}
$this.focus();
})
.checkboxradio( "refresh" );
},
refresh: function() {
var input = this.element[0],
label = this.label,
wrapper = input.parentNode,
icon = label.find( ".ui-icon" );
if ( input.checked ) {
label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
wrapper.setAttribute( this.ariaCheckedAttr, true );
} else {
label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
wrapper.setAttribute( this.ariaCheckedAttr, false );
}
if ( input.disabled ) {
this.disable();
} else {
this.enable();
}
},
disable: function() {
this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
},
enable: function() {
this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
}
});
//auto self-init widgets
$.mobile.$document.bind( "pagecreate create", function( e ) {
$.mobile.checkboxradio.prototype.enhanceWithin( e.target, true );
});
})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");