function removeChildren(a_node) {
    if (a_node.hasChildNodes()) {
        while (a_node.childNodes.length >= 1) {
            a_node.removeChild(a_node.firstChild);
        }
    }
}
 

     function makeXMLNotCachedRequest(a_url, a_callback, a_param, a_requirePost){
        if(isValid(a_param) || (isValid(a_requirePost) && a_requirePost)){
            // user_id, from_user_id, to_user_id, owner_id, pet_id
            try{
                var l_total_ids = 0;
                if(isValid(a_param.user_id)) l_total_ids += parseInt(a_param.user_id) % 123431;
                if(isValid(a_param.from_user_id)) l_total_ids += parseInt(a_param.from_user_id) % 123431;
                if(isValid(a_param.to_user_id)) l_total_ids += parseInt(a_param.to_user_id) % 123431;
                if(isValid(a_param.owner_id)) l_total_ids += parseInt(a_param.owner_id) % 123431;
                if(isValid(a_param.pet_id)) l_total_ids += parseInt(a_param.pet_id) % 123431;
                a_param.session_id = hex_sha1(""+l_total_ids);                
            }catch(err){
                a_param.session_id = "none";
            }

            XML_REQUEST_QUEUE.addRequest(XMLRequestQueue.DO_GET, a_url, a_callback, a_param);
        } else {
            XML_REQUEST_QUEUE.addRequest(XMLRequestQueue.DO_GET, a_url, a_callback, a_param);
        }
    }


    var XML_REQUEST_QUEUE = new XMLRequestQueue();
    XMLRequestQueue.DO_POST = 1;
    XMLRequestQueue.DO_GET = 2;
    XMLRequestQueue.MAX_RETRIES = 1;
    XMLRequestQueue.MAX_TIMEOUT = 30000;
    XMLRequestQueue.PREEMPT_REQUESTS = true;

    function XMLRequestQueue(){

        var m_numProcessedRequests = 0;
        var m_types = new Array();
        var m_urls = new Array();
        var m_callbacks = new Array();
        var m_params = new Array();
        var m_numRetries = new Array();
        var m_requestIds = new Array();

        var m_cur_type = undefined;
        var m_cur_url = undefined;
        var m_cur_callback = undefined;
        var m_cur_param = undefined;
        var m_cur_startTime = undefined;
        var m_cur_retries = 0;
        // allowing for timeouts and other goodness
        var m_cur_request_id = -1;

        // simple lock
        var m_preemptable = true;

        
        this.addRequest = addRequest;
        this.timeoutRequest = timeoutRequest;

        function addRequest(a_type, a_url, a_callback, a_param){

            if(XMLRequestQueue.PREEMPT_REQUESTS && m_preemptable && isValid(m_cur_startTime)){
                outputDebug("Request to " + m_cur_url + " has been pre-empted by " + a_url);

                m_types.push(m_cur_type);
                m_urls.push(m_cur_url);
                m_callbacks.push(m_cur_callback);
                m_params.push(m_cur_param);
                m_numRetries.push(m_cur_retries);
                m_requestIds.push(m_cur_request_id);
                clearCurState();
            }

            m_numProcessedRequests += 1;
            m_types.push(a_type);
            m_urls.push(a_url);
            m_callbacks.push(a_callback);
            m_params.push(a_param);
            m_numRetries.push(0);
            m_requestIds.push(m_numProcessedRequests);

            makeNewRequest();
        }

        function makeNewRequest(){
            if(m_urls.length == 0){
                return;
            }
            // When request timeout, queue is not started until there are new request
            if(m_cur_startTime != undefined){
                return;
            }

            m_cur_type = m_types.pop();
            m_cur_url = m_urls.pop();
            m_cur_callback = m_callbacks.pop();
            m_cur_param = m_params.pop();
            m_cur_startTime = (new Date()).getTime();
            m_cur_retries = m_numRetries.pop();
            m_cur_request_id = m_requestIds.pop();

            if(m_cur_retries > XMLRequestQueue.MAX_RETRIES){               
               var l_tempCallback = m_cur_callback;
               clearCurState();
               if(isValidFunction(l_tempCallback))
                    l_tempCallback(undefined);
               makeNewRequest();

           } else {
               doRequest();
           }
        }

        function retryRequest(){
           m_cur_retries += 1;
           outputDebug("retrying " + m_cur_url + " " + m_cur_retries + " retries");
           m_types.unshift(m_cur_type);
           m_urls.unshift(m_cur_url);
           m_callbacks.unshift(m_cur_callback);
           m_params.unshift(m_cur_param);
           m_numRetries.unshift(m_cur_retries);
           m_requestIds.unshift(m_cur_request_id);

           clearCurState();
           makeNewRequest();
        }

        
        function timeoutRequest(a_request_id){
            if(m_cur_request_id != a_request_id){
                outputDebug("Timeout request is already over for request with id: " + a_request_id);
                return;
            }

            outputDebug("Timeout for request with id: " + m_cur_request_id);
            retryRequest();
        }


        function doRequest(){
            window.setTimeout("XML_REQUEST_QUEUE.timeoutRequest(parseInt("+m_cur_request_id+"));",XMLRequestQueue.MAX_TIMEOUT);
            
            if(m_cur_type == XMLRequestQueue.DO_GET){
                doGetRequest(m_cur_request_id);
            } else if(m_cur_type == XMLRequestQueue.DO_POST){
                doPostRequest(m_cur_request_id);
            } else {
                outputDebug("UNRECOGNIZED REQUEST TYPE!!! " + m_cur_type);
                clearCurState();
                makeNewRequest();
            }
        }


        function doGetRequest(a_request_id){
            var f_curFinishFunction = function(response){
                onFinishRequest(response, a_request_id);
            }

            var l_sep = "?";
            if (m_cur_url.indexOf("?") > -1) {
                l_sep = "&";
            }

            var l_request_url = m_cur_url;
            if(isValid(m_cur_param)){
                var l_param_str = "";
                for(var l_id in m_cur_param){
                    if(typeof(m_cur_param[l_id]) == "number" || typeof(m_cur_param[l_id]) == "string" || typeof(m_cur_param[l_id]) == "boolean"){
                        l_param_str += l_id + "=" + m_cur_param[l_id] + "&";
                    }
                }
                if (l_param_str.length > 0 && l_param_str.lastIndexOf('&') == l_param_str.length - 1) {
                    l_param_str = l_param_str.substr(0, l_param_str.length - 1);
                }
                l_request_url += l_sep + l_param_str;
            }

            l_sep = "?";
            if (l_request_url.indexOf("?") > -1) {
                l_sep = "&";
            }
            var l_ts = new Date().getTime();
            l_request_url = [ l_request_url, l_sep, "nocache=", l_ts ].join("");

            var l_params = {};
            l_params['METHOD'] = 'GET';
            l_params['CONTENT_TYPE'] = gadgets.io.ContentType.DOM;
            l_params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;


            outputDebug("GET request with id " + a_request_id + " sent to " + l_request_url);

            gadgets.io.makeRequest(l_request_url, f_curFinishFunction, l_params);
        }

        function doPostRequest(a_request_id){
            var f_curFinishFunction = function(response){
                onFinishRequest(response, a_request_id);
            }

             if(isValid(POST_AS_STRING) && POST_AS_STRING){
                var l_param_str = "";
                for(var l_id in m_cur_param){
                    if(typeof(m_cur_param[l_id]) == "number" || typeof(m_cur_param[l_id]) == "string" || typeof(m_cur_param[l_id]) == "boolean"){
                        l_param_str += l_id + "=" + m_cur_param[l_id] + "&";
                    }
                }
                l_param_str += "nocache=" + new Date().getTime();

                var l_params = {};
                l_params[gadgets.io.RequestParameters.POST_DATA] = l_param_str;
                l_params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
                l_params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;
                l_params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;

                outputDebug("POST string request with id " + a_request_id + " sent to " + m_cur_url + " with param " + l_param_str);
                gadgets.io.makeRequest(m_cur_url, f_curFinishFunction, l_params);

            }else{
                m_cur_param.nocache = new Date().getTime();
                var l_params = {};
                l_params[gadgets.io.RequestParameters.POST_DATA] = m_cur_param;
                l_params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
                l_params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;
                l_params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
                 
                outputDebug("POST object request with id " + a_request_id + " sent to " + m_cur_url + " with param " + m_cur_param);
                gadgets.io.makeRequest(m_cur_url, f_curFinishFunction, l_params);
            }
        }

        function onFinishRequest(a_response, a_request_id){
            
            if(m_cur_request_id != a_request_id){
                outputDebug(" OUT of order request came back with id: " + a_request_id);
                return;
            }

            m_preemptable = false;
            
            if(m_cur_type == XMLRequestQueue.DO_GET){
                outputDebug(" GET request with id " + a_request_id + " cameback for " + m_cur_url + " after " + m_cur_retries + " num retries in " + ((new Date()).getTime() - m_cur_startTime) + " >>> " + a_response);
            } else {
                outputDebug(" POST request with id " + a_request_id + " cameback for " + m_cur_url + " after " + m_cur_retries + " num retries in " + ((new Date()).getTime() - m_cur_startTime) + " >>> " + a_response);
            }

            if(!isValid(a_response.data) && !isValid(a_response.text)){
                outputDebug(" response is not valid");
                retryRequest();
                
            } else{
                var l_tempCallback = m_cur_callback;
                clearCurState();
                if(isValidFunction(l_tempCallback)){
                    l_tempCallback(a_response);
                }
                makeNewRequest();
            }

            m_preemptable = true;
        }

        function clearCurState(){
            m_cur_type = undefined;
            m_cur_url = undefined;
            m_cur_callback = undefined;
            m_cur_param = undefined;
            m_cur_startTime = undefined;
            m_cur_retries = 0;
            m_cur_request_id = -1;
        }
    }


    function getGadgetResponseData(a_response){
        if(!isValid(a_response)){
            outputDebug("unfortunately there was an unknown error, please check back later: unknown error");
            return undefined;
        } else if(!isValid(a_response.data)){
            outputDebug("unfortunately there was an unknown error, please check back later: " + a_response.errors);
            return undefined;
        }
        return a_response.data;
    }

    function getGadgetResponseText(a_response){
        if(!isValid(a_response)){
            outputAlert("unfortunatley there was an unknown error, please check back later: unknown error");
            return undefined;
        } else if(!isValid(a_response.text)){
            outputDebug("unfortunatley there was no text, please check back later: " + a_response.errors);
            return undefined;
        }
        return a_response.text;
    }

    function parseXML(a_text) {
        if (typeof DOMParser != "undefined") {
            // Mozilla, Firefox, and related browsers
            return (new DOMParser()).parseFromString(a_text, "application/xml");
        }
        else if (typeof ActiveXObject != "undefined") {
            // Internet Explorer.
            var l_doc = XML.newDocument( );   // Create an empty document
            l_doc.loadXML(a_text);              //  Parse text into it
            return l_doc;                     // Return it
        }
        else {
            // As a last resort, try loading the document from a data: URL
            // This is supposed to work in Safari. Thanks to Manos Batsis and
            // his Sarissa library (sarissa.sourceforge.net) for this technique.
            var l_url = "data:text/xml;charset=utf-8," + encodeURIComponent(a_text);
            var l_request = new XMLHttpRequest();
            l_request.open("GET", l_url, false);
            l_request.send(null);
            return l_request.responseXML;
        }
    }

    function getXMLNodeValue(a_node, a_attribute){
        var l_attArray = a_node.getElementsByTagName(a_attribute);
        if(isValid(l_attArray) && l_attArray.length > 0 &&
           isValid(l_attArray[0].firstChild) &&
           isValid(l_attArray[0].firstChild.nodeValue)){

            return l_attArray[0].firstChild.nodeValue;
        }else{
//            outputAlert(attribute + " is undefined!");
            return undefined;
        }
    }

    function getXMLFirstNode(a_doc, a_attribute){
        var l_attArray = a_doc.getElementsByTagName(a_attribute);
        if(isValid(l_attArray) && l_attArray.length > 0 && isValid(l_attArray[0])){
            return l_attArray[0];
        } else {
            return undefined;
        }
    }

    function getXMLEncodedStringNodeValue(a_node, a_attribute) {
        var l_node = getXMLNodeValue(a_node, a_attribute);
        if (l_node == undefined) return undefined;
        else {
            return decodeURIComponent(l_node);
        }
    }


    OpenSocialRequest.REQUESTS = new Object();
    OpenSocialRequest.TIMEOUT = 5000;
    OpenSocialRequest.MAX_RETRIES = 3;    
    function OpenSocialRequest(a_request, a_key, a_callback){

        var m_key = a_key;
        var m_request = a_request;
        var m_callback = a_callback;
        var m_numRetries = 0;
        var m_finished = false;

        this.retryRequest = retryRequest;

        doRequest();

        function doRequest(){
            outputDebug("Doing request with key: " + m_key);

            OpenSocialRequest.REQUESTS[m_key] = retryRequest;
            m_request.send(onFinishRequest);
            window.setTimeout("OpenSocialRequest.REQUESTS['"+m_key+"']();", OpenSocialRequest.TIMEOUT);
            
        }

        function retryRequest(){
            outputDebug("Retrying request with key " + m_key + " num retries already: "+ m_numRetries);
            m_numRetries += 1;
            if(m_numRetries > OpenSocialRequest.MAX_RETRIES){
                onFinishRequest(undefined);
            } else {
                doRequest();
            }
        }

        function onFinishRequest(data){
            outputDebug("Finished request with key: " + m_key);
            
            if(!m_finished){
                m_callback(data);
            }
            m_finished = true;
        }
    }


    function addEvent(a_obj, a_evType, a_fn){
        if (a_obj.addEventListener){
            a_obj.addEventListener(a_evType, a_fn, false);
            return true;
        } else if (a_obj.attachEvent){
            var l_r = a_obj.attachEvent("on"+a_evType, a_fn);
            return l_r;
        } else {
            return false;
        }
    }

    function addEventWithParameter(a_obj, a_evType, a_fn, a_param){
        if (a_obj.addEventListener){
            a_obj.addEventListener(a_evType, function(){a_fn(a_param); return false;}, false);
            return true;
        } else if (a_obj.attachEvent){
            var l_r = a_obj.attachEvent("on"+a_evType, function(){a_fn(a_param); return false;});
            return l_r;
        } else {
            return false;
        }
    }
    
    function isValid(a_obj) {
        return (a_obj != undefined && a_obj != null);
    }

    function isValidFunction(a_obj){
        return isValid(a_obj) && (typeof(a_obj) == 'function');
    }
    

    function isDebugging(){
        try{
            return (isValid(GLOBAL_DEBUGGING) && GLOBAL_DEBUGGING);
        }catch(err){
            return false;
        }
    }

    function outputDebug(a_text) {
        try{
            if(isDebugging()){
                var l_debugElement = document.getElementById('debugOutput');
                if(isValid(l_debugElement) && isValid(l_debugElement.style) && l_debugElement.style.display != "none"){
                    //document.getElementById('debugOutput').innerHTML += a_text + '<br>';
                    var l_text = document.createElement("div");
                    l_text.innerHTML= a_text;
                    document.getElementById('debugOutput').appendChild(l_text);
                }
            }
        }catch(err){};
    }


    function outputAlert(a_text) {
        try{
            if(isDebugging()){
                alert(a_text);
            }
        }catch(err){};
    }

    function shortenedStringKeepEscapedCharacters(a_long_string, a_max_length) {
        if(!isValid(a_long_string)){
            return a_long_string;
        }

        if (a_long_string.length < a_max_length+1) {
            return a_long_string;
        }

        // new code - trying to be less retarded

        var l_i = 0;
        var l_j = 0;
        while(l_j<a_long_string.length && l_i<a_max_length) {
            // advance one character or symbol
            l_i++;
            if(a_long_string[l_j] != "&") {
                l_j++;
            } else {
                if(l_j+1 < a_long_string.length && a_long_string[l_j+1]==" ") {
                    l_j+=2;
                    l_i++;
                    continue;
                }
                var l_end = a_long_string.indexOf(";", l_j);
                if(l_end >0 && l_end-l_j<9) l_j=l_end;
                else break;
            }
        }
        if(l_j<a_long_string.length) return a_long_string.substring(0, l_j)+"..";
        else return a_long_string.substring(0, l_j);
    }

    function customEncoding(a_text) {
        return "cstm_" + encodeURIComponent(a_text).replace(/%/g, "@");
    }

    function replaceHTMLTags(a_str){
        var l_repStr = a_str.replace("<", "&lt;");
        l_repStr = l_repStr.replace(">", "&gt;");
        return l_repStr;
    }

    var TIME = {};
    TIME.MILLIS_IN_MINUTE = 60*1000;
    TIME.MILLIS_IN_HOUR = 60*60*1000;
    TIME.MILLIS_IN_DAY = 24*60*60*1000;
    TIME.MILLIS_IN_WEEK = 7*24*60*60*1000;
    TIME.MILLIS_IN_MONTH = 30*24*60*60*1000;

    function getTimeAgoString(a_timeInMillis){

        if(a_timeInMillis < TIME.MILLIS_IN_MINUTE){
            return Math.ceil(a_timeInMillis/1000) + " seconds ago";

        } else if(a_timeInMillis < TIME.MILLIS_IN_HOUR){
            return Math.floor(a_timeInMillis / TIME.MILLIS_IN_MINUTE) + " minutes ago";

        } else if(a_timeInMillis < TIME.MILLIS_IN_DAY){
            return Math.floor(a_timeInMillis / TIME.MILLIS_IN_HOUR) + " hours ago";

        } else if(a_timeInMillis < TIME.MILLIS_IN_WEEK){
            return Math.floor(a_timeInMillis / TIME.MILLIS_IN_DAY) + " days ago";

        } else if(a_timeInMillis < TIME.MILLIS_IN_MONTH){
            return Math.floor(a_timeInMillis / TIME.MILLIS_IN_WEEK) + " weeks ago";

        } else {
            return Math.floor(a_timeInMillis / TIME.MILLIS_IN_MONTH) + " months ago";
        }
    }



    function lineBreakText(a_text, a_maxlength, a_pattern){
        var l_re = new RegExp(a_pattern);

        var l_resultText = "";
        var l_remainingText = a_text;
        var l_match = l_re.exec(l_remainingText);

        while(l_match != null){
            l_resultText += l_remainingText.substring(0, l_match.index) + l_match + " ";
            l_remainingText = l_remainingText.substr(l_match.index + a_maxlength);
            l_match = l_re.exec(l_remainingText);
        }
        l_resultText += l_remainingText;

        return l_resultText;
    }



    // from http://pajhome.org.uk/crypt/md5/sha1src.html
    var g_hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
    var g_chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
    function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * g_chrsz));}

    function core_sha1(x, len)
    {
      /* append padding */
      x[len >> 5] |= 0x80 << (24 - len % 32);
      x[((len + 64 >> 9) << 4) + 15] = len;

      var w = Array(80);
      var a =  1732584193;
      var b = -271733879;
      var c = -1732584194;
      var d =  271733878;
      var e = -1009589776;

      for(var i = 0; i < x.length; i += 16)
      {
        var olda = a;
        var oldb = b;
        var oldc = c;
        var oldd = d;
        var olde = e;

        for(var j = 0; j < 80; j++)
        {
          if(j < 16) w[j] = x[i + j];
          else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
          var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
                           safe_add(safe_add(e, w[j]), sha1_kt(j)));
          e = d;
          d = c;
          c = rol(b, 30);
          b = a;
          a = t;
        }

        a = safe_add(a, olda);
        b = safe_add(b, oldb);
        c = safe_add(c, oldc);
        d = safe_add(d, oldd);
        e = safe_add(e, olde);
      }
      return Array(a, b, c, d, e);

    }
    function sha1_ft(t, b, c, d)
    {
      if(t < 20) return (b & c) | ((~b) & d);
      if(t < 40) return b ^ c ^ d;
      if(t < 60) return (b & c) | (b & d) | (c & d);
      return b ^ c ^ d;
    }
    function sha1_kt(t){
      return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
             (t < 60) ? -1894007588 : -899497514;
    }
    function safe_add(x, y){
      var lsw = (x & 0xFFFF) + (y & 0xFFFF);
      var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
      return (msw << 16) | (lsw & 0xFFFF);
    }
    function rol(num, cnt){
      return (num << cnt) | (num >>> (32 - cnt));
    }
    function str2binb(str)
    {
      var bin = Array();
      var mask = (1 << g_chrsz) - 1;
      for(var i = 0; i < str.length * g_chrsz; i += g_chrsz)
        bin[i>>5] |= (str.charCodeAt(i / g_chrsz) & mask) << (32 - g_chrsz - i%32);
      return bin;
    }
    function binb2hex(binarray)
    {
      var hex_tab = g_hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
      var str = "";
      for(var i = 0; i < binarray.length * 4; i++)
      {
        str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
               hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
      }
      return str;
    }


    function getOpenSocialParameter(a_paramId){
        try{
            var l_params = gadgets.views.getParams();
            if(isValid(l_params[a_paramId])) {
                return l_params[a_paramId];
            } else {
                return undefined;
            }
        }catch(err){
            outputDebug("getOSParam Error: " + err);
        }
    }



    // for debugging purposes
    function getFunctionName(a_theFunction) {
        // mozilla makes it easy. I love mozilla.
        if(a_theFunction.name) { return a_theFunction.name; }
        // try to parse the function name from the defintion
        var l_definition = a_theFunction.toString();
        var l_name = l_definition.substring(l_definition.indexOf('function') + 8,l_definition.indexOf('('));
        if(l_name) return l_name;
        // sometimes there won't be a function name
        // like for dynamic functions
        return "anonymous";
    }
    
    function getSignature(a_theFunction) {
        var l_signature = getFunctionName(a_theFunction);
        l_signature += "(";
        for(var l_x=0; isValid(a_theFunction.arguments) && l_x<a_theFunction.arguments.length; l_x++) {
            // trim long arguments
            var l_nextArgument = a_theFunction.arguments[l_x];
            if(l_nextArgument.length > 30)
                l_nextArgument = l_nextArgument.substring(0, 30) + "...";
            // apend the next argument to the signature
            l_signature += "'" + l_nextArgument + "'";
            // comma separator
            if(l_x < a_theFunction.arguments.length - 1)
                l_signature += ", ";
        }
        l_signature += ")";
        return l_signature;
    }

    function dump(a_obj) {
        var outputList = new Array();
        outputList.push("<P>");

        for (var name in a_obj) {
            outputList.push(name+":  "+a_obj[name]);
        }
        outputDebug(outputList.join("<br>"));
    }

    function outputStackTrace(a_startingPoint) {
        outputDebug("============================");
        outputDebug("Stack trace:"); 
        var l_nextCaller = a_startingPoint;
        while(l_nextCaller) {
            outputDebug(getSignature(l_nextCaller));;
            l_nextCaller = l_nextCaller.caller;
        }     
    }

    function navigateToCanvas(){
        var surfaces = gadgets.views.getSupportedViews();
        var surfaceRef = surfaces[gadgets.views.ViewType.CANVAS];
        gadgets.views.requestNavigateTo(surfaceRef);
    }

    function navigateToCanvasTab(a_initialTab){
        var param = {};

        if (isValid(a_initialTab) && typeof a_initialTab == "number" && a_initialTab >= 0) {
            param.initialTab = a_initialTab + '';
        }

        var surfaces = gadgets.views.getSupportedViews();
        var surfaceRef = surfaces[gadgets.views.ViewType.CANVAS];
        gadgets.views.requestNavigateTo(surfaceRef, param);
    }

    function navigateToCanvasTabPreserveDetailedUser(a_initialTab){
            var param = {};

            if (isValid(a_initialTab) && typeof a_initialTab == "number" && a_initialTab >= 0) {
                param.initialTab = a_initialTab + '';
            }

            if (isValid(GP.DETAIL_USER_ID) &&
                (GP.DETAIL_USER_ID != MAIN_DATA.getViewer().getUserId())) {
                param.showDetailedUserId = GP.DETAIL_USER_ID;
                if (isValid(GP.DETAIL_THUMBNAIL_URL)) {
                    param.detailedThumbnailUrl = GP.DETAIL_THUMBNAIL_URL;
                }

                if (isValid(GP.DETAIL_DISPLAY_NAME)) {
                    param.detailedDisplayName = GP.DETAIL_DISPLAY_NAME.replace(/[^A-Za-z0-9!@' ]/g, '');
                }
            } else {
                navigateToCanvasTab(1);
                return;
            }

            var surfaces = gadgets.views.getSupportedViews();
            var surfaceRef = surfaces[gadgets.views.ViewType.CANVAS];
            gadgets.views.requestNavigateTo(surfaceRef, param);
        }


    function getRandomNumbers(a_max, a_numNumbers){
           var a_numbers = new Array();
           if(a_max < a_numNumbers){
               for(var l_index = 0; l_index <= a_max; l_index++){
                   a_numbers.push(l_index);
               }
               return a_numbers;
           }

           var a_numbers = new Array();
           var a_pickedNumbers = {};

           for(var l_index = 0; l_index < a_numNumbers; l_index++){
               var l_current = Math.round(Math.random() * a_max);
               while(isValid(a_pickedNumbers[l_current])){
                   l_current = (l_current+1)%a_max;
               }
               a_pickedNumbers[l_current] = 1;
               a_numbers.push(l_current);
           }

           return a_numbers;
       }


function extendClass(a_subClass, a_baseClass) {
   function l_inheritance() {}
   l_inheritance.prototype = a_baseClass.prototype;

   a_subClass.prototype = new l_inheritance();
   a_subClass.prototype.constructor = a_subClass;
   a_subClass.baseConstructor = a_baseClass;
   a_subClass.superClass = a_baseClass.prototype;
}

/** Declare a new class as a subclass of another base class
 * @param init constructor
 * @param extra dictionary of additional methods and fields
 */
function declareClass(a_baseClass, a_initFunction, a_extraFields) {
    var l_newClass = a_initFunction;
    if(isValid(a_baseClass)) {
      extendClass(l_newClass, a_baseClass);
    }
    for(var m in a_extraFields) {
      l_newClass.prototype[m] = a_extraFields[m];
    }
    return l_newClass;
}

function getRandomNumbers(a_max, a_numNumbers){
   var a_numbers = new Array();
   if(a_max < a_numNumbers){
       for(var l_index = 0; l_index <= a_max; l_index++){
           a_numbers.push(l_index);
       }
       return a_numbers;
   }

   var a_numbers = new Array();
   var a_pickedNumbers = {};

   for(var l_index = 0; l_index < a_numNumbers; l_index++){
       var l_current = Math.round(Math.random() * a_max);
       while(isValid(a_pickedNumbers[l_current])){
           l_current = (l_current+1)%a_max;
       }
       a_pickedNumbers[l_current] = 1;
       a_numbers.push(l_current);
   }

   return a_numbers;
   }


   function convertToArray(a_hashMap, a_value) {
   var l_array = new Array();
   for (var i in a_hashMap) {
       if (a_hashMap[i] == a_value) {
           l_array.push(i)
       }
   }
   return l_array;
   }


   function appendParameter(a_url, a_param) {
   var result = a_url;

   if(a_url.charAt(a_url.length-1) == '?' ||
      a_url.charAt(a_url.length-1) == '&'){
       result += a_param;

   } else {

       var l_sep = "?";
       if (a_url.indexOf("?") > -1) {
           l_sep = "&";
       }
       result += l_sep + a_param;
   }

   return result;
   }

   // String improvements
   String.prototype.replaceAll = function(pcFrom, pcTo){
   var l_re = new RegExp(pcFrom, 'g')
   return this.replace(l_re, pcTo);
   }

   String.prototype.trim = function() {
   return this.replace(/^\s+|\s+$/g,"");
   }

   String.prototype.ltrim = function() {
   return this.replace(/^\s+/,"");
   }

   String.prototype.rtrim = function() {
   return this.replace(/\s+$/,"");
   }


   /** Returns a function, which when called, uses the given object as this and optionally passes in the given arguments */
   Function.prototype.bind = function(a_obj) {
   var l_self = this;
   var l_args = [];
   for(var i = 1; i < arguments.length; ++i) l_args.push(arguments[i]);
   return function() {
       var l_callArgs = l_args.slice(0);
       for(var i = 0; i < arguments.length; ++i) l_callArgs.push(arguments[i]);
       return l_self.apply(a_obj, l_callArgs);
   }
   }

   /** Permutes an array in place*/
   function array_permute(a) {
   var l_len = a.length;
   for(var i = 0; i < l_len; ++i) {
       var j = Math.floor(Math.random() * l_len);
       var temp = a[i];
       a[i] = a[j];
       a[j] = temp;
   }
   return a;
   }

   /** Pushes another array's elements onto this */
   function array_push_array(a1, a2) {
   for(var i = 0; i < a2.length; ++i) {
       a1.push(a2[i]);
   }
   }

   /** An object that can dispatch events to event handlers.  Based on jquery event handling methods (which only work for DOM nodes) */
   function EventDispatcher() {
   this.m_handlers = {};
   }

   /** binds an event handler to a named event.
* @param type  event type key
* @param fn    a function to bind to the event
*/
   EventDispatcher.prototype.bind = function(a_type, a_fn) {
   if(!this.m_handlers[a_type]) {
       this.m_handlers[a_type] = {}
   }

   this.m_handlers[a_type][$guid(a_fn)] = a_fn;
   return this;
   }

   /** unbinds an event handler
* @param type
* @param fn
*/
   EventDispatcher.prototype.unbind = function(a_type, a_fn) {
   try {
       delete this.m_handlers[a_type][$guid(a_fn)];
   } catch(err) {}
   return this;
   }

   /** Call to trigger an event */
   EventDispatcher.prototype.trigger = function(a_type, a_opt_data1, a_opt_data2, a_etc) {
   var l_handlers = this.m_handlers[a_type];
   if(!l_handlers) {
       return;
   }

   var args = [];
   for(var i = 1; i < arguments.length; ++i) args.push(arguments[i]);
   for(var guid in l_handlers) {
       l_handlers[guid].apply(this, args);
   }
   return this;
   }

   /** Declare an event - not necessary right now, but may be helpful */
   EventDispatcher.prototype.declareEvent = function(a_type) {
   this.m_handlers[a_type] = {};
   return this;
   }
   // end of EventDispatcher

/** attaches GUIDs to objects so that they may be used in a map */
   var __GUID = 1;
   function $guid(obj) {
   if(!obj._guid) {
       obj._guid = __GUID++;
   }
   return obj._guid;
   }

   /** A map that uses object GUID.
    * Gets around normal javascript map behaviour of turning objects into strings to use as keys */
   function GUIDMap() {
       this.m_map = {}
   }

   GUIDMap.prototype.put = function(key, value) {
       this.m_map[$guid(key)] = value;
   }

   GUIDMap.prototype.get = function(key) {
       return this.m_map[$guid(key)];
   }

   GUIDMap.prototype.remove = function(key) {
       delete this.m_map[$guid(key)];
   }

   GUIDMap.prototype.contains = function(key) {
       return isValid(this.m_map[$guid(key)]);
   }

   GUIDMap.prototype.clear = function() {
           this.m_map = {};
   }
   /* End of GUID map */


function hasTags(a_text) {
    return (a_text.match(".*<.*") != null ||
            a_text.match(".*>.*") != null);
}
