/**
* @name HiveMaps
* @version 0.9.2
* @author Joe Johnston <joe@socialhive.org>
* @copyright (c) 2009 Joe Johnston
* http://socialhive.org/hivemaps
* http://code.google.com/p/hivemaps
*
* Creates SOCIALHIVE.utils
*
**/

/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

(function($){
	// functions available in SOCIALHIVE.utils
  var HiveUtils = {
    xml2js: xml2js,
		ajaxUniqueCallback: ajaxUniqueCallback,
		searchObjProp: searchObjProp,
		loadScript: loadScript,
		queryString2Obj: queryString2Obj
  };
  

	/**
	* Convert simple XML into an object.
	*
	* Ignores XML attributes.
	**/
  function xml2js(xmlDoc, containerTag) {
    var output = new Array();
    var rawData = xmlDoc.getElementsByTagName(containerTag)[0];
    if (!rawData)
      return false;
    var i, j, oneRecord, oneObject;
    for (i = 0; i < rawData.childNodes.length; i++) {
      if (rawData.childNodes[i].nodeType == 1) {
        oneRecord = rawData.childNodes[i];
        oneObject = output[output.length] = new Object();
        for (j = 0; j < oneRecord.childNodes.length; j++) {
          if (oneRecord.childNodes[j].nodeType == 1 && oneRecord.childNodes[j].firstChild) {
            oneObject[oneRecord.childNodes[j].tagName] = oneRecord.childNodes[j].firstChild.nodeValue;    
          }
        }
      }
    }
    return output;
  }

	/**
	* Make sure only the last function matching the id is called.
	*
	* Often with AJAX apps, an ansynchronous function might be called multiple times
	* before the first call has returned.  The ajaxUniqueCallback method ensure that only
	* the callback function of the last call to the ansynchronous function is executed.
	* This is particularly useful for mapping applications where the interface might need 
	* updating each time the user moves the map, but we only really want the last update 
	* to fully execute.
	*
	* Usage:
	*	 var successCallback = ajaxUniqueCallback('someAsyncFunc', function(args){console.log('1', args)});
	*	 someAsyncFunc(successCallback);
	*	 var successCallback2 = ajaxUniqueCallback('someAsyncFunc', function(args){console.log('2', args)});
	*	 someAsyncFunc(successCallback2);
	*
	* 	 Only the second call to someAsyncFunc will successfully call its successCallback function.  The first
	*	 call will be silently ignored.
	**/
  function ajaxUniqueCallback(id, func, self) {
    if (! ajaxUniqueDict[id])
      ajaxUniqueDict[id] = 0;
    var uid = ++ajaxUniqueDict[id];
    return function() {
      if (!ajaxUniqueDict[id] || ajaxUniqueDict[id]!=uid)
        return;
      delete ajaxUniqueDict[id];  
      func.apply((self || this), arguments);
    };
  }
  var ajaxUniqueDict = {};

	/**
	* Searches an object hierarchy for the first matching property and return the respective value.  
	* If no match is found, default_value is returned.
	*
	* Usage:
	*		var obj = {address: {AdministrativeArea: {SubAdministrativeAreaName: 'Some Place'}}}
	* 	searchObjProp(obj, ['address.AdministrativeArea.Locality.DependentLocality.DependentLocalityName', 'address.AdministrativeArea.Locality.LocalityName', 'address.AdministrativeArea.SubAdministrativeAreaName', 'address.Locality.LocalityName'], 'Unknown')
	* 	// returns 'Some Place'
	**/
  function searchObjProp(obj, prop, default_value) {
		var r = null;
		if (prop.constructor == Array) {
			for (var i=0; i<prop.length; i++) {
				if (r = searchObjProp(obj, prop[i]))
					break;
			}
			return (r) ? r : default_value;
		}
		try {
			r = eval('obj.'+prop);
		} catch(e) {}
		return (r) ? r : default_value;
	}
	
	/**
	* Returns dictionary object of query string values.
	**/
	function queryString2Obj() {
	  var q = window.location.search.substring(1);
    var pairs = q.split("&");
    var o = {};
    for (i=pairs.length; i--;) {
      var tmp = pairs[i].split("=");
      o[tmp[0]] = tmp[1];
    }
	  return o;
	}
	
	/**
	* Loads a remote script by attaching it to the current page.
	*
	* ATTENTION: using loadScript could cause a serious security exploit!!!
	* Only load scripts that are completely trusted.
	**/
	function loadScript(url, attach) {
		var s = new Script(url);
		if (attach===null || attach)
			s.attach();
		return s;
	}
	function Script(url) {
    this.url = url; 
    this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
    this.headLoc = document.getElementsByTagName('head').item(0);
    this.id = 'SOCIALHIVE_script_' + scriptCounter++;
		this.attach = function(){
	    this.scriptObj = document.createElement("script");
	    this.scriptObj.setAttribute("type", "text/javascript");
	    this.scriptObj.setAttribute("src", this.url + this.noCacheIE);
	    this.scriptObj.setAttribute("id", this.id);
	    this.headLoc.appendChild(this.scriptObj);
		};
		this.remove = function(){
			this.headLoc.removeChild(this.scriptObj);  
		};
	}
	var scriptCounter = 1;
	
	
  if (!window.SOCIALHIVE)
    window.SOCIALHIVE = {};
  window.SOCIALHIVE.utils = HiveUtils;
})(jQuery);
