123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- /*
- * jQuery switchbutton
- *
- * Based on work by tdreyno on iphone-style-checkboxes for events management
- * (https://github.com/tdreyno/iphone-style-checkboxes)
- *
- * Copyright 2011, L.STEVENIN
- * Released under the MIT license.
- *
- * Depends:
- * jquery.ui.widget.js (jQuery UI Widget Factory - http://wiki.jqueryui.com/w/page/12138135/Widget%20factory)
- * jquery.tmpl.js (jQuery Templates - http://api.jquery.com/category/plugins/templates/)
- */
- (function($, switchbutton){
-
- $.widget('switchbutton.switchbutton', {
-
- options: {
- classes: '',
- duration: 200,
- dragThreshold: 5,
- autoResize: true,
- labels: true,
- checkedLabel: 'ON',
- uncheckedLabel: 'OFF',
- disabledClass: 'ui-switchbutton-disabled ui-state-disabled',
- template: '<div class="ui-switchbutton ui-switchbutton-default ${classes} {{if !labels}}ui-switchbutton-no-labels{{/if}}" tabindex=0>' +
- '<label class="ui-switchbutton-disabled">' +
- '<span>{{if labels}}${uncheckedLabel}{{/if}}</span>' +
- '</label>' +
- '<label class="ui-switchbutton-enabled">' +
- '<span>{{if labels}}${checkedLabel}{{/if}}</span>' +
- '</label>' +
- '<div class="ui-switchbutton-handle"></div>' +
- '</div>'
- },
-
- _create: function() {
- if(!this.element.is(':checkbox')) {
- return;
- }
-
- this._wrapCheckboxInContainer();
- this._attachEvents();
- this._globalEvents();
- this._disableTextSelection();
-
- if(this.element.prop('checked')) {
- this.$container.toggleClass('ui-state-active', this.element.prop('checked'));
- }
-
- if(this.options.autoResize) {
- this._autoResize();
- }
-
- this._initialPosition();
- },
-
- _wrapCheckboxInContainer: function() {
- this.$container = $.tmpl(this.options.template, this.options);
- this.element.after(this.$container);
- this.element.remove();
- this.$container.append(this.element);
- this.$disabledLabel = this.$container.children('.ui-switchbutton-disabled');
- this.$disabledSpan = this.$disabledLabel.children('span');
- this.$enabledLabel = this.$container.children('.ui-switchbutton-enabled');
- this.$enabledSpan = this.$enabledLabel.children('span');
- this.$handle = this.$container.children('.ui-switchbutton-handle');
- },
-
- _attachEvents: function() {
- var obj = this;
- this.$container
-
- // Listen for keyboard events such as <space>, <left> and <right>
- .bind('keydown', function(event){
-
- //Ignore if a key combo was used
- if(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey){
- return;
- }
-
- //Catch <space>, <left>, and <right>
- if(event.keyCode == 32 || event.keyCode == 37 || event.keyCode == 39) event.preventDefault();
-
- //Ignore if the element is disabled
- if(obj.element.prop('disabled')) {
- return;
- }
-
- //Determine if we're supposed to toggle
- var checked = obj.element.prop('checked')
- if(event.keyCode == 32 || (event.keyCode == 37 && checked) || (event.keyCode == 39 && !checked)) {
- //Perform the toggle
- var willChangeEvent = jQuery.Event('willChange');
- obj.element.trigger(willChangeEvent);
- if(willChangeEvent.isDefaultPrevented()) return;
-
- checked = !checked;
-
- obj.element.prop('checked', checked);
- obj.$container.toggleClass('ui-state-active', checked);
- obj.element.change();
- obj.element.trigger('didChange');
-
- }
-
- })
-
- // A mousedown anywhere in the control will start tracking for dragging
- .bind('mousedown touchstart', function(event) {
- event.preventDefault();
- if(obj.element.prop('disabled')) { return; }
-
- $(this).focus();
- var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
- $[switchbutton].currentlyClicking = obj.$handle;
- $[switchbutton].dragStartPosition = x;
- $[switchbutton].handleLeftOffset = parseInt(obj.$handle.css('left'), 10) || 0;
- $[switchbutton].dragStartedOn = obj.element;
- })
- // Utilize event bubbling to handle drag on any element beneath the container
- .bind('iPhoneDrag', function(event, x) {
- event.preventDefault();
- if(obj.element.prop('disabled')) { return; }
- if(obj.element != $[switchbutton].dragStartedOn) { return; }
- var p = (x + $[switchbutton].handleLeftOffset - $[switchbutton].dragStartPosition) / obj.rightSide;
- if(p < 0) { p = 0; }
- if(p > 1) { p = 1; }
- obj.$handle.css({ 'left': p * obj.rightSide });
- obj.$enabledLabel.css({ 'width': p * obj.rightSide });
- obj.$disabledSpan.css({ 'margin-right': -p * obj.rightSide });
- obj.$enabledSpan.css({ 'margin-left': -(1 - p) * obj.rightSide });
- })
- // Utilize event bubbling to handle drag end on any element beneath the container
- .bind('iPhoneDragEnd', function(event, x) {
- if(obj.element.prop('disabled')) { return; }
- var willChangeEvent = jQuery.Event('willChange');
- obj.element.trigger(willChangeEvent);
- if(willChangeEvent.isDefaultPrevented()) {
- checked = obj.element.prop('checked');
- }
- else {
- var checked;
- if($[switchbutton].dragging) {
- var p = (x - $[switchbutton].dragStartPosition) / obj.rightSide;
- checked = (p < 0) ? Math.abs(p) < 0.5 : p >= 0.5;
- }
- else {
- checked = !obj.element.prop('checked');
- }
- }
- $[switchbutton].currentlyClicking = null;
- $[switchbutton].dragging = null;
- obj.element.prop('checked', checked);
- obj.$container.toggleClass('ui-state-active', checked);
- obj.element.change();
- obj.element.trigger('didChange');
- });
- // Animate when we get a change event
- this.element.change(function() {
- obj.refresh();
- var new_left = obj.element.prop('checked') ? obj.rightSide : 0;
-
- obj.$handle.animate({ 'left': new_left }, obj.options.duration);
- obj.$enabledLabel.animate({ 'width': new_left }, obj.options.duration);
- obj.$disabledSpan.animate({ 'margin-right': -new_left }, obj.options.duration);
- obj.$enabledSpan.animate({ 'margin-left': new_left - obj.rightSide }, obj.options.duration);
- });
- },
-
- _globalEvents: function() {
- if($[switchbutton].initComplete) {
- return;
- }
- var opt = this.options;
- $(document)
- // As the mouse moves on the page, animate if we are in a drag state
- .bind('mousemove touchmove', function(event) {
- if(!$[switchbutton].currentlyClicking) { return; }
- event.preventDefault();
- var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
- if(!$[switchbutton].dragging && (Math.abs($[switchbutton].dragStartPosition - x) > opt.dragThreshold)) {
- $[switchbutton].dragging = true;
- }
- $(event.target).trigger('iPhoneDrag', [x]);
- })
-
- // When the mouse comes up, leave drag state
- .bind('mouseup touchend', function(event) {
- if(!$[switchbutton].currentlyClicking) { return; }
- event.preventDefault();
-
- var x = event.pageX || event.originalEvent.changedTouches[0].pageX;
- $($[switchbutton].currentlyClicking).trigger('iPhoneDragEnd', [x]);
- });
- },
-
- _disableTextSelection: function() {
- // Elements containing text should be unselectable
- $([this.$handle, this.$disabledLabel, this.$enabledLabel, this.$container]).attr('unselectable', 'on');
- },
-
- _autoResize: function() {
- var onLabelWidth = this.$enabledLabel.width(),
- offLabelWidth = this.$disabledLabel.width(),
- spanPadding = this.$disabledSpan.innerWidth() - this.$disabledSpan.width()
- handleMargins = this.$handle.outerWidth() - this.$handle.innerWidth();
-
- var containerWidth = handleWidth = (onLabelWidth > offLabelWidth) ? onLabelWidth : offLabelWidth;
-
- this.$handle.css({ 'width': handleWidth });
- handleWidth = this.$handle.width();
-
- containerWidth += handleWidth + 6;
- spanWidth = containerWidth - handleWidth - spanPadding - handleMargins;
-
- this.$container.css({ 'width': containerWidth });
- this.$container.find('span').width(spanWidth);
- },
-
- _initialPosition: function() {
- this.$disabledLabel.css({ width: this.$container.width() - 5 });
- this.rightSide = this.$container.width() - this.$handle.outerWidth();
-
- if(this.element.prop('checked')) {
- this.$handle.css({ 'left': this.rightSide });
- this.$enabledLabel.css({ 'width': this.rightSide });
- this.$disabledSpan.css({ 'margin-right': -this.rightSide });
- }
- else {
- this.$enabledLabel.css({ 'width': 0 });
- this.$enabledSpan.css({ 'margin-left': -this.rightSide });
- }
-
- this.refresh();
- },
-
- enable: function() {
- this.element.prop('disabled', false);
- this.refresh();
- return this._setOption('disabled', false);
- },
-
- disable: function() {
- this.element.prop('disabled', true);
- this.refresh();
- return this._setOption('disabled', true);
- },
-
- widget: function() {
- return this.$container;
- },
-
- refresh: function() {
- if(this.element.prop('disabled')) {
- this.$container.addClass(this.options.disabledClass);
- return false;
- }
- else {
- this.$container.removeClass(this.options.disabledClass);
- }
- }
-
- });
-
- })(jQuery, 'switchbutton');
|