/**
* iPhone-style Checkboxes jQuery plugin
* Copyright Thomas Reynolds, licensed GPL & MIT
*
* @package Customizr
* @since Customizr 1.0
*/
;(function( $, iphoneStyle) {
// Constructor
$[iphoneStyle] = function(elem, options) {
this.$elem = $(elem);
// Import options into instance variables
var obj = this;
$.each(options, function(key, value) {
obj[key] = value;
});
// Initialize the control
this.wrapCheckboxWithDivs();
this.attachEvents();
this.disableTextSelection();
if (this.resizeHandle) { this.optionallyResize( 'handle' ); }
if (this.resizeContainer) { this.optionallyResize( 'container' ); }
this.initialPosition();
};
$.extend( $[iphoneStyle].prototype, {
// Wrap the existing input[type=checkbox] with divs for styling and grab DOM references to the created nodes
wrapCheckboxWithDivs: function() {
this.$elem.wrap( '
' );
this.container = this.$elem.parent();
this.offLabel = $( '' ).appendTo(this.container);
this.offSpan = this.offLabel.children( 'span' );
this.onLabel = $( '' ).appendTo(this.container);
this.onSpan = this.onLabel.children( 'span' );
this.handle = $( '' ).appendTo(this.container);
},
// Disable IE text selection, other browsers are handled in CSS
disableTextSelection: function() {
if (!$.browser.msie) { return; }
// Elements containing text should be unselectable
$.each([this.handle, this.offLabel, this.onLabel, this.container], function(el) {
$(el).attr("unselectable", "on");
});
},
// Automatically resize the handle or container
optionallyResize: function(mode) {
var onLabelWidth = this.onLabel.width(),
offLabelWidth = this.offLabel.width();
if (mode == 'container' ) {
var newWidth = (onLabelWidth > offLabelWidth) ? onLabelWidth : offLabelWidth;
newWidth += this.handle.width() + 15;
} else {
var newWidth = (onLabelWidth < offLabelWidth) ? onLabelWidth : offLabelWidth;
}
this[mode].css({ width: newWidth });
},
attachEvents: function() {
var obj = this;
// A mousedown anywhere in the control will start tracking for dragging
this.container
.bind( 'mousedown touchstart' , function(event) {
event.preventDefault();
if (obj.$elem.is( ':disabled' )) { return; }
var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
$[iphoneStyle].currentlyClicking = obj.handle;
$[iphoneStyle].dragStartPosition = x;
$[iphoneStyle].handleLeftOffset = parseInt(obj.handle.css( 'left' ), 10) || 0;
})
// Utilize event bubbling to handle drag on any element beneath the container
.bind( 'iPhoneDrag' , function(event, x) {
event.preventDefault();
if (obj.$elem.is( ':disabled' )) { return; }
var p = (x + $[iphoneStyle].handleLeftOffset - $[iphoneStyle].dragStartPosition) / obj.rightSide;
if (p < 0) { p = 0; }
if (p > 1) { p = 1; }
obj.handle.css({ left: p * obj.rightSide });
obj.onLabel.css({ width: p * obj.rightSide + 4 });
obj.offSpan.css({ marginRight: -p * obj.rightSide });
obj.onSpan.css({ marginLeft: -(1 - p) * obj.rightSide });
})
// Utilize event bubbling to handle drag end on any element beneath the container
.bind( 'iPhoneDragEnd' , function(event, x) {
if (obj.$elem.is( ':disabled' )) { return; }
if ( $[iphoneStyle].dragging) {
var p = (x - $[iphoneStyle].dragStartPosition) / obj.rightSide;
obj.$elem.attr( 'checked' , (p >= 0.5));
} else {
obj.$elem.attr( 'checked' , !obj.$elem.attr( 'checked' ));
}
$[iphoneStyle].currentlyClicking = null;
$[iphoneStyle].dragging = null;
obj.$elem.change();
});
// Animate when we get a change event
this.$elem.change(function() {
if (obj.$elem.is( ':disabled' )) {
obj.container.addClass(obj.disabledClass);
return false;
} else {
obj.container.removeClass(obj.disabledClass);
}
var new_left = obj.$elem.attr( 'checked' ) ? obj.rightSide : 0;
obj.handle.animate({ left: new_left }, obj.duration);
obj.onLabel.animate({ width: new_left + 4 }, obj.duration);
obj.offSpan.animate({ marginRight: -new_left }, obj.duration);
obj.onSpan.animate({ marginLeft: new_left - obj.rightSide }, obj.duration);
});
},
// Setup the control's inital position
initialPosition: function() {
this.offLabel.css({ width: this.container.width() - 5 });
var offset = ( $.browser.msie && $.browser.version < 7) ? 3 : 6;
this.rightSide = this.container.width() - this.handle.width() - offset;
if (this.$elem.is( ':checked' )) {
this.handle.css({ left: this.rightSide });
this.onLabel.css({ width: this.rightSide + 4 });
this.offSpan.css({ marginRight: -this.rightSide });
} else {
this.onLabel.css({ width: 0 });
this.onSpan.css({ marginLeft: -this.rightSide });
}
if (this.$elem.is( ':disabled' )) {
this.container.addClass(this.disabledClass);
}
}
});
// jQuery-specific code
$.fn[iphoneStyle] = function(options) {
var checkboxes = this.filter( ':checkbox' );
// Fail early if we don't have any checkboxes passed in
if (!checkboxes.length) { return this; }
// Merge options passed in with global defaults
var opt = $.extend({}, $[iphoneStyle].defaults, options);
checkboxes.each(function() {
$(this).data(iphoneStyle, new $[iphoneStyle](this, opt));
});
if (!$[iphoneStyle].initComplete) {
// As the mouse moves on the page, animate if we are in a drag state
$(document)
.bind( 'mousemove touchmove' , function(event) {
if (!$[iphoneStyle].currentlyClicking) { return; }
event.preventDefault();
var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
if (!$[iphoneStyle].dragging &&
(Math.abs( $[iphoneStyle].dragStartPosition - x) > opt.dragThreshold)) {
$[iphoneStyle].dragging = true;
}
$(event.target).trigger( 'iPhoneDrag' , [x]);
})
// When the mouse comes up, leave drag state
.bind( 'mouseup touchend' , function(event) {
if (!$[iphoneStyle].currentlyClicking) { return; }
event.preventDefault();
var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
$( $[iphoneStyle].currentlyClicking).trigger( 'iPhoneDragEnd' , [x]);
});
$[iphoneStyle].initComplete = true;
}
return this;
}; // End of $.fn[iphoneStyle]
$[iphoneStyle].defaults = {
duration: 150, // Time spent during slide animation
checkedLabel: 'ON' , // Text content of "on" state
uncheckedLabel: 'OFF' , // Text content of "off" state
resizeHandle: true, // Automatically resize the handle to cover either label
resizeContainer: true, // Automatically resize the widget to contain the labels
disabledClass: 'iPhoneCheckDisabled' ,
containerClass: 'iPhoneCheckContainer' ,
labelOnClass: 'iPhoneCheckLabelOn' ,
labelOffClass: 'iPhoneCheckLabelOff' ,
handleClass: 'iPhoneCheckHandle' ,
handleCenterClass: 'iPhoneCheckHandleCenter' ,
handleRightClass: 'iPhoneCheckHandleRight' ,
dragThreshold: 5 // Pixels that must be dragged for a click to be ignored
};
})(jQuery, 'iphoneStyle' );
jQuery(function(){
jQuery( '.iphonecheck' ).iphoneStyle({ checkedLabel: 'Yes' , uncheckedLabel: 'No' });
});