| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- /*!
- // iPhone-style Checkboxes jQuery plugin
- // Copyright Thomas Reynolds, licensed GPL & MIT
- */
- ;(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('<div class="' + this.containerClass + '" />');
- this.container = this.$elem.parent();
-
- this.offLabel = $('<label class="'+ this.labelOffClass +'">' +
- '<span>'+ this.uncheckedLabel +'</span>' +
- '</label>').appendTo(this.container);
- this.offSpan = this.offLabel.children('span');
-
- this.onLabel = $('<label class="'+ this.labelOnClass +'">' +
- '<span>'+ this.checkedLabel +'</span>' +
- '</label>').appendTo(this.container);
- this.onSpan = this.onLabel.children('span');
-
- this.handle = $('<div class="' + this.handleClass + '">' +
- '</div>').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();
- } 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'), 0) || 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 + 8 });
- 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;
- new_left = new_left;
-
- if (new_left==26) {
- obj.onLabel.animate({ width: 40 }, obj.duration);
- obj.handle.animate({ left: 25 }, obj.duration);
- } else {
- obj.onLabel.animate({ width: 0 }, obj.duration);
- obj.handle.animate({ left: 1 }, 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 -1 });
- this.onLabel.css({ width: this.rightSide + 14 });
- 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: 200, // Time spent during slide animation
- checkedLabel: 'on', // Text content of "on" state
- uncheckedLabel: 'off', // Text content of "off" state
- resizeHandle: false, // 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');
|