/** * Ion.CheckRadio * version 2.0.0 Build 42 * © Denis Ineshin, 2015 * * Project page: http://ionden.com/a/plugins/ion.CheckRadio/en.html * GitHub page: https://github.com/IonDen/ion.CheckRadio * * Released under MIT licence: * http://ionden.com/a/plugins/licence-en.html */ ;(function ($, window) { "use strict"; if ($.fn.ionCheckRadio) { return; } if (!String.prototype.trim) { (function() { var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; String.prototype.trim = function() { return this.replace(rtrim, ''); }; })(); } var collection = {}, instances = {}; var IonCheckRadio = function (group) { this.group = group.content; this.type = group.type; this.$group = $(this.group); this.old = null; this.observer = null; this.init(); }; IonCheckRadio.prototype = { init: function () { var ready = this.$group.eq(0).hasClass("icr-input"); if (ready) { this.prepare(); } else { this.createHTML(); } }, prepare: function () { var self = this, $item, $parent, i; for (i = 0; i < this.group.length; i++) { $item = $(this.group[i]); $parent = $item.parent().parent(); $.data(this.group[i], "icr-parent", $parent); this.presetChecked(this.group[i]); this.presetDisabled(this.group[i]); } this.$group.on("change", function () { self.change(this); }); this.$group.on("focus", function () { self.focus(this); }); this.$group.on("blur", function () { self.blur(this); }); // Trace input "disabled" attribute mutation // Only for modern browsers. IE11+ // To add cross browser support, install this: // https://github.com/megawac/MutationObserver.js if (window.MutationObserver) { this.setUpObserver(); } }, setUpObserver: function () { var self = this, node, i; this.observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { node = mutation.target; if (mutation.attributeName === "disabled") { self.toggle(self.getParent(node), node.disabled, "disabled"); } }); }); for (i = 0; i < this.group.length; i++) { this.observer.observe(this.group[i], { attributes: true }); } }, destroy: function () { this.$group.off(); if (this.observer) { this.observer.disconnect(); this.observer = null; } }, presetChecked: function (node) { if (node.checked) { this.toggle(this.getParent(node), true, "checked"); if (this.type === "radio") { this.old = node; } } }, presetDisabled: function (node) { if (node.disabled) { this.toggle(this.getParent(node), true, "disabled"); } }, change: function (node) { this.toggle(this.getParent(node), node.checked, "checked"); if (this.type === "radio" && this.old && this.old !== node) { this.toggle(this.getParent(this.old), this.old.checked, "checked"); } this.old = node; }, focus: function (node) { this.toggle(this.getParent(node), true, "focused"); }, blur: function (node) { this.toggle(this.getParent(node), false, "focused"); }, toggle: function ($node, state, class_name) { if (state) { $node.addClass(class_name); } else { $node.removeClass(class_name); } }, getParent: function (node) { return $.data(node, "icr-parent"); }, // auto transform code to correct layout // VERY SLOW(!) for lazy developers // to avoid this, use recommended html layout createHTML: function () { var tpl = '', classes = [], types = [], names = [], values = [], texts = [], checked_list = [], disabled_list = [], nc = [], self = this; var getTextParent = function ($label) { var label = $label[0], queue = [], nodes = label.childNodes, cur, text, html, start, end, i; for (i = 0; i < nodes.length; i++) { queue.push(nodes[i]); } while (queue.length) { cur = queue[0]; if (cur.nodeType === 3) { text = cur.nodeValue.trim(); if (text) { break; } } else if (cur.nodeType === 1) { nodes = cur.childNodes; for (i = 0; i < nodes.length; i++) { queue.push(nodes[i]); } } Array.prototype.splice.call(queue, 0, 1); } html = cur.parentNode.innerHTML; if (html.indexOf('= 0) { start = html.indexOf(''); html = html.slice(end + 1).trim(); } return html; }; var getHTML = function (i) { var tp = tpl.replace(/\{class_list\}/, classes[i]); tp = tp.replace(/\{type\}/g, types[i]); tp = tp.replace(/\{name\}/, names[i]); tp = tp.replace(/\{value\}/, values[i]); tp = tp.replace(/\{text\}/, texts[i]); if (disabled_list[i]) { tp = tp.replace(/\{disabled\}/, "disabled"); } else { tp = tp.replace(/\{disabled\}/, ""); } if (checked_list[i]) { tp = tp.replace(/\{checked\}/, "checked"); } else { tp = tp.replace(/\{checked\}/, ""); } return tp; }; this.$group.each(function (i) { var $label, $next, $cur = $(this), class_list = $cur.prop("className"), type = $cur.prop("type"), name = $cur.prop("name"), val = $cur.prop("value"), checked = $cur.prop("checked"), disabled = $cur.prop("disabled"), id = $cur.prop("id"), html; classes.push(class_list); types.push(type); names.push(name); values.push(val); checked_list.push(checked); disabled_list.push(disabled); if (id) { $label = $("label[for='" + id + "']"); } else { $label = $cur.closest("label"); } texts.push(getTextParent($label)); html = getHTML(i); $label.after(html); $next = $label.next(); nc.push($next[0]); $cur.remove(); $label.remove(); }); this.$group = $(nc).find("input"); this.$group.each(function (i) { self.group[i] = this; collection[names[0]][i] = this; }); this.prepare(); } }; $.fn.ionCheckRadio = function () { var i, local = [], input, name; var warn = function (text) { window.console && window.console.warn && window.console.warn(text); }; for (i = 0; i < this.length; i++) { input = this[i]; name = input.name; // if (input.type !== "radio" && input.type !== "checkbox" || !name) { // warn("Ion.CheckRadio: Some inputs have wrong type or absent name attribute!"); // continue; // } collection[name] = { type: input.type, content: [] }; local.push(input); } for (i = 0; i < local.length; i++) { input = local[i]; name = input.name; collection[name].content.push(input); } for (i in collection) { if (instances[i]) { instances[i].destroy(); instances[i] = null; } instances[i] = new IonCheckRadio(collection[i]); } }; })(jQuery, window);