Array.prototype.contains = function(p_val) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == p_val) {
			return true;
		}
	}
	return false;
}

/**
 * @param {function} func
 */
function add_window_onload(func) {
    var oldonload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = func;
    }
    else {
        window.onload = function() {
            oldonload();
            func();
        }	  
    }
}  

/*
	Developed by Robert Nyman, http://www.robertnyman.com
	Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
var getElementsByClassName = function (className, tag, elm){
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
}

/**
 * Get string width in pixels
 *
 * @param {String} text
 * @return {integer}
 */
function get_string_width(text) {
    var span_element = document.createElement('span');
    span_element.style.whiteSpace = 'nowrap';
    span_element.innerHTML = text;
    document.body.appendChild(span_element);
    var width = span_element.offsetWidth;
    document.body.removeChild(span_element);
    
    return width;
}

   
/**
 * Reaction to location lists updating timeout
 *
 * Resets all selects to initial state and displays an alert
 */
function loc_list_update_timeout(country_select, province_select, city_select) {
	country_select.options[0].selected = true;
	
	first_option = province_select.options[0]; //save the first option for restoring
	province_select.options.length = 0; //remove old options
	add_select_option(province_select, first_option.text, first_option.value);
	province_select.disabled = false;
	
	first_option = province_select.options[0]; //save the first option for restoring
	city_select.options.length = 0; //remove old options
	add_select_option(city_select, first_option.text, first_option.value);
	city_select.disabled = false;
	
	alert("There was a time-out problem during the last action. We are sorry for the inconvenience. Please try repeating your action or reloading the page. \n\
If the problem persists, contact Learn4Good Admin.");
}

/**
 * Fill a province select with options corresponding to a selected country
 *
 * Uses AJAX
 * @param string country_select_id
 * @param string province_select_id
 * @param string city_select_id
 * @param string province_container_id Object which contains province select, it is hidden if a country doesn't have any provinces
 */
function province_list_update(country_select_id, province_select_id, city_select_id, province_container_id) {
    var countries_select = document.getElementById(country_select_id);
    var province_select = document.getElementById(province_select_id);
    var city_select = document.getElementById(city_select_id);
    
    document.getElementById(province_container_id).style.display = '';
    
	//display "wait" message
	add_select_option(province_select, 'Please wait...', 'wait');
	province_select.value = 'wait';
    
    //disable selects until they are filled with new data
    province_select.disabled = true;
    city_select.disabled = true;
    
    xhro = createXmlHttpRequestObject();
    if (xhro==null) return false;
    xhro.onreadystatechange=function () {
        if (xhro.readyState==4 || xhro.readyState=="complete") {
        	window.clearTimeout(province_list_update_timeout);
        	
            //alert(xhro.responseText); //for testing purposes
            try {
	            var resp = eval('(' + xhro.responseText + ')');
	             
	            //populate select with new options (DOM)
	            first_option = province_select.options[0]; //save the first option for restoring
	            province_select.options.length = 0; //remove old options
	            
	            add_select_option(province_select, first_option.text, first_option.value);

                var no_provinces = false;
	            if (!resp.options.length) {
                    no_provinces = true;
	                //hide this select if there are no provinces
	                document.getElementById(province_container_id).style.display = 'none';
	            }
	            else { //populate
	                for (i=0; i<resp.options.length; i++) {
	                    if (resp.options[i].name == null) continue;
	                    
	                    add_select_option(province_select, html_entity_decode(resp.options[i].name), resp.options[i].value);
	                }
	            }
	            //enable states select
	            province_select.disabled = false;
	            //call cities update right after
                if (no_provinces) {
                    city_list_update(country_select_id, province_select_id, city_select_id);
                }
                else {
                    if (province_select.value > 0) {
                        city_list_update(country_select_id, province_select_id, city_select_id);
                    }
                    else {
                        fast_city_list_update(country_select_id, province_select_id, city_select_id);
                    }
                }
	        }
	        catch (ex) {
	        
	        }
        }
    };
    data = "country_id="+countries_select.value;
    xhro.open("post","/schools/actions/province_list_update.php?" + new Date().getTime(), true); //true - asynchronous flag
    xhro.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhro.setRequestHeader("Content-length", data.length);
    xhro.setRequestHeader("Connection", "close");
    xhro.send(data);
    
    var province_list_update_timeout = window.setTimeout(function () {
		if (is_call_in_progress(xhro)) {
			xhro.abort();
			loc_list_update_timeout(countries_select, province_select, city_select);
		}   
    }, 10000); //10 seconds time out
}

/**
 * Basically the same as city_list_update but doesn't load a city list if no province is selected
 */
function fast_city_list_update(country_select_id, province_select_id, city_select_id) {
    var province_select = document.getElementById(province_select_id);
    var city_select = document.getElementById(city_select_id);
    if (province_select.value > 0 || province_select.style.display == 'none') {
        city_list_update(country_select_id, province_select_id, city_select_id);
    }
    else {
        //no cities (instead of full cities list)
        first_option = city_select.options[0]; //save the first option for restoring
        city_select.options.length = 0; //remove old options

        add_select_option(city_select, first_option.text, first_option.value);

        city_select.value = '0';
        //enable cities select
        city_select.disabled = false;
    }
}

/**
 * Fill a city select with options corresponding to a selected province
 *
 * Uses AJAX
 * @param {String} country_select_id
 * @param {String} province_select_id
 * @param {String} city_select_id
 */
function city_list_update(country_select_id, province_select_id, city_select_id) {
    var country_select = document.getElementById(country_select_id);
    var province_select = document.getElementById(province_select_id);
    var city_select = document.getElementById(city_select_id);
    
	//display "wait" message
	add_select_option(city_select, 'Please wait...', 'wait');
	city_select.value = 'wait';
    
    //disable selects until they are filled with new data
    city_select.disabled = true;
    
    xhro = createXmlHttpRequestObject();
    if (xhro==null) return false;
    xhro.onreadystatechange=function () {
        if (xhro.readyState==4 || xhro.readyState=="complete") {
        	window.clearTimeout(city_list_update_timeout);
        
            //alert(xhro.responseText); //for testing purposes
            try {
	            var resp = eval('(' + xhro.responseText + ')');
	            
	            //populate select with new options (DOM)
	            first_option = city_select.options[0]; //save the first option for restoring
	            city_select.options.length = 0; //remove old options
	            
	            add_select_option(city_select, first_option.text, first_option.value);
	                            
	            for (i=0; i<resp.options.length; i++) {
	                if (resp.options[i].name == null || resp.options[i].name == '') continue;
	                
	                add_select_option(city_select, html_entity_decode(resp.options[i].name), resp.options[i].value);
	            }
	            city_select.value = '0';
	            //enable cities select
	            city_select.disabled = false;
	        }
	        catch (ex) {
	        
	        }
        }
    };
    var data = '';
    if (country_select) { //there may be no country select (e.g. in listing importing)
    	data = "country_id=" + country_select.value;
    }
    data += "&province_id="+province_select.value;
    xhro.open("post", "/schools/actions/city_list_update.php?" + new Date().getTime(), true); //true - asynchronous flag
    xhro.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhro.setRequestHeader("Content-length", data.length);
    xhro.setRequestHeader("Connection", "close");
    xhro.send(data);
    
    var city_list_update_timeout = window.setTimeout(function () {
		if (is_call_in_progress(xhro)) {
			xhro.abort();
			loc_list_update_timeout(country_select, province_select, city_select);
		}   
    }, 10000); //10 seconds time out
}

/**
 * Add an option to a select form element
 *
 * @param Object select_object
 * @param string text
 * @param string value
 */
function add_select_option(select_object, text, value) {
	var new_option = document.createElement('option');
	new_option.text = text;
	new_option.value = value;
	try {
	   select_object.add(new_option, null); // standards compliant; doesn't work in IE
	}
	catch (ex) {
	   select_object.add(new_option); // IE only
	}  
}

/**
 * Calculate symbols types in a specific textarea
 *
 * @param string textarea_id A textarea where the text is typed
 * @param string counter_id An element holding a number of typed symbols (span, div, ...)
 * @param integer max_count The maximum number of allowed symbols
 * @return integer number of symbols
 */
function calculate_symbols(textarea, counter_id, max_count) {
    textarea = document.getElementById(textarea);
    counter = document.getElementById(counter_id);
    var real_len = textarea.value.replace(/[\r\n]/g, '').length
    counter.innerHTML = real_len;
    
    if (real_len > max_count) {
        counter.className = 'symbols_max_err';
    }
    else {
        counter.className = '';
    }
    
    return real_len;
}

/**
 * Select or deselect all options in a multiselect
 *
 * @param integer id ID of a multiselect
 * @param boolean selected True for select all, false for deselect all
 */
function multiselect_set_all(id, selected) {
	var multiselect = document.getElementById(id);
	for (i=0; i<multiselect.options.length; i++) {
		multiselect.options[i].selected = selected;
	}
}

/**
 * Toggles visibility of an object
 * 
 * @return boolean New visibility status
 */
function toggle(object_id) {
    var object = document.getElementById(object_id);
    if (object) {
        if (object.style.display == 'none') {
            object.style.display = '';
            return true;
        }
        else {
            object.style.display = 'none';
            return false;
        }
    }
}

/**
 * PHP equivalent html_entity_decode
 */
function html_entity_decode(string) {
    var ret, tarea = document.createElement('textarea');
    tarea.innerHTML = string;
    ret = tarea.value;
    return ret;
}

/**
 * JS equivalent for PHP's strip_tags
 * Taken from http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_strip_tags/
 * @param {String} str
 * @param {String} allowed_tags E.g. <b><i>
 * @return {String}
 */
function strip_tags(str, allowed_tags) { 
    var key = '', tag = '', allowed = false;
    var matches = allowed_array = [];
    var allowed_keys = {};
 
    var replacer = function(search, replace, str) {
        return str.split(search).join(replace);
    };
 
    // Build allowes tags associative array
    if (allowed_tags) {
        allowed_array = allowed_tags.match(/([a-zA-Z]+)/gi);
    }
  
    str += '';
 
    // Match tags
    matches = str.match(/(<\/?[^>]+>)/gi);
 
    // Go through all HTML tags
    for (key in matches) {
        if (isNaN(key)) {
            // IE7 Hack
            continue;
        }
 
        // Save HTML tag
        html = matches[key].toString();
 
        // Is tag not in allowed list? Remove from str!
        allowed = false;
 
        // Go through all allowed tags
        for (k in allowed_array) {
            // Init
            allowed_tag = allowed_array[k];
            i = -1;
 
            if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
            if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
            if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag)   ;}
 
            // Determine
            if (i == 0) {
                allowed = true;
                break;
            }
        }
 
        if (!allowed) {
            str = replacer(html, "", str); // Custom replace. No regexing
        }
    }
 
    return str;
}

/**
 * Highlights checked row, dehighlights unchecked row
 * @param string id ID of the row being highlighted
 * @param boolean checked The state of a checkbox controlling this row
 */
function highlight_row(id, checked) {
	var row = document.getElementById(id);
	if (row) {
		for (var i=1; row.childNodes.length > i; i++) { //from the second cell (the first comntains ordering controls)
               if (row.childNodes[i].className) {
                   if (checked) {
                       row.childNodes[i].className += ' highlight';
                   }
                   else {
                       row.childNodes[i].className = row.childNodes[i].className.replace(' highlight', '');
                   }
               }
		}
	}
}

/**
 * Center an element in the middle of a screen
 *
 * @param integer width Element width
 * @param integer height Element height
 * @param string div_id
 */
function show_in_center(width, height, div_id) { 
    // First, determine how much the visitor has scrolled 
    var scrolled_x, scrolled_y; 
    if (self.pageYOffset) { 
        scrolled_x = self.pageXOffset; 
        scrolled_y = self.pageYOffset; 
    }
    else if (document.documentElement && document.documentElement.scrollTop) { 
        scrolled_x = document.documentElement.scrollLeft; 
        scrolled_y = document.documentElement.scrollTop; 
    }
    else if (document.body) { 
        scrolled_x = document.body.scrollLeft; 
        scrolled_y = document.body.scrollTop; 
    } 
    
    // Next, determine the coordinates of browser's window 
    var center_x, center_y; 
    if (self.innerHeight) { 
        center_x = self.innerWidth; 
        center_y = self.innerHeight; 
    }
    else if (document.documentElement && document.documentElement.clientHeight) { 
        center_x = document.documentElement.clientWidth; 
        center_y = document.documentElement.clientHeight; 
    }
    else if (document.body) { 
        center_x = document.body.clientWidth; 
        center_y = document.body.clientHeight; 
    } 
    

    var left_offset = scrolled_x + (center_x - width) / 2; 
    var top_offset = (scrolled_y + (center_y - height) / 2) - 200;  

    var element = document.getElementById(div_id); 
    var element_style = element.style; 
    element_style.position = 'absolute'; 
    element_style.top = top_offset + 'px'; 
    element_style.left = left_offset + 'px'; 
    element_style.display = "block"; 
}

/**
 * Open new popup window and center in the middle of the screen.
 *
 * @param int size_x defines width of the window
 * @param int size_y defines height of the window
 * @param string window_name defines name of the window for reference purposes
 * @param string window_link defines link which should be open in popup window
 * @param string params Popup parameters
 */
function open_window_in_center(size_x, size_y, window_name, window_link, params) {
	//default arguments
	params = (typeof(params) != 'undefined' && params != '') ? params : 'resizable=1,scrollbars=1';
	
    window.open(window_link, 
                window_name, 
                params + ',width=' + size_x + 'px,height=' + size_y + 'px,left=' 
                + ((window.innerWidth == undefined) ? 
                        Math.round((document.documentElement.clientWidth - size_x) / 2) : 
                        Math.round((window.innerWidth - size_x) / 2)) 
                + 'px,top='
                + ((window.innerHeight == undefined) ? 
                        Math.round((document.documentElement.clientHeight - size_y) / 2) : 
                        Math.round((window.innerHeight - size_y) / 2))
                + 'px');    
}

/**
 * Get horizontal scroll offset
 * @return {integer}
 */
function get_scroll_top() {
	if (window.pageYOffset)
		return window.pageYOffset;
	if (document.documentElement)
		if (document.documentElement.scrollTop)
			return document.documentElement.scrollTop;
	if (document.body)
		if (document.body.scrollTop)
			return document.body.scrollTop;
			
	return 0;
}

/**
 * Get absolute x position of an object
 * @return {number}
 */
function get_absolute_x(obj) {
    var curleft = 0;
    if (obj.offsetParent) {
    	while (1) {
        	curleft += obj.offsetLeft;
          	if(!obj.offsetParent)
            	break;
          	obj = obj.offsetParent;
        }
    }
    else if (obj.x) {
        curleft += obj.x;
    }
    return curleft;
}