view examples/browser/web/js/jOWL_UI.js @ 663:bcc7a6ddb2c8

Better treatment of refine.hopsize Only adapt the query loop hopping if ADB_REFINE_HOP_SIZE is actually on the refine flags, rather than blithely taking the value of refine.hopsize. Significantly reduces the chances of inadvertently causing an infinite loop. Adjust the tests to remove the 23 now useless instances of "refine.hopsize = 1;" Closes trac ticket #16
author mas01cr
date Thu, 28 Jan 2010 10:23:42 +0000
parents 901803e1305f
children
line wrap: on
line source
/*
 * jOWL_UI, User Interface Elements for jOWL, semantic javascript library
 * Creator - David Decraene
 * Version 1.0
 * Website: http://Ontologyonline.org
 * Licensed under the MIT license
 * Verified with JSLint http://www.jslint.com/
 */
(function($){

jOWL.UI = {
	broadcaster : function(){
		var listeners = [];
		this.addListener = function(obj){
			function add(obj){if(obj.propertyChange) { listeners.push(obj); } }
			if(obj.constructor == Array){for(var i=0;i<obj.length;i++){ add(obj[i]); } }
			else { add(obj); }
			return this; 
		};
		this.broadcast = function(item){ for(var i=0;i<listeners.length;i++){ listeners[i].propertyChange.call(item, item); } };
		if(!this.propertyChange){ this.propertyChange = function(item){}; }
	},
	asBroadcaster : function(ui_elem){ ui_elem.broadcaster = jOWL.UI.broadcaster; ui_elem.broadcaster(); },
	defaults : {
		contentClass: "jowl-content",
		focusClass: "ui-state-hover",
		wrapperClass : "ui-widget-content"
	}
};

/** 
WIDGETS 
all widgets: 
	addListener : add an object with a propertyChange function, will be triggered on select
	propertyChange: update the widget with a new jowl object
Events:
	onSelect: (look into propertylens click), return false to suppress the event, this = jquery element, first argument = jOWL object
CSS:
	wrapperClass: css class(es) for main element, the el itself
	contentClass: css class(es) for main content element, accessible by el.content
	focusClass: css class(es) for element in focus,
*/
$.fn.extend({
/* 
owl_navbar
options:
	onSelect : this element refers to jquery node, first argument = jOWL object
*/
	owl_navbar: function(options){
		options = $.extend({
			contentClass : jOWL.UI.defaults.contentClass,
			focusClass : jOWL.UI.defaults.focusClass,
			onSelect : function(item){},
			onPropertyChange : function(item){}
		}, options);
		var self = this;
		this.addClass("jowl-navbar");
		this.content = $("."+options.contentClass, this).empty();
			if(!this.content.length) { this.content = $("<div/>").addClass(options.contentClass).appendTo(this); }
		this.parents =  $('<div/>').appendTo(this.content);
		this.focus = $('<div/>').addClass(options.focusClass).appendTo(this.content);
		this.children = $('<div/>').appendTo(this.content);
		var listnode = $('<span/>').click(function(){
			var node = $(this);
			var res = jOWL(node.attr('title'));
			if(options.onSelect.call(node, res) === false) { return; }
			if(res && res.isClass) { self.propertyChange.call(res, res); self.broadcast(res); }
		});

		jOWL.UI.asBroadcaster(this);

		this.propertyChange = function(item){
			if(options.onPropertyChange.call(this, item) === false) { return; }
			if(item.isClass){
				item.bind(self.focus);
				if(jOWL.options.reason) { item.hierarchy();}
				self.parents.empty().append(item.parents().bind(listnode));
				self.children.empty().append(item.children().bind(listnode));
			}
		};
		return this;
	},
/** 
autocomplete field.
*/
	owl_autocomplete : function(options){
		options = $.extend({
			time:500, //responsetime to check for new keystrokes, default 500
			chars:3, //number of characters needed before autocomplete starts searching
			focus:false, //put cursor on the input field when loading
			limit:10, //limit size of result list to given amount
			contentClass : "ui-widget-content",
			focusClass : jOWL.UI.defaults.focusClass,
			hintClass : "ui-autocomplete-hint",
			hint: false, //Message (if any) to show when unfocused.
			onSelect : function(item){}, //function that can be overridden
			formatListItem : function(listitem, type, identifier, termarray){ //formatting of results, can be overridden
				if(type){ listitem.append($('<div class="type"/>').text(type)); }
				listitem.append($('<div class="name"/>').text(identifier));
				if(termarray.length) { listitem.append($('<div class="terms"/>').text(termarray.join(', '))
					.prepend($('<span/>').addClass('termlabel').text("Terms: ")));
			}
		}}, options);
		jOWL.UI.asBroadcaster(this);

		this.showHint = function(){
			this.hinted = true;
			if(options.hint){
				this.addClass(options.hintClass).val(options.hint);
			}
			else {this.val(''); }
		};
		this.showHint();

		var self = this; var old = ''; var open = false; self.val('');
		var results = $('<ul/>').addClass(options.contentClass).addClass("jowl_autocomplete_results");
		var div = $("<div/>").addClass(options.wrapperClass).append(results); this.after(div);
		results.cache = {};
		results.isEmpty = function(){ for(x in results.cache) { return false; } return true; };
		results.close = function(){this.hide();};
		results.open = function(q, cache){
			this.show(); 
			if(q){
				if(!cache || results.isEmpty()) { results.cache = jOWL.query(q, options); }
				else { 
					var newcache = {};
					for(x in results.cache){
						var entry = results.cache[x]; 
						var found = false;
						var newentries = [];
						if(x.searchMatch(q) > -1) { found = true; }
						for(var i = 0;i<entry.length;i++){
							if(entry[i].term.searchMatch(q) > -1) { found = true; newentries.push(entry[i]); }
						}
						if(found) { newcache[x] = newentries; }
						}
					results.cache = newcache;
					}
				this.populate(results.cache);
				}
		};

		results.populate = function(data){
			var res = this; this.empty(); var count =0;
			var clickFunction = function(){
				var node = $(this), res = jOWL(node.data("jowltype"));
				if(options.onSelect.call(node, res) === false) { return; }
				self.broadcast(res);
			};

			var onHover = function(){ $(this).addClass(options.focusClass); };
			var offHover = function(){$(this).removeClass(options.focusClass);};

			for(x in data){
				if(count < options.limit){
					var item = data[x];
					var v = jOWL.isExternal(x);
					v = v ? v[1] : x;
					var list = $('<li/>').data("jowltype", x)
					.click(clickFunction).hover(onHover, offHover)
					.appendTo(res);
					var terms = [];
					for(var l = 0;l<item.length;l++){ 
						var found = false; var newterm = item[l].term;
						for(var y=0; y < terms.length;y++){ if(terms[y].toLowerCase() == newterm.toLowerCase()) { found = true; } }
						if(!found) { terms.push(newterm); }
						}
					options.formatListItem.call(list, list, item[0].type, v, terms);

				}
				count++;
			}
		};

		setInterval(function(){
			var newvalue = self.val();
			var cache = true;
			if(old != newvalue){
				var longervalue = newvalue.length > old.length && newvalue.indexOf(old) === 0;
				if(!old) { cache = false; }
				old = newvalue; 
				if(newvalue.length < options.chars && open){ results.close();open = false;}
				else if(newvalue.length >=options.chars && newvalue.length > 0){
					if(cache) { cache = longervalue && newvalue.length > options.chars; }
					results.open(newvalue, cache);
					open = true;
					}
				
			}
		}, options.time);

		self.bind('keyup', function(){ if(!this.value.length){ results.close(); open = false; } });
		self.bind('blur', function(){
			if(open){setTimeout(function(){results.close();}, 200);open = false;}
			if(!self.val().length){self.showHint();}
			});
		//timeout for registering clicks on results.
		self.bind('focus', function(){
			if(self.hinted){
				self.hinted = false;
				$(this).removeClass(options.hintClass).val('');
			}
			if(self.val().length && !open){results.open('', open);open = true;}});
		//reopen, but do not get results
		return this;
	},
/** 
Tree View
*/
	owl_treeview : function(options){
		options = $.extend({
			contentClass : jOWL.UI.defaults.contentClass,
			focusClass: "focus",
			nameClass: "name",
			treeClass: "jowl-treeview",
			rootClass: "root",
			onSelect : function(item){}, //function that can be overwritten to specfy behavior when something is selected
			rootThing : false, //if true then topnode is (owl) 'Thing'
			isStatic : false, // if static then selections will refresh the entire tree
			addChildren : false //add a given objects children to the treeview as well
		}, options);

		/** construct the hierarchy & make a tree of it */
		function TreeModel(owlobject){
			
			function clear(el){ //reset invParents, for later use.
				if(el.parents) { el.parents().each(function(){
					this.invParents = null; clear(this);
				}); }
			}

			function leaf(node){
				node.jnode.addClass(options.focusClass);
				if(options.addChildren){
					var entry = jOWL(node.$name.attr('title'));
					if(entry && entry.children){ entry.children().each(function(){ node.add(this); }); } }
			}

			function traverse(itemarray, appendto){
				if(!itemarray) { return; }
				itemarray.each(function(){
					var node = appendto.add(this);
					if(this.invParents){ traverse(this.invParents, node); }
					else { leaf(node); }
				});

			}	

			var h = owlobject.hierarchy(true);
			if(options.rootThing) { traverse(h, tree.root(jOWL("Thing"))); }
			else { 
				var root = tree.root(h); 
				for(var i=0;i<root.length;i++){ 
						traverse(root[i].invParents, root[i]);
						if(!root[i].invParents) { leaf(root[i]); }
					}

				}
			clear(owlobject);

		}

		/**
		var tree = $(selector).owl_treeview();
		var root = tree.root("node");
		root.add("node2").add("child");
		*/
		function Tree(node, treemodel, options){
			var rack = $('<ul/>').addClass(options.treeClass).appendTo(node);
			var tree = this;
			/**item can be text, a jOWL object, or a jOWL array */
			this.root = function(item){
				var rt = null; //root
				rack.empty();  
				if(item && item.each) {
					rt = [];
					item.each(function(it){
						var x =  new fn.node(it, true); 
						x.wrapper.addClass("tv");
						x.jnode.appendTo(rack);
						x.invParents = it.invParents; it.invParents = null;	//reset for later use
						rt.push(x);
					}); 
					return rt;
				}
				rt = new fn.node(item, true);
				rt.wrapper.addClass("tv"); 
				rt.jnode.appendTo(rack);
				return rt;
			};

			var fn = {};
			fn.node = function(text, isRoot){ //creates a new node
				this.jnode = isRoot ? $('<li/>').addClass(options.rootClass) : $('<li class="tvi"/>');
				this.$name = null;
				if(text){
					this.$name = $('<span/>').addClass(options.nameClass);
					if(typeof text == "string") { this.$name.html(text); }
					else if(text.bind) { text.bind(this.$name); }
					var n = this.$name; 
					this.$name.appendTo(this.jnode).click(function(){
						var entry = jOWL(n.attr('title'));
						if(entry && options.onSelect.call(n, entry) === false) { return; }
						tree.broadcast(entry); 
						if(options.isStatic) { tree.propertyChange(entry); }
						return false;});
				}
				
				this.wrapper = $('<ul/>').appendTo(this.jnode);
				var self = this;
					self.jnode.click(function(){toggle(); return false;});

				this.add = function(text){
					var nn = new fn.node(text);
					if(!self.wrapper.children().length) { toNode();	}
					else { 
						var lastchild = self.wrapper.children(':last'); 
						lastchild.swapClass("tvilc", "tvic");
						lastchild.swapClass("tvile", "tvie");
						lastchild.swapClass("tvil", "tvi");
						
						}//children - change end of list
					self.wrapper.append(nn.jnode.swapClass('tvi', 'tvil'));
					return nn;
					};

				function toggle(){ 
					var t = self.jnode.hasClass("tvic") || self.jnode.hasClass("tvie") || self.jnode.hasClass("tvilc") || self.jnode.hasClass("tvile");
					if(!t) { return; }
					self.jnode.swapClass('tvic', 'tvie'); self.jnode.swapClass('tvilc', 'tvile');
					self.wrapper.slideToggle();
					}

				function toNode(){ 
					self.jnode.swapClass('tvil', 'tvilc');
					self.jnode.swapClass('tvi', 'tvic');
					}
				};
				return this;
		}// end Tree

		this.addClass("jowl-tree");
		this.content = $("."+options.contentClass, this).empty();
		if(!this.content.length){ this.content = $('<div/>').addClass(options.contentClass).appendTo(this); }
		var tree = new Tree(this.content, null, options);
		jOWL.UI.asBroadcaster(tree);
		tree.propertyChange = function(item){ if(item.isClass) { var m = new TreeModel(item); } };
		return tree;
	},
/** Uses templating 	
options: 
onChange: owl:Class, owl:Thing, etc..., tell the widget what to do with the different kinds of Ontology Objects.
"data-jowl" : {split: ",  ", "somevariable" : function_triggered_for_each_result } 
   example: "rdfs:label" : {split: ",  ", "rdfs:label" : function(){ //'this' keyword refers to HTML element}} )
   example: "sparql-dl:PropertyValue(owl:Class, ?p, ?x)" : {"?p": function(){ //'this' keyword refers to HTML element }}
   //prefil: for sparql-dl queries
   //onComplete: function to trigger when the specific propertybox query is completed, this refers to the HTML element for propertybox
   //sort: sort results on specified parameter, for sparql-dl results.
onUpdate: called when the widget updates itself
*/
		owl_propertyLens : function(options){ 
			var self = this;
			self.options = $.extend({
				backlinkClass : "backlink",
				split: {},
				disable : {},
				click : {}},
				options);
			self.resourcetype = this.attr('data-jowl') || "owl:Class";
			var propertyboxes = [];
			$('.propertybox', this).each(function(){
				var node = new jOWL.UI.PropertyBox($(this), self);
				propertyboxes.push(node);
				node.el.hide();
			});
			var backlink = $('.backlink', this).hide();
			if(!backlink.length) { backlink = $('<div class="jowl_link"/>').addClass(self.options.backlinkClass).text("Back").hide().appendTo(this); }
			jOWL.UI.asBroadcaster(this);

			/** fn: optional function to execute*/
			this.link = function(source, target, htmlel, fn){
				htmlel.addClass("jowl_link").click(function(){
				if(fn) { fn(); }
				self.broadcast(target);
				self.propertyChange(target);
				backlink.source = source.name;
				backlink.show().unbind('click').click(function(){
					self.broadcast(source); self.propertyChange(source); backlink.hide();
				});

				});

			};

			var action = {
				"rdfs:label": function(item){ return [{"rdfs:label": item.label() }]; },
				"rdf:ID" : function(item){ return [{"rdf:ID": [item.name, item] }]; },
				"rdfs:comment" : function(item){
					return $.map(item.description(), function(n){return {"rdfs:comment":n }; });
					},
				"rdf:type" : function(item){
					if(item.owlClass) { return [{"rdf:type": item.owlClass() }]; }
					return [{"rdf:type": item.type }];
				},
				"term" : function(item){
					return $.map(item.terms(), function(n, i){ return { "term" : n[0] }; });
				},
				"rdfs:range": function(item){if(item.range) { return [{"rdfs:range": item.range }]; } },
				"rdfs:domain": function(item){if(item.domain) { return [{"rdfs:domain": item.domain }]; } },
				"permalink": function(item){
					var href = jOWL.permalink(item);
					return [{"permalink": "<a href='"+href+"'>Permalink</a>" }];
				},
				"owl:disjointWith": function(item){
					if(!(item.isClass)) { return; }
					return $.map(
							jOWL.Xpath('*', item.jnode)
								.filter(function(){return this.nodeName == "owl:disjointWith"; }), 
							function(n, i){ return {"owl:disjointWith": jOWL($(n).RDF_Resource())};
							});	
				},
				"default" : function(item){
					var type = this.attr("data-jowl");
					return $.map(
								jOWL.Xpath('*', item.jnode).filter(function(){return this.nodeName == type; }),
								function(n, i){ var x = {}; x[type] = $(n).text(); return x; }
								);	
				}
			};

			this.propertyChange = function(item){ 
				if(!item) { return; }
				self.property = item;
				if(backlink.source != item.name) { backlink.hide(); } else { backlink.source = false; }
				
				if(item.type != self.resourcetype){
					if(item.isDatatypeProperty && self.resourcetype == "rdf:Property") {}
					else if(item.isObjectProperty && self.resourcetype == "rdf:Property"){}
					else { return; }
				}

				for(var i = 0;i<propertyboxes.length;i++){
					var pbox = propertyboxes[i];
					pbox.clear();
					if(!pbox.actiontype){return; }
					var actiontype = pbox.actiontype;
					if(self.options.disable[actiontype]) { return; }

					if(!self.options[actiontype]) { self.options[actiontype] = {}; }

					if(actiontype.indexOf("sparql-dl:") === 0){ 
						var query = actiontype.split("sparql-dl:", 2)[1];
						var fill = {}; fill[self.resourcetype] = item;
						if(self.options[actiontype].prefill) { $.extend(fill, self.options[actiontype].prefill); }
						var qr = new jOWL.SPARQL_DL(query, fill).execute({onComplete : function(r){
							if(self.options[actiontype].sort) { r.sort(self.options[actiontype].sort); }
							pbox.setResults(r.results, item);
							}});
					}
					else {
						var choice = (action[actiontype]) ? actiontype : "default";
						var results = action[choice].call(pbox.valuebox, item);
						pbox.setResults(results, item);
					}
				}
					
					if(self.options.onUpdate) { self.options.onUpdate.call(this, item); }
			}; //end property change
		
		if(self.options.tooltip){
			var lens = this.remove();
			this.display = function(element, htmlel){
				htmlel.tooltip({
					title: element.label(), 
					html: function(){	lens.propertyChange(element); backlink.hide(); return lens.get(0); }
				}); 
			};
		}
		return this;
		},

/**
Use propertyChange to set the class
Use addField to add property refinements
Use serialize to serialize input
*/
	owl_datafield: function(options){
		options = $.extend({
			selectorClass : "jowl-datafield-selector",
			messageClass : "jowl-datafield-message",
			labelClass : "jowl-datafield-property-label"
		}, options);
		var self = this;
		var pArray = {}; //associative array for properties.
		this.messages = {};
		this.messages[jOWL.NS.xsd()+"positiveInteger"] = "Allowed values: positive numbers or comparisons like  '>5 && <15' ";

		this.addClass("owl_UI");
		jOWL.UI.asBroadcaster(this);

		this.property = null;

		this.propertyChange = function(item){
			if(item.isClass){
				this.property = item;
					for(x in pArray){//reset all properties
						if(pArray[x].remove){ pArray[x].remove(); delete pArray[x]; }
					}
			}
		};

		/** Sets up a new field */
        this.addField = function(property){
            if(pArray[property.URI]){
                //allow for multiple fields?
				return;
            }

			var $content = $("<div/>");
				 pArray[property.URI] = $content;

			var $title = property.bind($("<div/>")).addClass(options.labelClass).appendTo($content).click(function(){ $content.remove(); delete pArray[property.URI]; });

            if(property.isObjectProperty){

				var sp = new jOWL.SPARQL_DL("Type(?t, ?c),PropertyValue(concept, property, ?c)", {concept : self.property, property : property }).execute({ 
					onComplete : function(obj){
						if(!obj.results.length){ return; } //modify to deal with non value results
						obj.sort("?t");
						
						$select = $("<select class='"+options.selectorClass+"'/>").appendTo($content);

						for(var i=0;i<obj.results.length;i++){
							obj.results[i]['?t'].bind($("<option/>")).appendTo($select);
						}

						$content.appendTo(self);
					}});

            }
            else if(property.isDatatypeProperty){
				var msg ="";
				if(self.messages[property.range]){ msg = self.messages[property.range];	}

				var $input = $('<div/>').addClass(options.selectorClass).attr("title", property.range).append($('<input type="text" style="font-size:11px;width:100px;"/>'));
				var $message = $('<div/>').addClass(options.messageClass).text(msg).appendTo($input);

				$content.append($input).appendTo(self);
				$('input', $content).focus(function(){
					$message.animate({opacity: 1.0}, 1500).fadeOut();
				});

				
			}

		};

		this.serialize = function(){
			var q = { "Type": self.property, "PropertyValue" : [] };

			$('.'+options.selectorClass, self).each(function(){
				var $this = $(this);
				var $prop = $this.siblings('.'+options.labelClass);
				var prop = $prop.attr('title');
				if( $this.is("select")){
						var s = $this.get(0);
						var thing = $(s[s.selectedIndex]).attr('title');
						q.PropertyValue.push([prop, thing]);
					}
				else {
					var $input = $this.find("input");
					var datatype = $this.attr('title');
					var entry = $input.get(0).value;
					if(entry) { q.PropertyValue.push([prop, '"'+entry+'"']); }
				}
			});
			return q;
		};

		return this;
	}
});

/** Used by owl_PropertyLens */
jOWL.UI.PropertyBox = function($el, resourcebox){
	var v = $('[data-jowl]', $el);
	if(v.length){	this.descendant = true;}

	this.el = $el;
	this.resourcebox = resourcebox;
	this.valuebox = v.length ? v : $el;
	this.actiontype = this.valuebox.attr('data-jowl'); 
};

jOWL.UI.PropertyBox.prototype = {
	setResults : function(results, item){
		var nodes = jOWL.UI.Template(results, this.valuebox, this.resourcebox.options[this.actiontype].split);
		this.complete(nodes, item);
		if(nodes && nodes.length && this.descendant) { this.el.show(); this.valuebox.hide(); } 
		if(this.resourcebox.options[this.actiontype].onComplete) { this.resourcebox.options[this.actiontype].onComplete.call(this.el.get(0)); }	
	},
	complete : function(nodes, item){
		var res = this.resourcebox;
		if(!nodes || !nodes.length) { return; }
		var v = $.data(nodes, "parameters"); 
		for(x in v){ 
			if(v[x].length && typeof res.options[this.actiontype][x] == "function") {
				v[x].each(res.options[this.actiontype][x]);
			}}
		for(x in res.options.onChange){
			var data = $('[typeof='+x+']', nodes).add(nodes.filter('[typeof='+x+']'));
			if(x.charAt(0) == "." || x.charAt(0) == "#"){ data = data.add($(x, nodes));}
			data.each(function(){
				var node = $(this);
				$.data(node, 'data-jowl', x);
				var id = node.attr('title');
				if(id != "anonymousOntologyObject") { res.options.onChange[$.data(node, 'data-jowl')].call(node, item, jOWL(id), res); }
			});
		}
	},
	clear : function(){
		var prev = this.valuebox.prev('.jowl-template-result');
		if(!prev.length){ prev = this.valuebox.prev('.jowl-template-splitter');}
		if(prev.length) { prev.remove(); this.clear(this.valuebox); }
	}
};

/**arr: associative array of variablrd, jqel: node for which variables need to be substituted,  */
jOWL.UI.Template = function(arr, jqel, splitter){
	var options = {
		resultClass : "jowl-template-result",
		splitterClass : "jowl-template-splitter"
	};
	if(!arr) { return; }

	function bindObject(value, jnode){
		var bound = false;
		if(!value) { return false; }
		if(typeof value == 'string') { jnode.html(value); bound = true;}
		else if(value.constructor == Array){ 
			if(value.length == 2) { value[1].bind(jnode).text(value[0]); bound = true;	} 
			}
		else if(value.bind){ value.bind(jnode); bound = true; }
		return bound;
	}
	var count = 0, a = [], b = {};
	var remnantFn = function(){
		var txt = $(this).text(); 
		if(txt.indexOf('${') === 0 && txt.lastIndexOf('}') == txt.length-1 ) { $(this).hide(); }
	};
	for(var i=0;i<arr.length;i++){ 
		var x = jqel.clone(true).wrapInner("<"+jqel.get(0).nodeName+" class='"+options.resultClass+"'/>").children();
		/** copy style settings */
			x.addClass(jqel.attr('class')).removeClass('propertybox');
		/** accepted obj types= string, array["string", "jowlobject"], jowlobject*/
		for(obj in arr[i]){
			if(!b[obj]) { b[obj] = []; }
			var occurrences = $(':contains(${'+obj+'})', x);
			if(!occurrences.length){
				if(x.text() == "${"+obj+"}") { if(bindObject(arr[i][obj], x)) {
					count++; b[obj].push(x.get(0));
				}}
			}
			else { 
				occurrences.each(function(){	
					if(this.innerHTML == "${"+obj+"}") { var node = $(this); if(bindObject(arr[i][obj], node)) { count++;  b[obj].push(this); }	}
				});
			}
		}
		var remnants = $(':contains(${)', x); //hide parameters that weren't substituted
			remnants.each(remnantFn);
		if(count){
			x.insertBefore(jqel);
			a.push(x.get(0));
			if(count > 1 && splitter) { 
				$splitter = (splitter.indexOf('<') === 0) ? $(splitter) : $("<span/>").text(splitter);
				$splitter.addClass(options.splitterClass).insertBefore(x);
				}
		}
	}
	for(x in b){ if(b[x].length) { b[x] = $(b[x]); } }
	var nodes = $(a);
	$.data(nodes, "parameters", b);
	return nodes;
};

/** 
Supporting functionality
*/

$.fn.swapClass = function(c1,c2) {
	return this.each(function() {
		if ($(this).hasClass(c1)) { $(this).removeClass(c1); $(this).addClass(c2);} 
		else if ($(this).hasClass(c2)) {$(this).removeClass(c2);$(this).addClass(c1);}
		});
};

})(jQuery);