
function extend(Child, Parent)
{
  var F = function() { };
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Child.superclass = Parent.prototype;
}

//-----------------------------------------------------------------------------

function getRadioGroupValue(radioGroupObj)
{
	for (var i=0; i < radioGroupObj.length; i++)
		if (radioGroupObj[i].checked)
            return radioGroupObj[i].value;
  return null;
}

function setRadioGroupValue(radioGroupObj, value)
{
	for(var i=0; i < radioGroupObj.length; i++)
		if (radioGroupObj[i].value == value) {
			radioGroupObj[i].checked = true;
			return true;
		}
	return false;
}


var checkEmail = function(value)
{
	return /(\w|[-_])+@(\w|[-_])+\.[a-z]{2,4}/.test(value);
}


var timeDurationText = function(seconds)
{
	var hours = parseInt(seconds/3600);
	var mins = parseInt(seconds/60%60);
	var secs = parseInt(seconds%60);
	if (String(hours).length == 1) hours="0" + hours;
	if (String(mins).length == 1)  mins="0" + mins;
	if (String(secs).length == 1)  secs="0" + secs;
	var text = hours + ":" + mins + ":" + secs ;
	return text;
}



var esc = function(text)
{
	
	if (! text || typeof text != "string") return text;
	text = text.replace(/\\/g,"\\\\");
	text = text.replace(/\"/g,"\\\"");
	//text = text.replace(/\'/g,"\\\'");
	text = text.replace(/\'/g,"''"); //FIX
	return text;
}

function getBounds(element)
{
  var left = element.offsetLeft;
  var top = element.offsetTop;
  for (var parent = element.offsetParent; parent; parent = parent.offsetParent)
  {
    //debug.write(parent.scrollLeft + " " + parent.scrollTop);
		left += parent.offsetLeft - parent.scrollLeft;
    top += parent.offsetTop - parent.scrollTop;
  }
  return {left: left, top: top, width: element.offsetWidth, height: element.offsetHeight};
}

function getScrollDocumentWindow() {
      var x = y = 0;
      // Gecko поддерживает свойства scrollX(scrollY)
      // Для IE & Opera приходится идти в обход
      x = (window.scrollX) ? window.scrollX : document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
      y = y = (window.scrollY) ? window.scrollY : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
      return {x:x, y:y};
}


//-----------------------Debug---------------------------------------

var Debug = function(elem)
{
  this.elem = $(elem);
}

Debug.prototype.write = function(text)
{
  this.elem.innerHTML =  text + "<br/>" + this.elem.innerHTML;
}

Debug.prototype.clear = function()
{
  this.elem.innerHTML = "";
}
//-------------------------------------------------------------------

Function.prototype.bind = function(object) {
    var method = this
    return function() {
        return method.apply(object, arguments)
    }
}

///Add indexOf in Array (problem with IE)
if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
	        if(this[i]==obj)
                return i;
	    }
        return -1;
    }
}

Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

///



function containsDOM (container, containee)
{
    var isParent = false;
    do
    {
        if ((isParent = container == containee))
            break;
        try {containee = containee.parentNode;} catch(e) {return true;}
    }
    while (containee != null);
    return isParent;
}

function checkMouseOver (element, evt)
{
  if (element.contains && evt.fromElement)
  {
    return !element.contains(evt.fromElement);
  }
  else
    if (evt.relatedTarget)
    {
        return !containsDOM(element, evt.relatedTarget);
    }
}

function checkMouseOut (element, evt)
{
  if (element.contains && evt.toElement)
  {
    return !element.contains(evt.toElement);
  }
  else
    if (evt.relatedTarget)
    {
        return !containsDOM(element, evt.relatedTarget);
    }
}

//Размер клиентской области окна по горизонтали
function getClientWidth()
{
    var w=0;
    if (document.body)
    {
        w = Math.min(document.body.clientWidth, document.body.scrollWidth);
    }
    return w;
}

//Размер клиентской области окна по вертикали
function getClientHeight()
{
    var h=0;
    if (self.innerHeight) h = self.innerHeight;
    else if (document.documentElement && document.documentElement.clientHeight) h = document.documentElement.clientHeight;
    else if (document.body) h = document.body.clientHeight;

    return h;
}


function findPos(obj)
{
    if (!obj)
    {
         return [0,0];
    }
    var curleft = curtop = 0;
    if (obj.offsetParent)
    {
        curleft = obj.offsetLeft;
        curtop = obj.offsetTop;
        while (obj = obj.offsetParent)
        {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        }
    }
    return [curleft,curtop];
}

function $(elem)
{
	return document.getElementById(elem);
}

function $_(elem)
{

		if(elem && elem.parentNode)
    {
        return(elem.parentNode.removeChild(elem));
    }
    return(false);
}

function $$(tag)
{
    return document.createElement(tag);
}

function roundX(x)
{
    return Math.floor(x/gridSizeX)*gridSizeX;
}

function roundY(y)
{
    return Math.floor(y/gridSizeY)*gridSizeY;
}

function getMouseXY(e)
{
    var x = 0, y = 0;
    if (!e) e = window.event;

    if (e.pageX || e.pageY){
        x = e.pageX;
        y = e.pageY;
    } else if (e.clientX || e.clientY){
        x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
        y = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
    }

    return {"x":x, "y":y};
}


//------------------------------Work with CSS-----------------------------------
function hasClassName(elem, cname)
{
    if (!elem) return false;
    var a =  elem.className.split(/\s+/);
	for (var i = 0; i < a.length; i++){
        if (a[i] == cname) return true;
    }
    return false;
}

function addClassName(elem, cname)
{
    if (!elem) return false;
    if (hasClassName(elem, cname)) return false;
    elem.className += ' ' + cname;
    return true;
}

function delClassName(elem, cname)
{
    if (!elem) return false;
    var a =  elem.className.split(/\s+/);
    var newName = '';
    for (var i = 0; i < a.length; i++)
    if (a[i] != cname) newName += ' '+a[i];
    elem.className = newName;
    return true;
}

function getElementsByClassName(className, tag, elm){
    var testClass = new RegExp("(^|\\s)" + className + "(\\s|$)");
    var tag = tag || "*";
    var elm = elm || document;
    var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
    var returnElements = [];
    var current;
    var length = elements.length;
    for(var i=0; i<length; i++){
        current = elements[i];
        if(testClass.test(current.className)){
            returnElements.push(current);
        }
    }
    return returnElements;
}

//--------------------------Work with Events------------------------------------
function addListener(elm,event,func)
{
	if(document.attachEvent){
        elm.attachEvent("on"+event, func);
    }
    else if (document.addEventListener){
    	elm.addEventListener(event, func, true);
    } else {
		eval(elm+".on"+event+"="+func);
    }
}

function removeListener(elm,event,func)
{
    if(document.detachEvent){
        elm.detachEvent("on"+event, func);
    }
	else if(document.removeEventListener){
      	elm.removeEventListener(event, func, true);
    } else {
       	eval(elm+".on"+event+"= function(){return false;}");
    }
}

function stopBubbleStopReturn(event)
{
		if (!event) return false;
    if(event.stopPropagation){
        event.stopPropagation();
        event.preventDefault();
    }
    else if (event.cancelBubble == false || event.returnValue == true)
    {
    	event.cancelBubble = true;
		event.returnValue = false;
    }
}


function isParent(child, parent) {
    if (!child || !parent) {
        return false;
    }
    while (true) {
        if (child == parent) {
            return true;
        }
        if (child.parentElement) {
            child = child.parentElement;
        } else if (child.parentNode) {
            child = child.parentNode;
        } else {
            return false;
        }
    }
}

function renderMoney(s){
	if (!s){return s};
	var t = s.split('.');
	var d;
	if (t.length == 2){d=t[0]; t='.'+t[1];}else{d=t[0]; t='';}
	if (d.length>3){
		var r='';
		for(i=0;i<d.length;i++){r = d.substring(d.length-i-1,d.length-i) + (i%3?'':' ') + r;}
		return r.substring(0,r.length-1) + t;
	}
	if (t.length == 2) t += '0';
	return d + t;
}

function renderBool(data)
{
	if(data=='True' || data=='true' || data==true){return '•';}
	return '';
}

function renderIntToBool(data)
{
	if(data=='0' || data==0){return '';}
	return '•';
}

function selectedText(inputElement)
{
	if (document.all) //IE
 	{
		var r = inputElement.createTextRange();
   	r.select();
	}
 	else
 	{
	 	inputElement.setSelectionRange(0,inputElement.value.length);
	}
}

function mask_element(element, text){
	if (element.divMask) return;
	element.divMask = $$("div");	
	element.divMask.className = "ext-el-mask";		
	element.maskText = $$("div");
	element.maskText.className = "mask-text";
	if (!text) 
		element.maskText.innerHTML = "...";
	else
		element.maskText.innerHTML = text;			 					
	element.appendChild(element.maskText);
	element.divMask.style.height = element.clientHeight;
	addClassName(element ,"x-masked");
	element.appendChild(element.divMask);		
	
	element.maskText.style.top = (element.divMask.offsetHeight - element.maskText.offsetHeight)/2;
	element.maskText.style.left = (element.divMask.offsetWidth - element.maskText.offsetWidth)/2;	
}

function unmask_element(element){
	if (!element.divMask) return; 
	delClassName(element,"x-masked");
	element.removeChild(element.divMask);
	element.removeChild(element.maskText);
	element.divMask = null;
	element.maskText = null;
}

function get_style(obj, property){
    if(window.getComputedStyle)
        return window.getComputedStyle(obj, null)[property];
    else
        if (obj.currentStyle)
            return obj.currentStyle[property];
        else
            return null;
            
}

var int = parseInt;
var float = parseFloat;

function common_fail(msg){
    // str -> (req -> IO)
    // generate fail callback for Request function with specified msg
    return function(req){
               if (req.status != 403) {
  	               alert(msg);
               }
    }
}	

function _common_success(silent, callback){
    // bool -> (req.resp -> *'a -> 'b) -> *'a -> (req -> 'b)
    // generates success callback for Request function 
    // if callback is specified, it will be called with given args
    var clbargs = _a(arguments).piece(2);
	return function(req){
        eval("var resp = " + req.responseText);
        if(!silent)
	        alert(resp.message.replace(/<br>/g, "\n"));
	    if (callback)
    	    return callback.apply(callback,
                                  concat([resp], clbargs));
    }
}
common_success = curry(_common_success, false);
silent_success = curry(_common_success, true);


function grid_success(grid, silent){
    var success = silent ? silent_success : common_success;
    return success(function(r, gr){
                       gr.loadForButton();
                   }, grid);
}

function grid_win_success(grid, win, silent){
    var success = silent ? silent_success : common_success;
    return success(function(r, gr, w){
                              gr.loadForButton();
                              w.hide();
                          }, grid, win);
}

function request(url, data, clb, args, silent, method){
    var args = args || [];
    var fmsg = "Не удалось совершить запрос!";
    var success = silent ? silent_success : common_success;
    var gclb = success.apply(this, concat([clb], args));
    var meth = method || "POST";
    Request(url, gclb, data, common_fail(fmsg), meth, this);
}


function gen_request_tuned(grid, url, param){
    var param = param || {};
    var rec_name = param.record || "id";
    var send_name = param.send || rec_name;
    var check_name = param.check || rec_name;
    var msg = param.msg;
    var silent = param.silent != undefined ? param.silent : false;
    var pred_and_msg = param.pred_and_msg;
    var confirm_msg = param.confirm_msg;
    return function(e){
        var selRow = grid.getSelectedRow();
        if(!selRow || !selRow[check_name]) {
            if(msg)
                alert(msg);
            return;
        }

        if(pred_and_msg){
            for(var i=0; i < pred_and_msg.length; i++){
                if(!pred_and_msg[i]["pred"](selRow)){
                    if(pred_and_msg[i].msg)
                        alert(pred_and_msg[i]["msg"]);
                    return;
                }
            }
        }

        if(confirm_msg)
            if(!confirm(confirm_msg))
                return
        
        
        var _o = {};
        _o[send_name] = selRow[rec_name];
        Request(url, grid_success(grid, silent), _o,
                common_fail("Не удалось совершить запрос!"), "POST", this);
    }
}

function gen_request(grid, url, name, msg){
    return gen_request_tuned(grid, url,
                             {'send':name, 'msg':msg});
}


function rengen(ds){
    return function(data){
       record = ds.getById(data);
       if(record)
           return record.name;
       else
           return data;
    };
}
gen_render = rengen;

/// XXX: group_load (group_event_load))  => возвращать прокси поверх переданных ДС
/// для возможности решения задачи `загрузить группу дс, а затем идти дальше`
/// возможное решение -- создавать счетчик ДС, и каждый afterLoad инрементит
/// его. если совпало ==> звать глобальный афтер лоад


function group_load(){
	var asd = _a(arguments);
	for (var i = 0; i < asd.length; i++)
	    asd[i].load();
}

function chained_load(){
	var asd = Array.prototype.slice.call(arguments);
	for (var i = 0, k=1; k < asd.length; i++, k++){
		asd[i].afterLoad = function(){
			asd[this.k].load();
		}
		asd[i].k = k;
	}
	asd[0].load();
}

function gen_combo(url, name, add_param, add_fields){
    var add_fields = add_fields || [];
    var add_param = add_param || {};
    var fields = [{'name': "id"},
                  {'name': "name"}
                 ];
    for(var i=0; i<len(add_fields); i++)
        fields.push({'name':add_fields[i]});
    
    var ds = new dataStore(new Proxy(url,"GET"),
                           new ArrayReader({},
                                           fields
                                          )
                          );
    ds.limit = null;
    return new comboBox(ounite({'store':ds,'name':name, 'emptyField':true}, add_param)); 
}

function gen_cbList(url, name, add_param){
    var add_param = add_param || {};
    var ds = new dataStore(new Proxy(url,"GET"),
                           new ArrayReader({},
                                           [{name: "id"},
                                            {name: "name"}
                                           ]
                                          )
                          );
    ds.limit = null;
    return new cbList(ounite({'store':ds,'name':name},add_param));
} 
 
function gen_window(grid, win_url, win_name, name, common_msg,
                    pred_and_msg, gen_param){
    var dummy = function(){return {}};
    gen_param = gen_param || dummy;
    return function(e){
        var selectedRow = grid.getSelectedRow();
        if(!selectedRow){
            alert(common_msg);
            return;
        }
        if(!selectedRow.id){
            alert(common_msg);
            return;
        }
        if(pred_and_msg){
            for(var i=0; i < pred_and_msg.length; i++){
                if(!pred_and_msg[i]["pred"](selectedRow)){
                    if(pred_and_msg[i].msg)
                        alert(pred_and_msg[i]["msg"]);
                    return;
                }
            }
        }
        var base = {};
        base[name] = selectedRow.id;
        var w = windowOpen(win_url, win_name, ounite(base, gen_param(selectedRow)), e);
    }    
}


function gen_modal(divid, maskobj, header){
    return new modalWindow(divid, maskobj,
                           {'header':header}, true);
}

function gen_fmodal(divid, header){
    return new modalWindow(divid, document.body,
                           {'header':header}, true);
}

function gen_grid(url, fields, cm, div_name, grid_param, upurl){
    var upurl = upurl || url.replace("get", "update");
    var grid_param = grid_param || {};
    var ds_fields = [];
    for (var i=0; i<fields.length; i++)
        ds_fields.push({'name':fields[i]});

	var ds = new dataStore(new Proxy(url, "GET"),
						           new JSONReader({root: "rows",
                                                   totalProperty:"totalCount",
                                                   id:"id"},
	                                              ds_fields));
    	
	var cm = new columnModel(cm);	
	var grid = new Grid(ds, cm, div_name, [], grid_param);
	grid.url = upurl;
    return grid;
}

function gen_minigrid(url, fields, cm, div_name, grid_param, upurl){
    var upurl = upurl || url.replace("get", "update");
    var grid_param = grid_param || {};
    var ds_fields = [];
    for (var i=0; i<fields.length; i++)
        ds_fields.push({'name':fields[i]});

	var ds = new dataStore(new Proxy(url, "GET"),
						           new JSONReader({root: "rows",
                                                   totalProperty:"totalCount",
                                                   id:"id"},
	                                              ds_fields));
    	
	var cm = new columnModel(cm);	
	var grid = new miniGrid(ds, cm, div_name, [], grid_param);
	grid.url = upurl;
    return grid;
}

function gen_memcombo(pairs, name, add_param){
    var add_param = add_param || {};
    var ds = new dataStore(new MemoryProxy(pairs),
                           new ArrayReader({},[
                               {name: "id"},
                               {name: "name"}
                           ]
                                          )
                          );
    return new comboBox(ounite({'store':ds,'name':name, 'emptyField':true}, add_param)); 
}

function gen_calendar(name, param){
    return new calendarField(ounite({'name':name}, (param || {})));
}

function gen_text(param){
    return new textField(param || {});
}

function gen_checkbox(param){
    return new checkBox(param || {});
}

function gen_array_ds(url, fields){
    var ds_fields = [];
    for (var i=0; i<fields.length; i++)
        ds_fields.push({'name':fields[i]});
    var ds = new dataStore(new Proxy(url, "GET"),
                           new ArrayReader({}, ds_fields));
    return ds;
}

function gen_json_ds(url, fields){
    var ds_fields = [];
    for (var i=0; i<fields.length; i++)
        ds_fields.push({'name':fields[i]});

	var ds = new dataStore(new Proxy(url, "GET"),
						           new JSONReader({root: "rows",
                                                   totalProperty:"totalCount",
                                                   id:"id"},
	                                              ds_fields));
    	
    return ds;
}

function copycb(cb, updates){
    var updates = updates || {};
    return new comboBox(ounite({'store':cb.ds, 'emptyField':true,
                                'name':cb.name, 'width':cb.width}, updates));
}

function gen_http_ds(url, onload){
    var ds =  new dataStore(new Proxy(url, "GET"), 
						    new HttpReader({},[]));
    ds.afterLoad = onload;
    return ds;
}

function load_html(url, div_id, data, method, callback, args){
    var method = method || "GET";
    var data = data || {};
    
    var div = typeof(div_id)=="string" ? $(div_id) : div_id;

    var clb = function(resp){
        var html = resp.responseText;
        
        div.innerHTML = html;

        if(callback)
            callback.apply(callback, args);
        
    }
    Request(url, clb, data, common_fail("Не удалось совершить запрос!"),
            method, this);
    return div;
}

function load_html_script(url, div_id, data, method, callback, args){
    var args = args || [];
    var method = method || "GET";
    var data = data || {};
    
    var div = typeof(div_id)=="string" ? $(div_id) : div_id;
    var clb = function(resp){
        var data = resp.responseText.split("|<html|js>|")
        var html = data[0];
        var script = data[1];    
        
        div.innerHTML = html;
        try{
            var el = document.createElement('script');
            el.type="text/javascript";
            el.innerHTML = script;
            div.appendChild(el);
        }catch(e){ // dirty hack for IE
            var el = document.createElement('span');
            el.innerHTML = "<br /><scr"+"ipt type='text/javascript' defer='defer'>"+script+"</script" + ">";
            div.appendChild(el);
        }

        // from jquery
        var done = false;
        // Attach handlers for all browsers
        script.onload = script.onreadystatechange = function(){
            if (!done && (!this.readyState ||
                          this.readyState == "loaded" ||
                          this.readyState == "complete")
               ){
                done = true;

                // Handle memory leak in IE
                script.onload = script.onreadystatechange = null;
            }
        };
     
        if(callback)
            callback.apply(callback, args);
    }

    Request(url, clb, data, common_fail("Не удалось совершить запрос!"),
            method, this);
    return div;   
}


function now(){
    var now_y_m = new Date();
    var now_str = now_y_m.getFullYear();
    if (now_y_m.getMonth() < 9)
        now_str += '-0' + (now_y_m.getMonth() + 1);
    else
        now_str += '-' + (now_y_m.getMonth() + 1);
    
    if (now_y_m.getDate() < 9)
        now_str += '-0' + (now_y_m.getDate());
    else
        now_str += '-' + (now_y_m.getDate());
    return now_str;
}

function falert(msg){
    return function(){
        alert(msg);
    }
}

function unbr(st){
    return st.replace(/<br>/g, '\n');
}

function $t(txt){
    return document.createTextNode(txt);
}

var cbox = function(value,checked)
{
    this.cb=$$("input");
    this.cb.type="checkbox"
    this.cb.value=value;
    return this.cb
}

function shower(stype){
    return function(el){
        el.style.display = stype;
    }
}

function hider(){
    return function(el){
        el.style.display = "none";
    }
}

function show_el(el, stype){
    el.style.display = stype || '';
}

function hide_el(el){
    el.style.display = "none";
}

// return version of IE; if not ie then -1 
function getInternetExplorerVersion()
{
    var rv = -1; // Return value assumes failure.
    if (navigator.appName == 'Microsoft Internet Explorer')
        {
            var ua = navigator.userAgent;
            var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
            if (re.exec(ua) != null)
                rv = parseFloat( RegExp.$1 );
        }
    return rv;
}


function getScrollTop()
{
  return window.pageYOffset || document.documentElement.scrollTop || 0;
}


function getBrowser() {
    var sBrowser = navigator.userAgent;
    if (sBrowser.toLowerCase().indexOf('msie') > -1) return 'ie';
    else if (sBrowser.toLowerCase().indexOf('firefox') > -1) return 'firefox';
    else return 'mozilla';
}

