// jQueryExtensions © 2008 Jakub Łukomski

/* title: jQuery-DOM
 * =================
 *
 * DOM element creator for jQuery.
 *
 * about:
 *   based on script by Michael Geary (http://mg.to/topics/programming/javascript/jquery)
 *   which in turn is inspired by MochiKit.DOM by Bob Ippolito
 *
 * dependencies:
 *   jQuery-ns - optional, needed for jQuery.nodeNS
 *
 * history:
 *   when I first found Michael's DOM extension to jQuery I really loved it's
 *   syntax, however found out that it simply uses the jQuery namespace,
 *   actually making no use of jQuery functionality whatsoever.
 *   
 *   This both conflicted with the idea of actually using jQuery.*, as well as
 *   required users to enclose the freshly created tags inside another $()
 *   (e.g. $($.DIV(...))), which was rather annoying, so my first and
 *   fundamental change was actually returning a jQuery list instead of a DOM
 *   object, as well as accepting a jQuery list as children for the freshly
 *   created node.
 *
 *   Second important (even if simple) change was actually syncing the tag list
 *   with proper X/HTML standards - adding missing tags, removing proprietary
 *   ones and dividing into two lists strict and transitional.
 *
 *   A special method TEXT was created to define text nodes (using
 *   `createTextNode` instead of plain text).
 *
 *   Many minor changes in node creation routines followed, resulting in almost
 *   none of the original code remaining. This came to be the first "official"
 *   version: jQuery-DOM-1.0.
 *
 *   Version 1.1 changes included a move from separate functions into a
 *   `jQuery.extend()` method, and support for namespaces (using the jQuery-ns
 *   extension).
 *   Along with namespaces support came two simple functions for creation of
 *   custom nodes (without the need to declare special functions for them in
 *   advance)
 *
 *   version 1.2 introduced a change in tag handling. Only strict X/HTML tags
 *   are loaded by default, however transitional ones are loadable with a single
 *   function. New, user defined tags can be registered now as well.
 *
 *   NBSP and TEXT methods have been moved to _nbsp and _text to avoid conflict
 *   with potential <nbsp> and <text> nodes (the latter being more probable).
 *
 *   version, 1.3, made a change in handling of createNode function (by moving
 *   it to a closure only), as well as adding a few convenience functions.
 */

(function() { // HIDE VARS START

/* Strict Tags (HTML4 strict && XHTML 1.1)
 * '''''''''''''''''''''''''''''''''''''''
 * 
 *   This set of tags gets loaded by default. It's a list of all the tags in
 *   strict variant of HTML4 and in XHTML 1.1 (for example ruby tags are only in
 *   XHTML 1.1).
 */
var htmlStrictTags = [
	'a', 'abbr', 'acronym', 'address', 'area',
	'b', 'base', 'bdo', 'big', 'blockquote', 'body', 'br', 'button',
	'caption', 'cite', 'code', 'col', 'colgroup',
	'dd', 'del', 'dfn', 'div', 'dl', 'dt',
	'em', 'fieldset', 'form',
	'head', 'html', 'hr', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
	'i', 'img', 'input', 'ins', 'kbd',
	'label', 'legend', 'li', 'link',
	'map', 'meta', 'noscript',
	'object', 'ol', 'optgroup', 'option',
	'p', 'param', 'pre', 'q',
	'rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby',
	'samp', 'script', 'select', 'small', 'span', 'strong', 'style', 'sub', 'sup',
	'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'tt',
	'ul', 'var'
];

/* Transitional Tags (HTML4 transitional / XHTML1.0 transitional)
 * ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 *
 *   HTML 4 / XHTML 1.0 transitional tags, that are not loaded by default,
 *   however can be loaded using a built-in function.
 */
var htmlTransitionalTags = [
	'applet', 'basefont', 'center',
	'dir', 'font', 'frame', 'frameset',
	'iframe', 'isindex', 'menu',
	'noframes', 's', 'strike', 'u',
];

// NOTE: jQuery-DOM does NOT support proprietary tags (unless you define
// them yourself)

// create a new node
function createNode(namespaceURI, tag, args) {
	var e;
	try {
		var attrs = args[0] || {};
		e = jQuery(namespaceURI ? 
			document.createElementNS(namespaceURI, tag) :
			document.createElement(tag));
		e.attr(attrs);

		for(var i = 1; i < args.length; i++) {
			var arg = args[i];
			if(arg == null) continue;

			e.append(arg);
		}
	}
	catch(ex) {
		//alert('Cannot create <' + tag + '> element:\n' + args.toSource() + '\n' + args);
		e = null;
	}
	return e;
}

jQuery.extend({
	/* function: $.DOMtag
	 * ------------------
	 *
	 * Define a new tag. Creates a new tag function.
	 *
	 * parameters:
	 * 	(string) tag - name of tag to define
	 */
	DOMtag: function(tag) {
		if(typeof tag != 'string')
			return null;
	
		jQuery[tag.toUpperCase()] = function() {
			return createNode(null, tag, arguments);
		}
	},

	/* function: $.DOMtags
	 * -------------------
	 *
	 * Load a whole tag set, and creates corresponding tag functions.
	 *
	 * parameters:
	 * 	(array) tags - list of tagnames to load
	 */
	DOMtags: function(tags) {
		for(var i = 0 ; i < tags.length ; i++)
			jQuery.DOMtag( tags[i] );
	},

	/* function: $.DOMdtags
	 * --------------------
	 *
	 * Load (internally saved) deprecated/transitional X/HTML tags definitions.
	 */
	DOMdtags: function() {
		jQuery.DOMloadtags(htmlTransitionalTags);
	},

	/* function: $.nbsp
	 * ----------------
	 *
	 * Create a non-breakable space character
	 * TODO:
	 * 	see if this is valid - probably needs to be enclosed in a text
	 * 	node
	 */
	nbsp: function() { return '\u00a0' },

	/* function: $.text
	 * ----------------
	 *
	 * Create a text node instead of an element.
	 *
	 * parameters:
	 * 	(string) string - the contents of the new text node
	 *
	 * returns:
	 * 	jQuery object with a text node
	 */
	text: function(string) {
		return jQuery(document.createTextNode(string.toString()));
	},

	/* function: $.node
	 * ----------------
	 * Create a custom tag. Does not register a tag function.
	 *
	 * parameters:
	 * 	(string) name  - tag name
	 * 	(string) attrs - tag attributes
	 *
	 * returns:
	 * 	jQuery object containing newly created node
	 */
	node: function() {
		var tag = arguments[0];
		var newArgs = [];
		for(i = 1; i < arguments.length; i++)
			newArgs.push(arguments[i]);
		return createNode(null, tag, newArgs);
	},

	/* function: $.nodeNS
	 * ------------------
	 *
	 * Create a custom node with namespace. To operate, requires the
	 * jQuery-ns extension loaded.
	 *
	 * parameters:
	 * 	(string) namespacePrefix - prefix of the namespace to assign
	 * 	                           this tag to
	 * 	(string) name            - tag name
	 * 	(string) attrs           - tag attributes
	 */
	nodeNS: function() {
		var newArgs = [];
		for(i = 2; i < arguments.length; i++)
			newArgs.push(arguments[i]);

		var namespacePrefix = arguments[0];
		var tag = arguments[1];
		var namespaceURI = jQuery.getNamespaceURI(namespacePrefix);
		return createNode(namespaceURI, tag, newArgs);
	}
});

jQuery.fn.extend({
	/* function: $appendTag
	 * --------------------
	 *
	 * Convenience function combining $append and <$.node> in one function.
	 * Allows for creating a new tag and appending it at the same time.
	 *
	 * parameters:
	 * 	(string) tag     - name of the tag to create
	 * 	(object) attrs   - tag attributes
	 *	(mixed)  content - contents of the tag
	 */
	appendTag: function(tag, options, content) {
		return this.append(jQuery.node(tag, attrs, content));
	},

	/* function: $appendTagNonEmpty
	 * ----------------------------
	 *
	 * Extension of <$appendTag> that appends the tag only if it's contents
	 * are not empty.
	 */
	appendTagNonEmpty: function(tag, options, content) {
		if(!content) return this;
		return this.appendTag(tag, options, content);
	},

	/* function: $atne
	 * ---------------
	 *
	 * Convenience alias for appendTagNonEmpty.
	 */
	atne: function(tag, options, content) {
		return this.appendTag(tag, options, content);
	}
});

jQuery.DOMtags(htmlStrictTags);

})(); // HIDE VARS END

 