/*
    DanskIT Widgets
    A widget is an implementation of some kind of manipulation of data on the client
    in genereal we us the "standard" jquery.ui widget for widgets,
    but it lacks good features to subclass a widget or we also want a generalized way of doing
    aspect oriented programming (added stuff to instances if a widget)
    
        
*/
(function($) {



/* 
    var object = (function(){
        function F(){}
        return function(p){
            F.prototype = p;
            return new F();
        };
    })();
   
*/
    
    var object = (function(F){
    	return (function(p){
	    		F.prototype = p;
	    		return new F();
	    });
    })(function (){});    

/*
    the above 2 functions does almost the same thing.
    In use it will be the same thing
    above in the 2nd assignment to var object a function (the (function(){}) ) is passed to the (function(F) {... so there its F
    the return value is a function using F to assign the supplied parameter p in (function(p)) as the prototype of the new object we create with new F()
    
    The top one does just about the same but uses an internal function for F instead
    
    In both cases we end up with object being a function that takes one parameter and returns a new object that has that parameter as its prototype
    function object(p)
        var o={};
        o.prototype=p
        return o;
    }
    is kind of equivalent but the cost of creating the annomimus
    
    Which one is best to use... hmm im not quite sure what cost most. The creation of the internal function or the creation of the function send as parameter.
    It seems to me that its just about the same.
    Various browsers implementation of the Javascript virtual machine might prefer one to the other, but they really shouldnt
    If i run into trouble ill start a bigger test.
        
*/
    $.fn.extend({
        isWidget: function() {
            if (1 === this.length) {
                return $.isPlainObject(this.data("ui-widget"));
            } else {
                var is=true;
                this.each(function(i) {
                    if (!$.isPlainObject($(this).data("ui-widget")))
                        return is=false; // break loop;
                });
                return is;
            }
        },
        asWidget: function(dropNonWidgets) {
            if (1==this.length) {
                return this.data("ui-widget");
            } else {
                var instances=[];
                this.each(function(i) {
                    var inst=$(this).data("ui-widget");
                    if (!dropNonWidgets || $.isPlainObject(inst) )
                        instances[instances.length]=$(this).data("ui-widget");
                });
                return instances;
            }
        }
    });
    
/*
    breaks other jquery widgets
    so cant use it yet.
        
    $.fn.oldClone=$.fn.clone
    var depth=0;
    $.fn.clone = function clone(withDataAndEvents) {
        var $t;
        var w;
        depth++;        
        if (w=this.isUIWidget()) {
            if ($.isFunction(w.clone)) {
               $t=w.clone(withDataAndEvents);        
            } else {
                var n=this.cloneNode();
                $t=$.fn[ w.widgetName ].call(n, w.options ); 
            }
        } else {
            $t = $.fn.oldClone.call(this,withDataAndEvents);
            var $c=$t.children();
            if ($c.length>0) {
                $c.detach();
                var ac=$c.map(function(index,domElm) {
                    return clone.call($(domElm),withDataAndEvents);
                });
                $.each(ac,function(i,$e) {
                    $t.append($e);
                });
                var x=0;
            } 
        }
        depth--;
        return $t;
    }
*/
    $.widget("ui.widget",{
    	// Aspect Oriented Programming tools from Justin Palmer's article
    	yield: null,
    	returnValues: { },
    	before: function(method, f) {
    		var original = this[method];
    		this[method] = function() {
    			this.returnValues[method] = f.apply(this, arguments);
    			return original.apply(this, arguments);
        		};
    	},
    	after: function(method, f) {
    		var original = this[method];
	    	this[method] = function() {
	    		this.returnValues[method] = original.apply(this, arguments);
	    		return f.apply(this, arguments);
	    	}
	    },
	    around: function(method, f) {
	    	var original = this[method];
	    	this[method] = function() {
	    		var tmp = this.yield;
	    		this.yield = original;
	    		var ret = f.apply(this, arguments);
	    		this.yield = tmp;
	    		return ret;
	    	}
	    },
	    trigger: function() {
	        return $.fn.trigger.apply($(this.element),$.makeArray(arguments));
	    },
	    triggerHandler: function() {
	        return $.fn.triggerHandler.apply($(this.element),$.makeArray(arguments));
	    },
	    bind: function() {
	        $.fn.bind.apply($(this.element),$.makeArray(arguments));
	    }
	    
    });


    // creating an empty "base" widget class
 //   $.widget("ui.widget");

    
    // subclassing. eg creating a specializatin of another widget
    var OVERRIDE = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
    $.ui.widget.subclass = function subclass(name) {
        // check how we can improve this. Its a bit inefficient.
        // Creating widget and discarding prototype is kind of an overkill
        $.widget(name,this);
        name=name.split(".");
        
        var widget = $[name[0]][name[1]];
        var superProto = this.prototype;
        var args = $.makeArray(arguments);
        var widgetName= widget.prototype.widgetName;
        var widgetEventPrefix= widget.prototype.widgetEventPrefix;
        var proto = args[0] = widget.prototype = object(superProto);
        $.extend.apply(null,args);
        proto.widgetName=widgetName
        proto.widgetEventPrefix=widgetEventPrefix
        proto.inheritedClass=superProto.widgetName;
        widget.subclass= subclass; // this function.
        for (key in proto) {
            if (proto.hasOwnProperty(key)) { // true if  proto[key] exists
                switch (key) {
                    case '_create':
                        var create = proto._create;
                        proto._create = function() {
                            this.element.data("ui-widget",this);
                            superProto._create.apply(this);
                            create.apply(this);
                        };
                    break;
                    case '_init':
                        var init = proto._init;
                        proto._init = function() {
                            superProto._init.apply(this);
                            init.apply(this);
                        };
                    break;
                    case 'destroy':
                        var destroy = proto.destroy;
                        proto.destroy = function() {
                            destroy.apply(this);
                            superProto.destroy.apply(this);
                        };
                    break;
                    case 'options':
                        var options = proto.options;
                        proto.options = $.extend(
                            {},
                            superProto.options, options
                        );
                    break;
                    default:
                        if ( $.isFunction(proto[key])
                             && $.isFunction(superProto[key])
                             && OVERRIDE.test(proto[key])
                            )
                        {
                            
                            proto[key] = (function(name, fn){
                                return function() {
                                    var tmp = this._super;
                                    this._super = superProto[name];
                                    try {
                                        var ret = fn.apply(this, arguments);
                                    }
                                    finally {
                                        this._super = tmp;
                                    }
                                    return ret;
                                };
                            })(key, proto[key]);
                            
                            /* explaining the above
                                first
                                Its an assignment
                                proto[key] = X
                                
                                so lets see what X is
                                (function(name,fn){
                                    return function() {
                                        ... doing stuff ...
                                        
                                        return ret
                                    }
                                })(key, proto[key])
                                
                                is creating a function that get called
                                var fn1 = function(name, fn) {
                                    var fn2 = function() {
                                        return Y
                                    }
                                    return fn2
                                }
                                
                                so the value that gets assigned to proto[key] is fn2
                                
                                
                            */
                        }
                    break;
                
                }
            }
        }
    };
})(jQuery)

