var PraxeonObject = {
 create : function() {
    function F(){}
    F.prototype = this;
    var o = new F();
    o.initialize.apply(o, arguments);
    return o;
  },
 
 initialize : function() {
    //placeholder
    //sub-objects override this
  },
 
 //Extend an object with another's properties and methods
 extend : function(target, definition) {
    for(var i in definition) {
      var val = definition[i];
      target[i] = val;
    }
    return target;
  },
 
 /* Use this to create a new Praxeon object, the new object's prototype will be this object
    The object returned can be 'instantiated' with .create() */
 construct : function(definition) {
    var o = this.create();
    return this.extend(o, definition);
  }
};

var PraxeonWidget = PraxeonObject.construct({
    //shared table for callbacks
    callbacks : {
	'callback' : function() {
	    //the default, does nothing
	}
    },
      
    /* Accessor functions so widgets that inherit from here can get to the callback table */
    set_callback : function(callback_name, fn) {
	PraxeonWidget.callbacks[callback_name] = fn;
    },
    
    get_callback : function(callback_name) {
	if(callback_name) {
	    return PraxeonWidget.callbacks[callback_name];
	}
    },
    
    run_callback : function() {
	var callback_name = arguments[0];
	if(callback_name) {
	    var args = [];
	    for(var i=1; i< arguments.length; i++) {
		args.push(arguments[i]);
	    }
	    var fn = PraxeonWidget.get_callback(callback_name);
	    if(fn) {
		fn.apply(null, args);
	    }
	}
    },
    

    /* Useful methods for sub-widgets */

    generate_unique_callback_string : function() {
	    var str = 'callback_';
	    var choices = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
	    for(var i=0; i<11;i ++) {
		str = str + choices[Math.round(Math.random() * (choices.length - 1))]
	    }
	    if(!PraxeonWidget.get_callback(str)) {
		return str;
	    }
	    else {
		return this.generate_unique_callback_string();
	    }
     },

     is_element : function(el_or_id) {
           return el_or_id.constructor != String; //this could be better...
     },

     element_for_element_or_id : function(el_or_id) {
	   if(this.is_element(el_or_id)) {
	       return el_or_id
	   }
	   else {
	       return document.getElementById(el_or_id);
	   }    
     },

     insert_script_tag : function(url) {
          var script_el = document.createElement('script');
	  script_el.setAttribute('src', url);
	  document.body.appendChild(script_el);
	  return script_el;
     }

});


var SearchWidget = PraxeonWidget.construct({
	corpus : null,

	offset : null,  
 	limit : null,
	linkTarget : null,
 	orderBy : null,

	display_element_id : null,
	display_element_cache : null,

	initialize : function(corpus, display_element_or_id) {
	    this.corpus = corpus;

	    if(this.is_element(display_element_or_id)) {
		this.display_element_cache = display_element_or_id;
	    }
	    else {
		this.display_element_id = display_element_or_id;
	    }
	},

	display_element : function() {
	    if(!this.display_element_cache) {
		this.display_element_cache = document.getElementById(this.display_element_id);      
	    }
	    return this.display_element_cache;
	},


	draw_widget : function(html) {
	    this.display_element().innerHTML = html;
	},
 
	show_loading_message : function() {
	    this.draw_widget("<div class='header' style='width: 95%;'><img src='/shared/images/ECGAnim.gif'>&nbsp;&nbsp;<b>Searching MyDailyApple...</b></div>");
	},

	search_with_text : function(text) {
	    this.show_loading_message();
	    var calling_widget = this;
	    
	    var callback_string = this.generate_unique_callback_string();
	    
	    var insert;
	    this.set_callback(callback_string, function(html) {
		    calling_widget.draw_widget(html);
		    //clean up
		    calling_widget.set_callback(callback_string, null);
		    if(insert && insert.parentNode) {
			insert.parentNode.removeChild(insert);
		    }
		});
	    var script_url = 'http://www.mydailyapple.com/widget_results/search?search_text=' + encodeURIComponent(text) + '&corpus=' + encodeURIComponent(this.corpus) + '&callback=' + encodeURIComponent(callback_string) + '&format=js';
	    if ( this.offset != null ) script_url += '&offset=' + this.offset;
	    if ( this.limit != null ) script_url += '&limit=' + this.limit;
	    if ( this.linkTarget != null ) script_url += '&linkTarget=' + this.linkTarget;
	    if ( this.orderBy != null ) script_url += '&orderBy=' + this.orderBy;

	    insert = this.insert_script_tag(script_url);
	},
 
	search_with_element : function(el_or_id) {
	    this.search_with_elements([el_or_id]);
	},	

	search_with_elements : function(els_or_ids) {
	    var search_text = '';
	    for(var i = 0; i < els_or_ids.length; i++) {
		search_text += this.scrub_text_for_search(this.element_for_element_or_id(els_or_ids[i]));
	    }
	    this.search_with_text(search_text);
	},

	scrub_text_for_search : function(el) {
	    return this.get_inner_text(el);
	},

	is_text_entry_field : function(el) {
	    var tag = el.tagName.toLowerCase();
	    return (tag == 'textarea' || (tag == 'input' && el.hasAttribute('type') && (el.getAttribute('type') == 'text')));
	},

	get_inner_text : function(el) {
	    if (el.nodeType == 3) { //text node
		return el.nodeValue;	
	    }
	    else if (el.nodeType == 1) { //element node
		if(this.is_text_entry_field(el)) { //user has written in a form control
		    return el.value;
		}
		else { //we're looking at page content
		    var texts = [];
		    for(var i = 0; i< el.childNodes.length; i++) {
			texts.push(this.get_inner_text(el.childNodes[i]));
		    }
		    return texts.join(' ' ).replace(/\s+/, ' ' );
		}
	    }
	    else {
		return '';
	    }
	}
    });

