/*! * jQuery Data Link Plugin * http://github.com/jquery/jquery-datalink * * Copyright Software Freedom Conservancy, Inc. * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ (function($){ var oldcleandata = $.cleanData, links = [], fnSetters = { val: "val", html: "html", text: "text" }; function setValue(target, field, value) { if ( target.nodeType ) { if (field == 'val' && $(target).is('input[type=checkbox]')){ if (value) $(target).attr('checked', true); else $(target).attr('checked', false); } else{ var setter = fnSetters[ field ] || "attr"; $(target)[setter](value); } } else { $(target).data( field, value ); } } function getLinks(obj) { var data = $.data( obj ), cache, fn = data._getLinks || (cache={s:[], t:[]}, data._getLinks = function() { return cache; }); return fn(); } function bind(obj, wrapped, handler) { wrapped.bind( obj.nodeType ? "change" : "changeData", handler ); } function unbind(obj, wrapped, handler) { wrapped.unbind( obj.nodeType ? "change" : "changeData", handler ); } $.extend({ cleanData: function( elems ) { for ( var j, i = 0, elem; (elem = elems[i]) != null; i++ ) { // remove any links with this element as the source // or the target. var links = $.data( elem, "_getLinks" ); if ( links ) { links = links(); // links this element is the source of var self = $(elem); $.each(links.s, function() { unbind( elem, self, this.handler ); if ( this.handlerRev ) { unbind( this.target, $(this.target), this.handlerRev ); } }); // links this element is the target of $.each(links.t, function() { unbind( this.source, $(this.source), this.handler ); if ( this.handlerRev ) { unbind( elem, self, this.handlerRev ); } }); links.s = []; links.t = []; } } oldcleandata( elems ); }, convertFn: { "!": function(value) { return !value; } } }); function getMapping(ev, changed, newvalue, map, source, target) { var target = ev.target, isSetData = ev.type === "changeData", mappedName, convert; name; if ( isSetData ) { name = changed; if ( ev.namespace ) { name += "." + ev.namespace; } } else { name = (target.name || target.id); } if ( !map ) { mappedName = name; } else { var m = map[ name ]; if ( !m ) { return null; } mappedName = m.name; convert = m.convert; if ( typeof convert === "string" ) { convert = $.convertFn[ convert ]; } } var val = null; if (isSetData) val = newvalue; else if (target && $(target).is('input[type=checkbox]')){ val = $(target).is(':checked'); } else val = $(target).val(); return { name: mappedName, convert: convert, value: val }; } $.extend($.fn, { link: function(target, mapping) { var self = this; if ( !target ) { return self; } function matchByName(name) { var selector = "[name=" + name + "], [id=" + name +"]"; // include elements in this set that match as well a child matches return self.filter(selector).add(self.find(selector)); } if ( typeof target === "string" ) { target = $( target, this.context || null )[ 0 ]; } var hasTwoWay = !mapping, map, mapRev, handler = function(ev, changed, newvalue) { // a dom element change event occurred, update the target var m = getMapping( ev, changed, newvalue, map ); if ( m ) { var name = m.name, value = m.value, convert = m.convert; if ( convert ) { value = convert( value, ev.target, target ); } if ( value !== undefined ) { setValue( target, name, value ); } } }, handlerRev = function(ev, changed, newvalue) { // a change or changeData event occurred on the target, // update the corresponding source elements var m = getMapping( ev, changed, newvalue, mapRev ); if ( m ) { var name = m.name, value = m.value, convert = m.convert; // find elements within the original selector // that have the same name or id as the field that updated matchByName(name).each(function() { newvalue = value; if ( convert ) { newvalue = convert( newvalue, target, this ); } if ( newvalue !== undefined ) { setValue( this, "val", newvalue ); } }); } }; if ( mapping ) { $.each(mapping, function(n, v) { var name = v, convert, convertBack, twoWay; if ( $.isPlainObject( v ) ) { name = v.name || n; convert = v.convert; convertBack = v.convertBack; twoWay = v.twoWay !== false; hasTwoWay |= twoWay; } else { hasTwoWay = twoWay = true; } if ( twoWay ) { mapRev = mapRev || {}; mapRev[ n ] = { name: name, convert: convertBack }; } map = map || {}; map[ name ] = { name: n, convert: convert, twoWay: twoWay }; }); } // associate the link with each source and target so it can be // removed automaticaly when _either_ side is removed. self.each(function() { bind( this, $(this), handler ); var link = { handler: handler, handlerRev: hasTwoWay ? handlerRev : null, target: target, source: this }; getLinks( this ).s.push( link ); if ( target.nodeType ) { getLinks( target ).t.push( link ); } }); if ( hasTwoWay ) { bind( target, $(target), handlerRev ); } for (var n in target){ if (!$.isFunction(target[n])){ handlerRev({type: 'changeData'}, n, target[n]); } } return self; }, unlink: function(target) { this.each(function() { var self = $(this), links = getLinks( this ).s; for (var i = links.length-1; i >= 0; i--) { var link = links[ i ]; if ( link.target === target ) { // unbind the handlers //wrapped.unbind( obj.nodeType ? "change" : "changeData", handler ); unbind( this, self, link.handler ); if ( link.handlerRev ) { unbind( link.target, $(link.target), link.handlerRev ); } // remove from source links links.splice( i, 1 ); // remove from target links var targetLinks = getLinks( link.target ).t, index = $.inArray( link, targetLinks ); if ( index !== -1 ) { targetLinks.splice( index, 1 ); } } } }); } }); })(jQuery);