195 lines
5.4 KiB
JavaScript
195 lines
5.4 KiB
JavaScript
/*!
|
|
* JavaScript Custom Forms : Radio Module
|
|
*
|
|
* Copyright 2014-2015 PSD2HTML - http://psd2html.com/jcf
|
|
* Released under the MIT license (LICENSE.txt)
|
|
*
|
|
* Version: 1.2.1
|
|
*/
|
|
|
|
(function(jcf) {
|
|
|
|
jcf.addModule(function($) {
|
|
'use strict';
|
|
|
|
return {
|
|
name: 'Radio',
|
|
selector: 'input[type="radio"]',
|
|
options: {
|
|
wrapNative: true,
|
|
checkedClass: 'jcf-checked',
|
|
uncheckedClass: 'jcf-unchecked',
|
|
labelActiveClass: 'jcf-label-active',
|
|
fakeStructure: '<span class="jcf-radio"><span></span></span>'
|
|
},
|
|
matchElement: function(element) {
|
|
return element.is(':radio');
|
|
},
|
|
init: function() {
|
|
this.initStructure();
|
|
this.attachEvents();
|
|
this.refresh();
|
|
},
|
|
initStructure: function() {
|
|
// prepare structure
|
|
this.doc = $(document);
|
|
this.realElement = $(this.options.element);
|
|
this.fakeElement = $(this.options.fakeStructure).insertAfter(this.realElement);
|
|
this.labelElement = this.getLabelFor();
|
|
|
|
if (this.options.wrapNative) {
|
|
// wrap native radio inside fake block
|
|
this.realElement.prependTo(this.fakeElement).css({
|
|
position: 'absolute',
|
|
opacity: 0
|
|
});
|
|
} else {
|
|
// just hide native radio
|
|
this.realElement.addClass(this.options.hiddenClass);
|
|
}
|
|
},
|
|
attachEvents: function() {
|
|
// add event handlers
|
|
this.realElement.on({
|
|
focus: this.onFocus,
|
|
click: this.onRealClick
|
|
});
|
|
this.fakeElement.on('click', this.onFakeClick);
|
|
this.fakeElement.on('jcf-pointerdown', this.onPress);
|
|
},
|
|
onRealClick: function(e) {
|
|
// redraw current radio and its group (setTimeout handles click that might be prevented)
|
|
var self = this;
|
|
this.savedEventObject = e;
|
|
setTimeout(function() {
|
|
self.refreshRadioGroup();
|
|
}, 0);
|
|
},
|
|
onFakeClick: function(e) {
|
|
// skip event if clicked on real element inside wrapper
|
|
if (this.options.wrapNative && this.realElement.is(e.target)) {
|
|
return;
|
|
}
|
|
|
|
// toggle checked class
|
|
if (!this.realElement.is(':disabled')) {
|
|
delete this.savedEventObject;
|
|
this.currentActiveRadio = this.getCurrentActiveRadio();
|
|
this.stateChecked = this.realElement.prop('checked');
|
|
this.realElement.prop('checked', true);
|
|
this.fireNativeEvent(this.realElement, 'click');
|
|
if (this.savedEventObject && this.savedEventObject.isDefaultPrevented()) {
|
|
this.realElement.prop('checked', this.stateChecked);
|
|
this.currentActiveRadio.prop('checked', true);
|
|
} else {
|
|
this.fireNativeEvent(this.realElement, 'change');
|
|
}
|
|
delete this.savedEventObject;
|
|
}
|
|
},
|
|
onFocus: function() {
|
|
if (!this.pressedFlag || !this.focusedFlag) {
|
|
this.focusedFlag = true;
|
|
this.fakeElement.addClass(this.options.focusClass);
|
|
this.realElement.on('blur', this.onBlur);
|
|
}
|
|
},
|
|
onBlur: function() {
|
|
if (!this.pressedFlag) {
|
|
this.focusedFlag = false;
|
|
this.fakeElement.removeClass(this.options.focusClass);
|
|
this.realElement.off('blur', this.onBlur);
|
|
}
|
|
},
|
|
onPress: function(e) {
|
|
if (!this.focusedFlag && e.pointerType === 'mouse') {
|
|
this.realElement.focus();
|
|
}
|
|
this.pressedFlag = true;
|
|
this.fakeElement.addClass(this.options.pressedClass);
|
|
this.doc.on('jcf-pointerup', this.onRelease);
|
|
},
|
|
onRelease: function(e) {
|
|
if (this.focusedFlag && e.pointerType === 'mouse') {
|
|
this.realElement.focus();
|
|
}
|
|
this.pressedFlag = false;
|
|
this.fakeElement.removeClass(this.options.pressedClass);
|
|
this.doc.off('jcf-pointerup', this.onRelease);
|
|
},
|
|
getCurrentActiveRadio: function() {
|
|
return this.getRadioGroup(this.realElement).filter(':checked');
|
|
},
|
|
getRadioGroup: function(radio) {
|
|
// find radio group for specified radio button
|
|
var name = radio.attr('name'),
|
|
parentForm = radio.parents('form');
|
|
|
|
if (name) {
|
|
if (parentForm.length) {
|
|
return parentForm.find('input[name="' + name + '"]');
|
|
} else {
|
|
return $('input[name="' + name + '"]:not(form input)');
|
|
}
|
|
} else {
|
|
return radio;
|
|
}
|
|
},
|
|
getLabelFor: function() {
|
|
var parentLabel = this.realElement.closest('label'),
|
|
elementId = this.realElement.prop('id');
|
|
|
|
if (!parentLabel.length && elementId) {
|
|
parentLabel = $('label[for="' + elementId + '"]');
|
|
}
|
|
return parentLabel.length ? parentLabel : null;
|
|
},
|
|
refreshRadioGroup: function() {
|
|
// redraw current radio and its group
|
|
this.getRadioGroup(this.realElement).each(function() {
|
|
jcf.refresh(this);
|
|
});
|
|
},
|
|
refresh: function() {
|
|
// redraw current radio button
|
|
var isChecked = this.realElement.is(':checked'),
|
|
isDisabled = this.realElement.is(':disabled');
|
|
|
|
this.fakeElement.toggleClass(this.options.checkedClass, isChecked)
|
|
.toggleClass(this.options.uncheckedClass, !isChecked)
|
|
.toggleClass(this.options.disabledClass, isDisabled);
|
|
|
|
if (this.labelElement) {
|
|
this.labelElement.toggleClass(this.options.labelActiveClass, isChecked);
|
|
}
|
|
},
|
|
destroy: function() {
|
|
// restore structure
|
|
if (this.options.wrapNative) {
|
|
this.realElement.insertBefore(this.fakeElement).css({
|
|
position: '',
|
|
width: '',
|
|
height: '',
|
|
opacity: '',
|
|
margin: ''
|
|
});
|
|
} else {
|
|
this.realElement.removeClass(this.options.hiddenClass);
|
|
}
|
|
|
|
// removing element will also remove its event handlers
|
|
this.fakeElement.off('jcf-pointerdown', this.onPress);
|
|
this.fakeElement.remove();
|
|
|
|
// remove other event handlers
|
|
this.doc.off('jcf-pointerup', this.onRelease);
|
|
this.realElement.off({
|
|
blur: this.onBlur,
|
|
focus: this.onFocus,
|
|
click: this.onRealClick
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
}(jcf));
|