dojo.provide("plugd.base");
;(function(d){

	var	place = d.place,
		style = d.style,
		
		// shrinksafe loves this
		display = "display",
		visibility = "visibility",
		
		// used for the "smart" show()/hide()/toggle()
		speedMap = {
			"slow"    : 1800,
			"fast"    : 420,
			//>>excludeStart("sillyness", kwArgs.silly == "off");
			// these are just to be silly:
			"granny"  : 7600,
			"racecar" : 200,
			"snail"   : 1200,
			"rocket"  : 100,
			"peller"  : 3500,
			// this is "public API" bewlow, down here so we can build-exclude 
			// the above silly-ness and not worry about the comma breaking 
			// after build.
			//>>excludeEnd("sillyness");
			"mild"    : 900
		},
		
		// so djConfig.keepLayout is a Boolean, and defaults to false
		// which is display:none vs true which is visibility:hidden
		useLayout 	  = d.config.keepLayout || false,
		
		// define some constants, for our re-use and the benefit of shrinksafe
		styleProperty = useLayout ? visibility : display,
		hideProperty  = useLayout ? "hidden" : "none",
		
		// also, djConfig.useBlock will set display:block for the visible state
		//	if not using keepLayout. Otherwise, display:"" will be used.	
		showProperty  = useLayout ? "visible" : (d.config.useBlock ? "block" : ""),
		
		_getDuration = function(arg){
			return speedMap[arg in speedMap ? arg : "fast"];
		},
		
		// these too are for ShrinkSafe's benefit. 
		NodeList = d.NodeList,
		_each = NodeList._adaptAsForEach,
		
		// for dojo.generateId
		globalId, 
		id_count = 0, 
		
		// because IE is insane:
		_jankyEvent = /enter|over/,
		
		// dojo.selection function defition (one-time cost to determine):
		_selection = "getSelection",
		selection = d.global[_selection] || d.doc[_selection] || function(){
			return d.doc.selection.createRange().text || "";
		}
	;

	// namespace-polluting functions:
	d[_selection] = function(){ return selection() + ""; }
	
	d.unique = function(/* Function */testFn, /* String? */base){
		base = base || "djid_";
		do{ globalId = base + (++id_count); }
		while(testFn(globalId));
		return globalId; // String
	}

	d.generateId = function(/* String? */base){
		return d.unique(d.byId, base); // String
	}
	
	d.load = function(){

		var a = d._toArray(arguments), l = a.length,
			f = l && !d.isString(a[l - 1]) ? a.pop() : null;

		d.forEach(a, d.require, d);
		f && d.ready(f);
	}
	
	d.show = function(/* String|DomNode */n, /* String? */arg){
		if(!arg){
			style(n, styleProperty, showProperty);
		}else{
			// we have an arg!
			if(d.isString(arg)){
				// "fast" and "slow" seem good, default to "fast", use fade
				style(n, "opacity", 0)
				d.show(n)
				d.anim(n, { opacity: 1 }, _getDuration(arg));
			}
		}// else{ fail silently! }
	}
	
	d.hide = function(/* String|DomNode */n, /* String? */arg){
		if(!arg){
			style(n, styleProperty, hideProperty);
		}else{
			// we have an arg!
			if(d.isString(arg)){
				// "fast" and "slow" seem good, default to "fast", use fade
				style(n, "opacity", 1);
				d.anim(n, { opacity: 0 }, _getDuration(arg), null, d.hitch(d, "hide", n));
			}
		}// else{ fail silently! }
	}
	
	d.wrap = function(/* String|DomNode */n, /* String */nodeType){
		var element = d.create(nodeType);
		place(element, n, "before");
		place(n, element, "first");
		return element; // DomNode
	}
	
	d.toggle = function(/* String|DomNode */n, /* String? */speed){
		n = d.byId(n);
		d[(n.style[styleProperty] == hideProperty ? "show" : "hide")](n, speed);
	}
	
	d.qw = function(/* String? */str){
		return str ? d.map(str.split(/\ +/), d.trim) : []; // Array
	}
	
	d.create = function(nodeType, attrs, refNode, pos){
		var n = nodeType.charAt(0) == "<" ? 
			d._toDom(nodeType) : d.doc.createElement(nodeType);
		if(attrs){ d.attr(n, attrs); }
		if(refNode){ place(n, refNode, pos); }
		return n; // DomNode
	}
	
	d.sub = d.subscribe;
	d.pub = function(t){
		d.publish(t, d._toArray(arguments, 1));
	}
	
	d.forIn = function(obj, callback, scope){
		scope = scope || d.global;
		for(var key in obj){
			// FIXME: i feel like these are backwards? should it be function(key, value) ?
			callback.call(scope, obj[key], key, obj);
		}
	}
	
	d.compose = function(/* Function... */){
		var list = d._toArray(arguments);
		return function(){ // function
			var a = arguments;
			d.forEach(list, function(fn){
				a = fn.apply(this, d.isArrayLike(a) ? a : [a]);
			});
			return a; // Anything
		}
	}

	
	d.delay = function(fn, timeout){
		var args = d._toArray(arguments, 2);
		return setTimeout(function(){
			fn.apply(this, args);
		}, timeout);
	}
	
	d.defer = function(/* Function */fn){
		d.delay(fn, 0);
	}
	
	d.now = function(){
		return +(new Date()); // Number
	}

	d.reduce = function(arr, key){
		return d.map(arr, function(item){
			return item[key];
		});
	}

	d.throttle = function(cb, wait, thisObj){
		var canrun = true; 
		return function(){
			if(!canrun) return; 
			canrun = false;
			cb.apply(thisObj || cb, arguments);
			setTimeout(function(){ 
				canrun = true; 
			}, wait);
		}
	}

	d.debounce = function(cb, wait, thisObj){
		var timer;
		return function(){
			if(timer) clearTimeout(timer);
			var a = arguments;
			timer = setTimeout(function(){
				cb.apply(thisObj || cb, a);
			}, wait);
		}
	}

	d.all = d.every;
	d.any = d.some;
	d.each = function(list){
		return list && d[(d.isArrayLike(list) ? "forEach" : "forIn")].apply(d, arguments);
	}
	
	// wrap them into dojo.NodeList
	d.extend(NodeList, {


		show: _each(d.show),
		hide: _each(d.hide),
		toggle: _each(d.toggle),
		destroy: _each(d.destroy), 
		selectable: _each(d.setSelectable),
		
		create: function(/* String */tagName){
			return this.map(function(){ // dojo.NodeList
				return d.create(tagName);
			})._stash(this); 
		},
		
		size: function(boxType){
			boxType = boxType || "marginBox";
			var s = this.map(function(n){ 
				return d[boxType](n);
			});
			return s.length == 1 ? s[0] : s; // Array|Object
		},
		
		hover: function(/* Function */over, /* Function? */out){
			return this.onmouseenter(over).onmouseleave(out || over) // dojo.NodeList
		},
		
		hoverClass: function(/* String */className){
			return this.hover(function(e){ // dojo.NodeList
				d.toggleClass(this, className, _jankyEvent.test(e.type));
			});
		},
		
		grab: function(url, extraArgs, method){
			this.length && d.xhr(method || "GET", d._mixin({ url:url }, extraArgs))
				.addCallback(this, function(r){ this.addContent(r, "only"); });
			return this; // dojo.NodeList
		}
		
	});
	
	var oldQuery = d.query;
	d.query = function(/* String|DomNode */query, /* String?|DomNode? */scope){
		var c = d.isString(query) && query.charAt(0) == "<",
			r = oldQuery(c ? d.create(query) : query, scope);
		return r; // dojo.NodeList
	}
	
	d.conflict = function(){
		d.global.$ = d.mixin(function(){ 
			return d.mixin(d.query.apply(this, arguments), $.fn); }, { fn: {} } );
		d.global.$.fn.ready = d.ready;
		d.config.conflict = true; // set to true so other things can know we're in conflict mode
	}

	//>>excludeStart("autoConflict", kwArgs.autoConflict == "on");
	if(d.config.conflict){ 
	//>>excludeEnd("autoConflict");	
		d.conflict(); 
	//>>excludeStart("autoConflict", kwArgs.autoConflict == "on");	
	}
	//>>excludeEnd("autoConflict");	
	
	//>>excludeEnd("noConflict");

})(dojo);
