2576 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2576 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| Product Name: dhtmlxSuite 
 | |
| Version: 4.0.3 
 | |
| Edition: Professional 
 | |
| License: content of this file is covered by DHTMLX Commercial or Enterprise license. Usage without proper license is prohibited. To obtain it contact sales@dhtmlx.com
 | |
| Copyright UAB Dinamenta http://www.dhtmlx.com
 | |
| */
 | |
| 
 | |
| /*
 | |
| Copyright DHTMLX LTD. http://www.dhtmlx.com
 | |
| You allowed to use this component or parts of it under GPL terms
 | |
| To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com
 | |
| */
 | |
| /*
 | |
| 2014 March 19
 | |
| */
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'assert.js'*/
 | |
| 
 | |
| 
 | |
| if (!window.dhtmlx) 
 | |
| 	dhtmlx={};
 | |
| 
 | |
| //check some rule, show message as error if rule is not correct
 | |
| dhtmlx.assert = function(test, message){
 | |
| 	if (!test)	dhtmlx.error(message);
 | |
| };
 | |
| dhtmlx.assert_enabled=function(){ return false; };
 | |
| 
 | |
| //register names of event, which can be triggered by the object
 | |
| dhtmlx.assert_event = function(obj, evs){
 | |
| 	if (!obj._event_check){
 | |
| 		obj._event_check = {};
 | |
| 		obj._event_check_size = {};
 | |
| 	}
 | |
| 		
 | |
| 	for (var a in evs){
 | |
| 		obj._event_check[a.toLowerCase()]=evs[a];
 | |
| 		var count=-1; for (var t in evs[a]) count++;
 | |
| 		obj._event_check_size[a.toLowerCase()]=count;
 | |
| 	}
 | |
| };
 | |
| dhtmlx.assert_method_info=function(obj, name, descr, rules){
 | |
| 	var args = [];
 | |
| 	for (var i=0; i < rules.length; i++) {
 | |
| 		args.push(rules[i][0]+" : "+rules[i][1]+"\n   "+rules[i][2].describe()+(rules[i][3]?"; optional":""));
 | |
| 	}
 | |
| 	return obj.name+"."+name+"\n"+descr+"\n Arguments:\n - "+args.join("\n - ");
 | |
| };
 | |
| dhtmlx.assert_method = function(obj, config){
 | |
| 	for (var key in config)
 | |
| 		dhtmlx.assert_method_process(obj, key, config[key].descr, config[key].args, (config[key].min||99), config[key].skip);
 | |
| };
 | |
| dhtmlx.assert_method_process = function (obj, name, descr, rules, min, skip){
 | |
| 	var old = obj[name];
 | |
| 	if (!skip)
 | |
| 		obj[name] = function(){
 | |
| 			if (arguments.length !=	rules.length && arguments.length < min) 
 | |
| 				dhtmlx.log("warn","Incorrect count of parameters\n"+obj[name].describe()+"\n\nExpecting "+rules.length+" but have only "+arguments.length);
 | |
| 			else
 | |
| 				for (var i=0; i<rules.length; i++)
 | |
| 					if (!rules[i][3] && !rules[i][2](arguments[i]))
 | |
| 						dhtmlx.log("warn","Incorrect method call\n"+obj[name].describe()+"\n\nActual value of "+(i+1)+" parameter: {"+(typeof arguments[i])+"} "+arguments[i]);
 | |
| 			
 | |
| 			return old.apply(this, arguments);
 | |
| 		};
 | |
| 	obj[name].describe = function(){	return dhtmlx.assert_method_info(obj, name, descr, rules);	};
 | |
| };
 | |
| dhtmlx.assert_event_call = function(obj, name, args){
 | |
| 	if (obj._event_check){
 | |
| 		if (!obj._event_check[name])
 | |
| 			dhtmlx.log("warn","Not expected event call :"+name);
 | |
| 		else if (dhtmlx.isNotDefined(args))
 | |
| 			dhtmlx.log("warn","Event without parameters :"+name);
 | |
| 		else if (obj._event_check_size[name] != args.length)
 | |
| 			dhtmlx.log("warn","Incorrect event call, expected "+obj._event_check_size[name]+" parameter(s), but have "+args.length +" parameter(s), for "+name+" event");
 | |
| 	}		
 | |
| };
 | |
| dhtmlx.assert_event_attach = function(obj, name){
 | |
| 	if (obj._event_check && !obj._event_check[name]) 
 | |
| 			dhtmlx.log("warn","Unknown event name: "+name);
 | |
| };
 | |
| //register names of properties, which can be used in object's configuration
 | |
| dhtmlx.assert_property = function(obj, evs){
 | |
| 	if (!obj._settings_check)
 | |
| 		obj._settings_check={};
 | |
| 	dhtmlx.extend(obj._settings_check, evs);		
 | |
| };
 | |
| //check all options in collection, against list of allowed properties
 | |
| dhtmlx.assert_check = function(data,coll){
 | |
| 	if (typeof data == "object"){
 | |
| 		for (var key in data){
 | |
| 			dhtmlx.assert_settings(key,data[key],coll);
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| //check if type and value of property is the same as in scheme
 | |
| dhtmlx.assert_settings = function(mode,value,coll){
 | |
| 	coll = coll || this._settings_check;
 | |
| 
 | |
| 	//if value is not in collection of defined ones
 | |
| 	if (coll){
 | |
| 		if (!coll[mode])	//not registered property
 | |
| 			return dhtmlx.log("warn","Unknown propery: "+mode);
 | |
| 			
 | |
| 		var descr = "";
 | |
| 		var error = "";
 | |
| 		var check = false;
 | |
| 		for (var i=0; i<coll[mode].length; i++){
 | |
| 			var rule = coll[mode][i];
 | |
| 			if (typeof rule == "string")
 | |
| 				continue;
 | |
| 			if (typeof rule == "function")
 | |
| 				check = check || rule(value);
 | |
| 			else if (typeof rule == "object" && typeof rule[1] == "function"){
 | |
| 				check = check || rule[1](value);
 | |
| 				if (check && rule[2])
 | |
| 					dhtmlx["assert_check"](value, rule[2]); //temporary fix , for sources generator
 | |
| 			}
 | |
| 			if (check) break;
 | |
| 		}
 | |
| 		if (!check )
 | |
| 			dhtmlx.log("warn","Invalid configuration\n"+dhtmlx.assert_info(mode,coll)+"\nActual value: {"+(typeof value)+"} "+value);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.assert_info=function(name, set){ 
 | |
| 	var ruleset = set[name];
 | |
| 	var descr = "";
 | |
| 	var expected = [];
 | |
| 	for (var i=0; i<ruleset.length; i++){
 | |
| 		if (typeof rule == "string")
 | |
| 			descr = ruleset[i];
 | |
| 		else if (ruleset[i].describe)
 | |
| 			expected.push(ruleset[i].describe());
 | |
| 		else if (ruleset[i][1] && ruleset[i][1].describe)
 | |
| 			expected.push(ruleset[i][1].describe());
 | |
| 	}
 | |
| 	return "Property: "+name+", "+descr+" \nExpected value: \n - "+expected.join("\n - ");
 | |
| };
 | |
| 
 | |
| 
 | |
| if (dhtmlx.assert_enabled()){
 | |
| 	
 | |
| 	dhtmlx.assert_rule_color=function(check){
 | |
| 		if (typeof check != "string") return false;
 | |
| 		if (check.indexOf("#")!==0) return false;
 | |
| 		if (check.substr(1).replace(/[0-9A-F]/gi,"")!=="") return false;
 | |
| 		return true;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_color.describe = function(){
 | |
| 		return "{String} Value must start from # and contain hexadecimal code of color";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_template=function(check){
 | |
| 		if (typeof check == "function") return true;
 | |
| 		if (typeof check == "string") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_template.describe = function(){
 | |
| 		return "{Function},{String} Value must be a function which accepts data object and return text string, or a sting with optional template markers";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_boolean=function(check){
 | |
| 		if (typeof check == "boolean") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_boolean.describe = function(){
 | |
| 		return "{Boolean} true or false";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_object=function(check, sub){
 | |
| 		if (typeof check == "object") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_object.describe = function(){
 | |
| 		return "{Object} Configuration object";
 | |
| 	};
 | |
| 	
 | |
| 	
 | |
| 	dhtmlx.assert_rule_string=function(check){
 | |
| 		if (typeof check == "string") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_string.describe = function(){
 | |
| 		return "{String} Plain string";
 | |
| 	};
 | |
| 	
 | |
| 	
 | |
| 	dhtmlx.assert_rule_htmlpt=function(check){
 | |
| 		return !!dhtmlx.toNode(check);
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_htmlpt.describe = function(){
 | |
| 		return "{Object},{String} HTML node or ID of HTML Node";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_notdocumented=function(check){
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_notdocumented.describe = function(){
 | |
| 		return "This options wasn't documented";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_key=function(obj){
 | |
| 		var t = function (check){
 | |
| 			return obj[check];
 | |
| 		};
 | |
| 		t.describe=function(){
 | |
| 			var opts = [];
 | |
| 			for(var key in obj)
 | |
| 				opts.push(key);
 | |
| 			return  "{String} can take one of next values: "+opts.join(", ");
 | |
| 		};
 | |
| 		return t;
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_dimension=function(check){
 | |
| 		if (check*1 == check && !isNaN(check) && check >= 0) return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_dimension.describe=function(){
 | |
| 		return "{Integer} value must be a positive number";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_number=function(check){
 | |
| 		if (typeof check == "number") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_number.describe=function(){
 | |
| 		return "{Integer} value must be a number";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_function=function(check){
 | |
| 		if (typeof check == "function") return true;
 | |
| 		return false;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_function.describe=function(){
 | |
| 		return "{Function} value must be a custom function";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_any=function(check){
 | |
| 		return true;
 | |
| 	};
 | |
| 	dhtmlx.assert_rule_any.describe=function(){
 | |
| 		return "Any value";
 | |
| 	};
 | |
| 	
 | |
| 	dhtmlx.assert_rule_mix=function(a,b){
 | |
| 		var t = function(check){
 | |
| 			if (a(check)||b(check)) return true;
 | |
| 			return false;
 | |
| 		};
 | |
| 		t.describe = function(){
 | |
| 			return a.describe();
 | |
| 		};
 | |
| 		return t;
 | |
| 	};
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'dhtmlx.js'*/
 | |
| 
 | |
| 
 | |
| /*DHX:Depend assert.js*/
 | |
| 
 | |
| /*
 | |
| 	Common helpers
 | |
| */
 | |
| dhtmlx.version="3.0";
 | |
| dhtmlx.codebase="./";
 | |
| 
 | |
| //coding helpers
 | |
| 
 | |
| dhtmlx.copy = function(source){
 | |
| 	var f = dhtmlx.copy._function;
 | |
| 	f.prototype = source;
 | |
| 	return new f();
 | |
| };
 | |
| dhtmlx.copy._function = function(){};
 | |
| 
 | |
| //copies methods and properties from source to the target
 | |
| dhtmlx.extend = function(target, source){
 | |
| 	for (var method in source)
 | |
| 		target[method] = source[method];
 | |
| 		
 | |
| 	//applying asserts
 | |
| 	if (dhtmlx.assert_enabled() && source._assert){
 | |
| 		target._assert();
 | |
| 		target._assert=null;
 | |
| 	}
 | |
| 	
 | |
| 	dhtmlx.assert(target,"Invalid nesting target");
 | |
| 	dhtmlx.assert(source,"Invalid nesting source");
 | |
| 	//if source object has init code - call init against target
 | |
| 	if (source._init)	
 | |
| 		target._init();
 | |
| 				
 | |
| 	return target;	
 | |
| };
 | |
| dhtmlx.proto_extend = function(){
 | |
| 	var origins = arguments;
 | |
| 	var compilation = origins[0];
 | |
| 	var construct = [];
 | |
| 	
 | |
| 	for (var i=origins.length-1; i>0; i--) {
 | |
| 		if (typeof origins[i]== "function")
 | |
| 			origins[i]=origins[i].prototype;
 | |
| 		for (var key in origins[i]){
 | |
| 			if (key == "_init") 
 | |
| 				construct.push(origins[i][key]);
 | |
| 			else if (!compilation[key])
 | |
| 				compilation[key] = origins[i][key];
 | |
| 		}
 | |
| 	};
 | |
| 	
 | |
| 	if (origins[0]._init)
 | |
| 		construct.push(origins[0]._init);
 | |
| 	
 | |
| 	compilation._init = function(){
 | |
| 		for (var i=0; i<construct.length; i++)
 | |
| 			construct[i].apply(this, arguments);
 | |
| 	};
 | |
| 	compilation.base = origins[1];
 | |
| 	var result = function(config){
 | |
| 		this._init(config);
 | |
| 		if (this._parseSettings)
 | |
| 			this._parseSettings(config, this.defaults);
 | |
| 	};
 | |
| 	result.prototype = compilation;
 | |
| 	
 | |
| 	compilation = origins = null;
 | |
| 	return result;
 | |
| };
 | |
| //creates function with specified "this" pointer
 | |
| dhtmlx.bind=function(functor, object){ 
 | |
| 	return function(){ return functor.apply(object,arguments); };  
 | |
| };
 | |
| 
 | |
| //loads module from external js file
 | |
| dhtmlx.require=function(module){
 | |
| 	if (!dhtmlx._modules[module]){
 | |
| 		dhtmlx.assert(dhtmlx.ajax,"load module is required");
 | |
| 		
 | |
| 		//load and exec the required module
 | |
| 		dhtmlx.exec( dhtmlx.ajax().sync().get(dhtmlx.codebase+module).responseText );
 | |
| 		dhtmlx._modules[module]=true;	
 | |
| 	}
 | |
| };
 | |
| dhtmlx._modules = {};	//hash of already loaded modules
 | |
| 
 | |
| //evaluate javascript code in the global scoope
 | |
| dhtmlx.exec=function(code){
 | |
| 	if (window.execScript)	//special handling for IE
 | |
| 		window.execScript(code);
 | |
| 	else window.eval(code);
 | |
| };
 | |
| 
 | |
| /*
 | |
| 	creates method in the target object which will transfer call to the source object
 | |
| 	if event parameter was provided , each call of method will generate onBefore and onAfter events
 | |
| */
 | |
| dhtmlx.methodPush=function(object,method,event){
 | |
| 	return function(){
 | |
| 		var res = false;
 | |
| 		//if (!event || this.callEvent("onBefore"+event,arguments)){ //not used anymore, probably can be removed
 | |
| 			res=object[method].apply(object,arguments);
 | |
| 		//	if (event) this.callEvent("onAfter"+event,arguments);
 | |
| 		//}
 | |
| 		return res;	//result of wrapped method
 | |
| 	};
 | |
| };
 | |
| //check === undefined
 | |
| dhtmlx.isNotDefined=function(a){
 | |
| 	return typeof a == "undefined";
 | |
| };
 | |
| //delay call to after-render time
 | |
| dhtmlx.delay=function(method, obj, params, delay){
 | |
| 	setTimeout(function(){
 | |
| 		var ret = method.apply(obj,params);
 | |
| 		method = obj = params = null;
 | |
| 		return ret;
 | |
| 	},delay||1);
 | |
| };
 | |
| 
 | |
| //common helpers
 | |
| 
 | |
| //generates unique ID (unique per window, nog GUID)
 | |
| dhtmlx.uid = function(){
 | |
| 	if (!this._seed) this._seed=(new Date).valueOf();	//init seed with timestemp
 | |
| 	this._seed++;
 | |
| 	return this._seed;
 | |
| };
 | |
| //resolve ID as html object
 | |
| dhtmlx.toNode = function(node){
 | |
| 	if (typeof node == "string") return document.getElementById(node);
 | |
| 	return node;
 | |
| };
 | |
| //adds extra methods for the array
 | |
| dhtmlx.toArray = function(array){ 
 | |
| 	return dhtmlx.extend((array||[]),dhtmlx.PowerArray);
 | |
| };
 | |
| //resolve function name
 | |
| dhtmlx.toFunctor=function(str){ 
 | |
| 	return (typeof(str)=="string") ? eval(str) : str; 
 | |
| };
 | |
| 
 | |
| //dom helpers
 | |
| 
 | |
| //hash of attached events
 | |
| dhtmlx._events = {};
 | |
| //attach event to the DOM element
 | |
| dhtmlx.event=function(node,event,handler,master){
 | |
| 	node = dhtmlx.toNode(node);
 | |
| 	
 | |
| 	var id = dhtmlx.uid();
 | |
| 	dhtmlx._events[id]=[node,event,handler];	//store event info, for detaching
 | |
| 	
 | |
| 	if (master) 
 | |
| 		handler=dhtmlx.bind(handler,master);	
 | |
| 		
 | |
| 	//use IE's of FF's way of event's attaching
 | |
| 	if (node.addEventListener)
 | |
| 		node.addEventListener(event, handler, false);
 | |
| 	else if (node.attachEvent)
 | |
| 		node.attachEvent("on"+event, handler);
 | |
| 
 | |
| 	return id;	//return id of newly created event, can be used in eventRemove
 | |
| };
 | |
| 
 | |
| //remove previously attached event
 | |
| dhtmlx.eventRemove=function(id){
 | |
| 	
 | |
| 	if (!id) return;
 | |
| 	dhtmlx.assert(this._events[id],"Removing non-existing event");
 | |
| 		
 | |
| 	var ev = dhtmlx._events[id];
 | |
| 	//browser specific event removing
 | |
| 	if (ev[0].removeEventListener)
 | |
| 		ev[0].removeEventListener(ev[1],ev[2],false);
 | |
| 	else if (ev[0].detachEvent)
 | |
| 		ev[0].detachEvent("on"+ev[1],ev[2]);
 | |
| 		
 | |
| 	delete this._events[id];	//delete all traces
 | |
| };
 | |
| 
 | |
| 
 | |
| //debugger helpers
 | |
| //anything starting from error or log will be removed during code compression
 | |
| 
 | |
| //add message in the log
 | |
| dhtmlx.log = function(type,message,details){
 | |
| 	if (window.console && console.log){
 | |
| 		type=type.toLowerCase();
 | |
| 		if (window.console[type])
 | |
| 			window.console[type](message||"unknown error");
 | |
| 		else
 | |
| 			window.console.log(type +": "+message);
 | |
| 		if (details) 
 | |
| 			window.console.log(details);
 | |
| 	}	
 | |
| };
 | |
| //register rendering time from call point 
 | |
| dhtmlx.log_full_time = function(name){
 | |
| 	dhtmlx._start_time_log = new Date();
 | |
| 	dhtmlx.log("Info","Timing start ["+name+"]");
 | |
| 	window.setTimeout(function(){
 | |
| 		var time = new Date();
 | |
| 		dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx._start_time_log.valueOf())/1000+"s");
 | |
| 	},1);
 | |
| };
 | |
| //register execution time from call point
 | |
| dhtmlx.log_time = function(name){
 | |
| 	var fname = "_start_time_log"+name;
 | |
| 	if (!dhtmlx[fname]){
 | |
| 		dhtmlx[fname] = new Date();
 | |
| 		dhtmlx.log("Info","Timing start ["+name+"]");
 | |
| 	} else {
 | |
| 		var time = new Date();
 | |
| 		dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx[fname].valueOf())/1000+"s");
 | |
| 		dhtmlx[fname] = null;
 | |
| 	}
 | |
| };
 | |
| //log message with type=error
 | |
| dhtmlx.error = function(message,details){
 | |
| 	dhtmlx.log("error",message,details);
 | |
| };
 | |
| //event system
 | |
| dhtmlx.EventSystem={
 | |
| 	_init:function(){
 | |
| 		this._events = {};		//hash of event handlers, name => handler
 | |
| 		this._handlers = {};	//hash of event handlers, ID => handler
 | |
| 		this._map = {};
 | |
| 	},
 | |
| 	//temporary block event triggering
 | |
| 	block : function(){
 | |
| 		this._events._block = true;
 | |
| 	},
 | |
| 	//re-enable event triggering
 | |
| 	unblock : function(){
 | |
| 		this._events._block = false;
 | |
| 	},
 | |
| 	mapEvent:function(map){
 | |
| 		dhtmlx.extend(this._map, map);
 | |
| 	},
 | |
| 	//trigger event
 | |
| 	callEvent:function(type,params){
 | |
| 		if (this._events._block) return true;
 | |
| 		
 | |
| 		type = type.toLowerCase();
 | |
| 		dhtmlx.assert_event_call(this, type, params);
 | |
| 		
 | |
| 		var event_stack =this._events[type.toLowerCase()];	//all events for provided name
 | |
| 		var return_value = true;
 | |
| 
 | |
| 		if (dhtmlx.debug)	//can slowdown a lot
 | |
| 			dhtmlx.log("info","["+this.name+"] event:"+type,params);
 | |
| 		
 | |
| 		if (event_stack)
 | |
| 			for(var i=0; i<event_stack.length; i++)
 | |
| 				/*
 | |
| 					Call events one by one
 | |
| 					If any event return false - result of whole event will be false
 | |
| 					Handlers which are not returning anything - counted as positive
 | |
| 				*/
 | |
| 				if (event_stack[i].apply(this,(params||[]))===false) return_value=false;
 | |
| 				
 | |
| 		if (this._map[type] && !this._map[type].callEvent(type,params))
 | |
| 			return_value =	false;
 | |
| 			
 | |
| 		return return_value;
 | |
| 	},
 | |
| 	//assign handler for some named event
 | |
| 	attachEvent:function(type,functor,id){
 | |
| 		type=type.toLowerCase();
 | |
| 		dhtmlx.assert_event_attach(this, type);
 | |
| 		
 | |
| 		id=id||dhtmlx.uid(); //ID can be used for detachEvent
 | |
| 		functor = dhtmlx.toFunctor(functor);	//functor can be a name of method
 | |
| 
 | |
| 		var event_stack=this._events[type]||dhtmlx.toArray();
 | |
| 		//save new event handler
 | |
| 		event_stack.push(functor);
 | |
| 		this._events[type]=event_stack;
 | |
| 		this._handlers[id]={ f:functor,t:type };
 | |
| 		
 | |
| 		return id;
 | |
| 	},
 | |
| 	//remove event handler
 | |
| 	detachEvent:function(id){
 | |
| 		if(this._handlers[id]){
 | |
| 			var type=this._handlers[id].t;
 | |
| 			var functor=this._handlers[id].f;
 | |
| 			
 | |
| 			//remove from all collections
 | |
| 			var event_stack=this._events[type];
 | |
| 			event_stack.remove(functor);
 | |
| 			delete this._handlers[id];
 | |
| 		}
 | |
| 	} 
 | |
| };
 | |
| 
 | |
| //array helper
 | |
| //can be used by dhtmlx.toArray()
 | |
| dhtmlx.PowerArray={
 | |
| 	//remove element at specified position
 | |
| 	removeAt:function(pos,len){
 | |
| 		if (pos>=0) this.splice(pos,(len||1));
 | |
| 	},
 | |
| 	//find element in collection and remove it 
 | |
| 	remove:function(value){
 | |
| 		this.removeAt(this.find(value));
 | |
| 	},	
 | |
| 	//add element to collection at specific position
 | |
| 	insertAt:function(data,pos){
 | |
| 		if (!pos && pos!==0) 	//add to the end by default
 | |
| 			this.push(data);
 | |
| 		else {	
 | |
| 			var b = this.splice(pos,(this.length-pos));
 | |
|   			this[pos] = data;
 | |
|   			this.push.apply(this,b); //reconstruct array without loosing this pointer
 | |
|   		}
 | |
|   	},  	
 | |
|   	//return index of element, -1 if it doesn't exists
 | |
|   	find:function(data){ 
 | |
|   		for (i=0; i<this.length; i++) 
 | |
|   			if (data==this[i]) return i; 	
 | |
|   		return -1; 
 | |
|   	},
 | |
|   	//execute some method for each element of array
 | |
|   	each:function(functor,master){
 | |
| 		for (var i=0; i < this.length; i++)
 | |
| 			functor.call((master||this),this[i]);
 | |
| 	},
 | |
| 	//create new array from source, by using results of functor 
 | |
| 	map:function(functor,master){
 | |
| 		for (var i=0; i < this.length; i++)
 | |
| 			this[i]=functor.call((master||this),this[i]);
 | |
| 		return this;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.env = {};
 | |
| 
 | |
| //environment detection
 | |
| if (navigator.userAgent.indexOf('Opera') != -1)
 | |
| 	dhtmlx._isOpera=true;
 | |
| else{
 | |
| 	//very rough detection, but it is enough for current goals
 | |
| 	dhtmlx._isIE=!!document.all;
 | |
| 	dhtmlx._isFF=!document.all;
 | |
| 	dhtmlx._isWebKit=(navigator.userAgent.indexOf("KHTML")!=-1);
 | |
| 	if (navigator.appVersion.indexOf("MSIE 8.0")!= -1 && document.compatMode != "BackCompat") 
 | |
| 		dhtmlx._isIE=8;
 | |
| 	if (navigator.appVersion.indexOf("MSIE 9.0")!= -1 && document.compatMode != "BackCompat") 
 | |
| 		dhtmlx._isIE=9;
 | |
| }
 | |
| 
 | |
| dhtmlx.env = {};
 | |
| 
 | |
| // dhtmlx.env.transform 
 | |
| // dhtmlx.env.transition
 | |
| (function(){
 | |
| 	dhtmlx.env.transform = false;
 | |
| 	dhtmlx.env.transition = false;
 | |
| 	var options = {};
 | |
| 	options.names = ['transform', 'transition'];
 | |
| 	options.transform = ['transform', 'WebkitTransform', 'MozTransform', 'oTransform','msTransform'];
 | |
| 	options.transition = ['transition', 'WebkitTransition', 'MozTransition', 'oTransition'];
 | |
| 	
 | |
| 	var d = document.createElement("DIV");
 | |
| 	var property;
 | |
| 	for(var i=0; i<options.names.length; i++) {
 | |
| 		while (p = options[options.names[i]].pop()) {
 | |
| 			if(typeof d.style[p] != 'undefined')
 | |
| 				dhtmlx.env[options.names[i]] = true;
 | |
| 		}
 | |
| 	}
 | |
| })();
 | |
| dhtmlx.env.transform_prefix = (function(){
 | |
| 		var prefix;
 | |
| 		if(dhtmlx._isOpera)
 | |
| 			prefix = '-o-';
 | |
| 		else {
 | |
| 			prefix = ''; // default option
 | |
| 			if(dhtmlx._isFF) 
 | |
| 				prefix = '-moz-';
 | |
| 			if(dhtmlx._isWebKit) 
 | |
| 					prefix = '-webkit-';
 | |
| 		}
 | |
| 		return prefix;
 | |
| })();
 | |
| dhtmlx.env.svg = (function(){
 | |
| 		return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
 | |
| })();
 | |
| 
 | |
| //store maximum used z-index
 | |
| dhtmlx.zIndex={ drag : 10000 };
 | |
| 
 | |
| //html helpers
 | |
| dhtmlx.html={
 | |
| 	create:function(name,attrs,html){
 | |
| 		attrs = attrs || {};
 | |
| 		var node = document.createElement(name);
 | |
| 		for (var attr_name in attrs)
 | |
| 			node.setAttribute(attr_name, attrs[attr_name]);
 | |
| 		if (attrs.style)
 | |
| 			node.style.cssText = attrs.style;
 | |
| 		if (attrs["class"])
 | |
| 			node.className = attrs["class"];
 | |
| 		if (html)
 | |
| 			node.innerHTML=html;
 | |
| 		return node;
 | |
| 	},
 | |
| 	//return node value, different logic for different html elements
 | |
| 	getValue:function(node){
 | |
| 		node = dhtmlx.toNode(node);
 | |
| 		if (!node) return "";
 | |
| 		return dhtmlx.isNotDefined(node.value)?node.innerHTML:node.value;
 | |
| 	},
 | |
| 	//remove html node, can process an array of nodes at once
 | |
| 	remove:function(node){
 | |
| 		if (node instanceof Array)
 | |
| 			for (var i=0; i < node.length; i++)
 | |
| 				this.remove(node[i]);
 | |
| 		else
 | |
| 			if (node && node.parentNode)
 | |
| 				node.parentNode.removeChild(node);
 | |
| 	},
 | |
| 	//insert new node before sibling, or at the end if sibling doesn't exist
 | |
| 	insertBefore: function(node,before,rescue){
 | |
| 		if (!node) return;
 | |
| 		if (before)
 | |
| 			before.parentNode.insertBefore(node, before);
 | |
| 		else
 | |
| 			rescue.appendChild(node);
 | |
| 	},
 | |
| 	//return custom ID from html element 
 | |
| 	//will check all parents starting from event's target
 | |
| 	locate:function(e,id){
 | |
| 		e=e||event;
 | |
| 		var trg=e.target||e.srcElement;
 | |
| 		while (trg){
 | |
| 			if (trg.getAttribute){	//text nodes has not getAttribute
 | |
| 				var test = trg.getAttribute(id);
 | |
| 				if (test) return test;
 | |
| 			}
 | |
| 			trg=trg.parentNode;
 | |
| 		}	
 | |
| 		return null;
 | |
| 	},
 | |
| 	//returns position of html element on the page
 | |
| 	offset:function(elem) {
 | |
| 		if (elem.getBoundingClientRect) { //HTML5 method
 | |
| 			var box = elem.getBoundingClientRect();
 | |
| 			var body = document.body;
 | |
| 			var docElem = document.documentElement;
 | |
| 			var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
 | |
| 			var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
 | |
| 			var clientTop = docElem.clientTop || body.clientTop || 0;
 | |
| 			var clientLeft = docElem.clientLeft || body.clientLeft || 0;
 | |
| 			var top  = box.top +  scrollTop - clientTop;
 | |
| 			var left = box.left + scrollLeft - clientLeft;
 | |
| 			return { y: Math.round(top), x: Math.round(left) };
 | |
| 		} else { //fallback to naive approach
 | |
| 			var top=0, left=0;
 | |
| 			while(elem) {
 | |
| 				top = top + parseInt(elem.offsetTop,10);
 | |
| 				left = left + parseInt(elem.offsetLeft,10);
 | |
| 				elem = elem.offsetParent;
 | |
| 			}
 | |
| 			return {y: top, x: left};
 | |
| 		}
 | |
| 	},
 | |
| 	//returns position of event
 | |
| 	pos:function(ev){
 | |
| 		ev = ev || event;
 | |
|         if(ev.pageX || ev.pageY)	//FF, KHTML
 | |
|             return {x:ev.pageX, y:ev.pageY};
 | |
|         //IE
 | |
|         var d  =  ((dhtmlx._isIE)&&(document.compatMode != "BackCompat"))?document.documentElement:document.body;
 | |
|         return {
 | |
|                 x:ev.clientX + d.scrollLeft - d.clientLeft,
 | |
|                 y:ev.clientY + d.scrollTop  - d.clientTop
 | |
|         };
 | |
| 	},
 | |
| 	//prevent event action
 | |
| 	preventEvent:function(e){
 | |
| 		if (e && e.preventDefault) e.preventDefault();
 | |
| 		dhtmlx.html.stopEvent(e);
 | |
| 	},
 | |
| 	//stop event bubbling
 | |
| 	stopEvent:function(e){
 | |
| 		(e||event).cancelBubble=true;
 | |
| 		return false;
 | |
| 	},
 | |
| 	//add css class to the node
 | |
| 	addCss:function(node,name){
 | |
|         node.className+=" "+name;
 | |
|     },
 | |
|     //remove css class from the node
 | |
|     removeCss:function(node,name){
 | |
|         node.className=node.className.replace(RegExp(name,"g"),"");
 | |
|     }
 | |
| };
 | |
| 
 | |
| //autodetect codebase folder
 | |
| (function(){
 | |
| 	var temp = document.getElementsByTagName("SCRIPT");	//current script, most probably
 | |
| 	dhtmlx.assert(temp.length,"Can't locate codebase");
 | |
| 	if (temp.length){
 | |
| 		//full path to script
 | |
| 		temp = (temp[temp.length-1].getAttribute("src")||"").split("/");
 | |
| 		//get folder name
 | |
| 		temp.splice(temp.length-1, 1);
 | |
| 		dhtmlx.codebase = temp.slice(0, temp.length).join("/")+"/";
 | |
| 	}
 | |
| })();
 | |
| 
 | |
| if (!dhtmlx.ui)
 | |
| 	dhtmlx.ui={};
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'destructor.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Behavior:Destruction
 | |
| 	
 | |
| 	@export
 | |
| 		destructor
 | |
| */
 | |
| 
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| dhtmlx.Destruction = {
 | |
| 	_init:function(){
 | |
| 		//register self in global list of destructors
 | |
| 		dhtmlx.destructors.push(this);
 | |
| 	},
 | |
| 	//will be called automatically on unload, can be called manually
 | |
| 	//simplifies job of GC
 | |
| 	destructor:function(){
 | |
| 		this.destructor=function(){}; //destructor can be called only once
 | |
| 		
 | |
| 		//html collection
 | |
| 		this._htmlmap  = null;
 | |
| 		this._htmlrows = null;
 | |
| 		
 | |
| 		//temp html element, used by toHTML
 | |
| 		if (this._html)
 | |
| 			document.body.appendChild(this._html);	//need to attach, for IE's GC
 | |
| 
 | |
| 		this._html = null;
 | |
| 		if (this._obj) {
 | |
| 			this._obj.innerHTML="";
 | |
| 			this._obj._htmlmap = null;
 | |
| 		}
 | |
| 		this._obj = this._dataobj=null;
 | |
| 		this.data = null;
 | |
| 		this._events = this._handlers = {};
 | |
| 		if(this.render)
 | |
| 			this.render = function(){};//need in case of delayed method calls (virtual render case)
 | |
| 	}
 | |
| };
 | |
| //global list of destructors
 | |
| dhtmlx.destructors = [];
 | |
| dhtmlx.event(window,"unload",function(){
 | |
| 	//call all registered destructors
 | |
| 	if (dhtmlx.destructors){
 | |
| 		for (var i=0; i<dhtmlx.destructors.length; i++)
 | |
| 			dhtmlx.destructors[i].destructor();
 | |
| 		dhtmlx.destructors = [];
 | |
| 	}
 | |
| 	
 | |
| 	//detach all known DOM events
 | |
| 	for (var a in dhtmlx._events){
 | |
| 		var ev = dhtmlx._events[a];
 | |
| 		if (ev[0].removeEventListener)
 | |
| 			ev[0].removeEventListener(ev[1],ev[2],false);
 | |
| 		else if (ev[0].detachEvent)
 | |
| 			ev[0].detachEvent("on"+ev[1],ev[2]);
 | |
| 		delete dhtmlx._events[a];
 | |
| 	}
 | |
| });
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'load.js'*/
 | |
| 
 | |
| 
 | |
| /* 
 | |
| 	ajax operations 
 | |
| 	
 | |
| 	can be used for direct loading as
 | |
| 		dhtmlx.ajax(ulr, callback)
 | |
| 	or
 | |
| 		dhtmlx.ajax().item(url)
 | |
| 		dhtmlx.ajax().post(url)
 | |
| 
 | |
| */
 | |
| 
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| dhtmlx.ajax = function(url,call,master){
 | |
| 	//if parameters was provided - made fast call
 | |
| 	if (arguments.length!==0){
 | |
| 		var http_request = new dhtmlx.ajax();
 | |
| 		if (master) http_request.master=master;
 | |
| 		http_request.get(url,null,call);
 | |
| 	}
 | |
| 	if (!this.getXHR) return new dhtmlx.ajax(); //allow to create new instance without direct new declaration
 | |
| 	
 | |
| 	return this;
 | |
| };
 | |
| dhtmlx.ajax.prototype={
 | |
| 	//creates xmlHTTP object
 | |
| 	getXHR:function(){
 | |
| 		if (dhtmlx._isIE)
 | |
| 		 return new ActiveXObject("Microsoft.xmlHTTP");
 | |
| 		else 
 | |
| 		 return new XMLHttpRequest();
 | |
| 	},
 | |
| 	/*
 | |
| 		send data to the server
 | |
| 		params - hash of properties which will be added to the url
 | |
| 		call - callback, can be an array of functions
 | |
| 	*/
 | |
| 	send:function(url,params,call){
 | |
| 		var x=this.getXHR();
 | |
| 		if (typeof call == "function")
 | |
| 		 call = [call];
 | |
| 		//add extra params to the url
 | |
| 		if (typeof params == "object"){
 | |
| 			var t=[];
 | |
| 			for (var a in params){
 | |
| 				var value = params[a];
 | |
| 				if (value === null || value === dhtmlx.undefined)
 | |
| 					value = "";
 | |
| 				t.push(a+"="+encodeURIComponent(value));// utf-8 escaping
 | |
| 		 	}
 | |
| 			params=t.join("&");
 | |
| 		}
 | |
| 		if (params && !this.post){
 | |
| 			url=url+(url.indexOf("?")!=-1 ? "&" : "?")+params;
 | |
| 			params=null;
 | |
| 		}
 | |
| 		
 | |
| 		x.open(this.post?"POST":"GET",url,!this._sync);
 | |
| 		if (this.post)
 | |
| 		 x.setRequestHeader('Content-type','application/x-www-form-urlencoded');
 | |
| 		 
 | |
| 		//async mode, define loading callback
 | |
| 		//if (!this._sync){
 | |
| 		 var self=this;
 | |
| 		 x.onreadystatechange= function(){
 | |
| 			if (!x.readyState || x.readyState == 4){
 | |
| 				//dhtmlx.log_full_time("data_loading");	//log rendering time
 | |
| 				if (call && self) 
 | |
| 					for (var i=0; i < call.length; i++)	//there can be multiple callbacks
 | |
| 					 if (call[i])
 | |
| 						call[i].call((self.master||self),x.responseText,x.responseXML,x);
 | |
| 				self.master=null;
 | |
| 				call=self=null;	//anti-leak
 | |
| 			}
 | |
| 		 };
 | |
| 		//}
 | |
| 		
 | |
| 		x.send(params||null);
 | |
| 		return x; //return XHR, which can be used in case of sync. mode
 | |
| 	},
 | |
| 	//GET request
 | |
| 	get:function(url,params,call){
 | |
| 		this.post=false;
 | |
| 		return this.send(url,params,call);
 | |
| 	},
 | |
| 	//POST request
 | |
| 	post:function(url,params,call){
 | |
| 		this.post=true;
 | |
| 		return this.send(url,params,call);
 | |
| 	}, 
 | |
| 	sync:function(){
 | |
| 		this._sync = true;
 | |
| 		return this;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| dhtmlx.AtomDataLoader={
 | |
| 	_init:function(config){
 | |
| 		//prepare data store
 | |
| 		this.data = {}; 
 | |
| 		if (config){
 | |
| 			this._settings.datatype = config.datatype||"json";
 | |
| 			this._after_init.push(this._load_when_ready);
 | |
| 		}
 | |
| 	},
 | |
| 	_load_when_ready:function(){
 | |
| 		this._ready_for_data = true;
 | |
| 		
 | |
| 		if (this._settings.url)
 | |
| 			this.url_setter(this._settings.url);
 | |
| 		if (this._settings.data)
 | |
| 			this.data_setter(this._settings.data);
 | |
| 	},
 | |
| 	url_setter:function(value){
 | |
| 		if (!this._ready_for_data) return value;
 | |
| 		this.load(value, this._settings.datatype);	
 | |
| 		return value;
 | |
| 	},
 | |
| 	data_setter:function(value){
 | |
| 		if (!this._ready_for_data) return value;
 | |
| 		this.parse(value, this._settings.datatype);
 | |
| 		return true;
 | |
| 	},
 | |
| 	//loads data from external URL
 | |
| 	load:function(url,call){
 | |
| 		this.callEvent("onXLS",[]);
 | |
| 		if (typeof call == "string"){	//second parameter can be a loading type or callback
 | |
| 			this.data.driver = dhtmlx.DataDriver[call];
 | |
| 			call = arguments[2];
 | |
| 		}
 | |
| 		else
 | |
| 			this.data.driver = dhtmlx.DataDriver["xml"];
 | |
| 		//load data by async ajax call
 | |
| 		dhtmlx.ajax(url,[this._onLoad,call],this);
 | |
| 	},
 | |
| 	//loads data from object
 | |
| 	parse:function(data,type){
 | |
| 		this.callEvent("onXLS",[]);
 | |
| 		this.data.driver = dhtmlx.DataDriver[type||"xml"];
 | |
| 		this._onLoad(data,null);
 | |
| 	},
 | |
| 	//default after loading callback
 | |
| 	_onLoad:function(text,xml,loader){
 | |
| 		var driver = this.data.driver;
 | |
| 		var top = driver.getRecords(driver.toObject(text,xml))[0];
 | |
| 		this.data=(driver?driver.getDetails(top):text);
 | |
| 		this.callEvent("onXLE",[]);
 | |
| 	},
 | |
| 	_check_data_feed:function(data){
 | |
| 		if (!this._settings.dataFeed || this._ignore_feed || !data) return true;
 | |
| 		var url = this._settings.dataFeed;
 | |
| 		if (typeof url == "function")
 | |
| 			return url.call(this, (data.id||data), data);
 | |
| 		url = url+(url.indexOf("?")==-1?"?":"&")+"action=get&id="+encodeURIComponent(data.id||data);
 | |
| 		this.callEvent("onXLS",[]);
 | |
| 		dhtmlx.ajax(url, function(text,xml){
 | |
| 			this._ignore_feed=true;
 | |
| 			this.setValues(dhtmlx.DataDriver.json.toObject(text)[0]);
 | |
| 			this._ignore_feed=false;
 | |
| 			this.callEvent("onXLE",[]);
 | |
| 		}, this);
 | |
| 		return false;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| /*
 | |
| 	Abstraction layer for different data types
 | |
| */
 | |
| 
 | |
| dhtmlx.DataDriver={};
 | |
| dhtmlx.DataDriver.json={
 | |
| 	//convert json string to json object if necessary
 | |
| 	toObject:function(data){
 | |
| 		if (!data) data="[]";
 | |
| 		if (typeof data == "string"){
 | |
| 		 eval ("dhtmlx.temp="+data);
 | |
| 		 return dhtmlx.temp;
 | |
| 		}
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		if (data && !(data instanceof Array))
 | |
| 		 return [data];
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get hash of properties for single record
 | |
| 	getDetails:function(data){
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get count of data and position at which new data need to be inserted
 | |
| 	getInfo:function(data){
 | |
| 		return { 
 | |
| 		 _size:(data.total_count||0),
 | |
| 		 _from:(data.pos||0),
 | |
| 		 _key:(data.dhx_security)
 | |
| 		};
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.DataDriver.json_ext={
 | |
| 	//convert json string to json object if necessary
 | |
| 	toObject:function(data){
 | |
| 		if (!data) data="[]";
 | |
| 		if (typeof data == "string"){
 | |
| 			var temp;
 | |
| 			eval ("temp="+data);
 | |
| 			dhtmlx.temp = [];
 | |
| 			var header  = temp.header;
 | |
| 			for (var i = 0; i < temp.data.length; i++) {
 | |
| 				var item = {};
 | |
| 				for (var j = 0; j < header.length; j++) {
 | |
| 					if (typeof(temp.data[i][j]) != "undefined")
 | |
| 						item[header[j]] = temp.data[i][j];
 | |
| 				}
 | |
| 				dhtmlx.temp.push(item);
 | |
| 			}
 | |
| 			return dhtmlx.temp;
 | |
| 		}
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		if (data && !(data instanceof Array))
 | |
| 		 return [data];
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get hash of properties for single record
 | |
| 	getDetails:function(data){
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get count of data and position at which new data need to be inserted
 | |
| 	getInfo:function(data){
 | |
| 		return {
 | |
| 		 _size:(data.total_count||0),
 | |
| 		 _from:(data.pos||0)
 | |
| 		};
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.DataDriver.html={
 | |
| 	/*
 | |
| 		incoming data can be
 | |
| 		 - collection of nodes
 | |
| 		 - ID of parent container
 | |
| 		 - HTML text
 | |
| 	*/
 | |
| 	toObject:function(data){
 | |
| 		if (typeof data == "string"){
 | |
| 		 var t=null;
 | |
| 		 if (data.indexOf("<")==-1)	//if no tags inside - probably its an ID
 | |
| 			t = dhtmlx.toNode(data);
 | |
| 		 if (!t){
 | |
| 			t=document.createElement("DIV");
 | |
| 			t.innerHTML = data;
 | |
| 		 }
 | |
| 		 
 | |
| 		 return t.getElementsByTagName(this.tag);
 | |
| 		}
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		if (data.tagName)
 | |
| 		 return data.childNodes;
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get hash of properties for single record
 | |
| 	getDetails:function(data){
 | |
| 		return dhtmlx.DataDriver.xml.tagToObject(data);
 | |
| 	},
 | |
| 	//dyn loading is not supported by HTML data source
 | |
| 	getInfo:function(data){
 | |
| 		return { 
 | |
| 		 _size:0,
 | |
| 		 _from:0
 | |
| 		};
 | |
| 	},
 | |
| 	tag: "LI"
 | |
| };
 | |
| 
 | |
| dhtmlx.DataDriver.jsarray={
 | |
| 	//eval jsarray string to jsarray object if necessary
 | |
| 	toObject:function(data){
 | |
| 		if (typeof data == "string"){
 | |
| 		 eval ("dhtmlx.temp="+data);
 | |
| 		 return dhtmlx.temp;
 | |
| 		}
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get hash of properties for single record, in case of array they will have names as "data{index}"
 | |
| 	getDetails:function(data){
 | |
| 		var result = {};
 | |
| 		for (var i=0; i < data.length; i++) 
 | |
| 		 result["data"+i]=data[i];
 | |
| 		 
 | |
| 		return result;
 | |
| 	},
 | |
| 	//dyn loading is not supported by js-array data source
 | |
| 	getInfo:function(data){
 | |
| 		return { 
 | |
| 		 _size:0,
 | |
| 		 _from:0
 | |
| 		};
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.DataDriver.csv={
 | |
| 	//incoming data always a string
 | |
| 	toObject:function(data){
 | |
| 		return data;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		return data.split(this.row);
 | |
| 	},
 | |
| 	//get hash of properties for single record, data named as "data{index}"
 | |
| 	getDetails:function(data){
 | |
| 		data = this.stringToArray(data);
 | |
| 		var result = {};
 | |
| 		for (var i=0; i < data.length; i++) 
 | |
| 		 result["data"+i]=data[i];
 | |
| 		 
 | |
| 		return result;
 | |
| 	},
 | |
| 	//dyn loading is not supported by csv data source
 | |
| 	getInfo:function(data){
 | |
| 		return { 
 | |
| 		 _size:0,
 | |
| 		 _from:0
 | |
| 		};
 | |
| 	},
 | |
| 	//split string in array, takes string surrounding quotes in account
 | |
| 	stringToArray:function(data){
 | |
| 		data = data.split(this.cell);
 | |
| 		for (var i=0; i < data.length; i++)
 | |
| 		 data[i] = data[i].replace(/^[ \t\n\r]*(\"|)/g,"").replace(/(\"|)[ \t\n\r]*$/g,"");
 | |
| 		return data;
 | |
| 	},
 | |
| 	row:"\n",	//default row separator
 | |
| 	cell:","	//default cell separator
 | |
| };
 | |
| 
 | |
| dhtmlx.DataDriver.xml={
 | |
| 	//convert xml string to xml object if necessary
 | |
| 	toObject:function(text,xml){
 | |
| 		if (xml && (xml=this.checkResponse(text,xml)))	//checkResponse - fix incorrect content type and extra whitespaces errors
 | |
| 		 return xml;
 | |
| 		if (typeof text == "string"){
 | |
| 		 return this.fromString(text);
 | |
| 		}
 | |
| 		return text;
 | |
| 	},
 | |
| 	//get array of records
 | |
| 	getRecords:function(data){
 | |
| 		return this.xpath(data,this.records);
 | |
| 	},
 | |
| 	records:"/*/item",
 | |
| 	//get hash of properties for single record
 | |
| 	getDetails:function(data){
 | |
| 		return this.tagToObject(data,{});
 | |
| 	},
 | |
| 	//get count of data and position at which new data_loading need to be inserted
 | |
| 	getInfo:function(data){
 | |
| 		return { 
 | |
| 		 _size:(data.documentElement.getAttribute("total_count")||0),
 | |
| 		 _from:(data.documentElement.getAttribute("pos")||0),
 | |
| 		 _key:(data.documentElement.getAttribute("dhx_security"))
 | |
| 		};
 | |
| 	},
 | |
| 	//xpath helper
 | |
| 	xpath:function(xml,path){
 | |
| 		if (window.XPathResult){	//FF, KHTML, Opera
 | |
| 		 var node=xml;
 | |
| 		 if(xml.nodeName.indexOf("document")==-1)
 | |
| 		 xml=xml.ownerDocument;
 | |
| 		 var res = [];
 | |
| 		 var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null);
 | |
| 		 var temp = col.iterateNext();
 | |
| 		 while (temp){ 
 | |
| 			res.push(temp);
 | |
| 			temp = col.iterateNext();
 | |
| 		}
 | |
| 		return res;
 | |
| 		}	
 | |
| 		else {
 | |
| 			var test = true;
 | |
| 			try {
 | |
| 				if (typeof(xml.selectNodes)=="undefined")
 | |
| 					test = false;
 | |
| 			} catch(e){ /*IE7 and below can't operate with xml object*/ }
 | |
| 			//IE
 | |
| 			if (test)
 | |
| 				return xml.selectNodes(path);
 | |
| 			else {
 | |
| 				//Google hate us, there is no interface to do XPath
 | |
| 				//use naive approach
 | |
| 				var name = path.split("/").pop();
 | |
| 				return xml.getElementsByTagName(name);
 | |
| 			}
 | |
| 		}
 | |
| 	},
 | |
| 	//convert xml tag to js object, all subtags and attributes are mapped to the properties of result object
 | |
| 	tagToObject:function(tag,z){
 | |
| 		z=z||{};
 | |
| 		var flag=false;
 | |
| 		
 | |
| 		//map attributes
 | |
| 		var a=tag.attributes;
 | |
| 		if(a && a.length){
 | |
| 			for (var i=0; i<a.length; i++)
 | |
| 		 		z[a[i].name]=a[i].value;
 | |
| 		 	flag = true;
 | |
| 	 	}
 | |
| 		//map subtags
 | |
| 		
 | |
| 		var b=tag.childNodes;
 | |
| 		var state = {};
 | |
| 		for (var i=0; i<b.length; i++){
 | |
| 			if (b[i].nodeType==1){
 | |
| 				var name = b[i].tagName;
 | |
| 				if (typeof z[name] != "undefined"){
 | |
| 					if (!(z[name] instanceof Array))
 | |
| 						z[name]=[z[name]];
 | |
| 					z[name].push(this.tagToObject(b[i],{}));
 | |
| 				}
 | |
| 				else
 | |
| 					z[b[i].tagName]=this.tagToObject(b[i],{});	//sub-object for complex subtags
 | |
| 				flag=true;
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		if (!flag)
 | |
| 			return this.nodeValue(tag);
 | |
| 		//each object will have its text content as "value" property
 | |
| 		z.value = this.nodeValue(tag);
 | |
| 		return z;
 | |
| 	},
 | |
| 	//get value of xml node 
 | |
| 	nodeValue:function(node){
 | |
| 		if (node.firstChild)
 | |
| 			return node.firstChild.wholeText||node.firstChild.data;
 | |
| 		return "";
 | |
| 	},
 | |
| 	//convert XML string to XML object
 | |
| 	fromString:function(xmlString){
 | |
| 		if (window.DOMParser && !dhtmlx._isIE)		// FF, KHTML, Opera
 | |
| 		 return (new DOMParser()).parseFromString(xmlString,"text/xml");
 | |
| 		if (window.ActiveXObject){	// IE, utf-8 only 
 | |
| 		 var temp=new ActiveXObject("Microsoft.xmlDOM");
 | |
| 		 temp.loadXML(xmlString);
 | |
| 		 return temp;
 | |
| 		}
 | |
| 		dhtmlx.error("Load from xml string is not supported");
 | |
| 	},
 | |
| 	//check is XML correct and try to reparse it if its invalid
 | |
| 	checkResponse:function(text,xml){ 
 | |
| 		if (xml && ( xml.firstChild && xml.firstChild.tagName != "parsererror") )
 | |
| 			return xml;
 | |
| 		//parsing as string resolves incorrect content type
 | |
| 		//regexp removes whitespaces before xml declaration, which is vital for FF
 | |
| 		var a=this.fromString(text.replace(/^[\s]+/,""));
 | |
| 		if (a) return a;
 | |
| 		
 | |
| 		dhtmlx.error("xml can't be parsed",text);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'datastore.js'*/
 | |
| 
 | |
| 
 | |
| /*DHX:Depend load.js*/
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| /*
 | |
| 	Behavior:DataLoader - load data in the component
 | |
| 	
 | |
| 	@export
 | |
| 		load
 | |
| 		parse
 | |
| */
 | |
| dhtmlx.DataLoader={
 | |
| 	_init:function(config){
 | |
| 		//prepare data store
 | |
| 		config = config || "";
 | |
| 		this.name = "DataStore";
 | |
| 		this.data = (config.datastore)||(new dhtmlx.DataStore());
 | |
| 		this._readyHandler = this.data.attachEvent("onStoreLoad",dhtmlx.bind(this._call_onready,this));
 | |
| 	},
 | |
| 	//loads data from external URL
 | |
| 	load:function(url,call){
 | |
| 		dhtmlx.AtomDataLoader.load.apply(this, arguments);
 | |
| 		//prepare data feed for dyn. loading
 | |
| 		if (!this.data.feed)
 | |
| 		 this.data.feed = function(from,count){
 | |
| 			//allow only single request at same time
 | |
| 			if (this._load_count)
 | |
| 				return this._load_count=[from,count];	//save last ignored request
 | |
| 			else
 | |
| 				this._load_count=true;
 | |
| 				
 | |
| 			this.load(url+((url.indexOf("?")==-1)?"?":"&")+"posStart="+from+"&count="+count,function(){
 | |
| 				//after loading check if we have some ignored requests
 | |
| 				var temp = this._load_count;
 | |
| 				this._load_count = false;
 | |
| 				if (typeof temp =="object")
 | |
| 					this.data.feed.apply(this, temp);	//load last ignored request
 | |
| 			});
 | |
| 		};
 | |
| 	},
 | |
| 	//default after loading callback
 | |
| 	_onLoad:function(text,xml,loader){
 | |
| 		this.data._parse(this.data.driver.toObject(text,xml));
 | |
| 		this.callEvent("onXLE",[]);
 | |
| 		if(this._readyHandler){
 | |
| 			this.data.detachEvent(this._readyHandler);
 | |
| 			this._readyHandler = null;
 | |
| 		}
 | |
| 	},
 | |
| 	dataFeed_setter:function(value){
 | |
| 		this.data.attachEvent("onBeforeFilter", dhtmlx.bind(function(text, value){
 | |
| 			if (this._settings.dataFeed){
 | |
| 				var filter = {};
 | |
| 				if (!text && !filter) return;
 | |
| 				if (typeof text == "function"){
 | |
| 					if (!value) return;
 | |
| 					text(value, filter);
 | |
| 				} else 
 | |
| 					filter = { text:value };
 | |
| 
 | |
| 				this.clearAll();
 | |
| 				var url = this._settings.dataFeed;
 | |
| 				if (typeof url == "function")
 | |
| 					return url.call(this, value, filter);
 | |
| 				var urldata = [];
 | |
| 				for (var key in filter)
 | |
| 					urldata.push("dhx_filter["+key+"]="+encodeURIComponent(filter[key]));
 | |
| 				this.load(url+(url.indexOf("?")<0?"?":"&")+urldata.join("&"), this._settings.datatype);
 | |
| 				return false;
 | |
| 			}
 | |
| 		},this));
 | |
| 		return value;
 | |
| 	},
 | |
| 	_call_onready:function(){
 | |
| 		if (this._settings.ready){
 | |
| 			var code = dhtmlx.toFunctor(this._settings.ready);
 | |
| 			if (code && code.call) code.apply(this, arguments);
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	DataStore is not a behavior, it standalone object, which represents collection of data.
 | |
| 	Call provideAPI to map data API
 | |
| 
 | |
| 	@export
 | |
| 		exists
 | |
| 		idByIndex
 | |
| 		indexById
 | |
| 		get
 | |
| 		set
 | |
| 		refresh
 | |
| 		dataCount
 | |
| 		sort
 | |
| 		filter
 | |
| 		next
 | |
| 		previous
 | |
| 		clearAll
 | |
| 		first
 | |
| 		last
 | |
| */
 | |
| dhtmlx.DataStore = function(){
 | |
| 	this.name = "DataStore";
 | |
| 	
 | |
| 	dhtmlx.extend(this, dhtmlx.EventSystem);
 | |
| 	
 | |
| 	this.setDriver("xml");	//default data source is an XML
 | |
| 	this.pull = {};						//hash of IDs
 | |
| 	this.order = dhtmlx.toArray();		//order of IDs
 | |
| };
 | |
| 
 | |
| dhtmlx.DataStore.prototype={
 | |
| 	//defines type of used data driver
 | |
| 	//data driver is an abstraction other different data formats - xml, json, csv, etc.
 | |
| 	setDriver:function(type){
 | |
| 		dhtmlx.assert(dhtmlx.DataDriver[type],"incorrect DataDriver");
 | |
| 		this.driver = dhtmlx.DataDriver[type];
 | |
| 	},
 | |
| 	//process incoming raw data
 | |
| 	_parse:function(data){
 | |
| 		this.callEvent("onParse", [this.driver, data]);
 | |
| 		if (this._filter_order)
 | |
| 			this.filter();
 | |
| 			
 | |
| 		//get size and position of data
 | |
| 		var info = this.driver.getInfo(data);
 | |
| 		if (info._key)
 | |
| 			dhtmlx.security_key = info._key;
 | |
| 		//get array of records
 | |
| 
 | |
| 		var recs = this.driver.getRecords(data);
 | |
| 		var from = (info._from||0)*1;
 | |
| 		
 | |
| 		if (from === 0 && this.order[0]) //update mode
 | |
| 			from = this.order.length;
 | |
| 		
 | |
| 		var j=0;
 | |
| 		for (var i=0; i<recs.length; i++){
 | |
| 			//get has of details for each record
 | |
| 			var temp = this.driver.getDetails(recs[i]);
 | |
| 			var id = this.id(temp); 	//generate ID for the record
 | |
| 			if (!this.pull[id]){		//if such ID already exists - update instead of insert
 | |
| 				this.order[j+from]=id;	
 | |
| 				j++;
 | |
| 			}
 | |
| 			this.pull[id]=temp;
 | |
| 			//if (this._format)	this._format(temp);
 | |
| 			
 | |
| 			if (this.extraParser)
 | |
| 				this.extraParser(temp);
 | |
| 			if (this._scheme){ 
 | |
| 				if (this._scheme.$init)
 | |
| 					this._scheme.$update(temp);
 | |
| 				else if (this._scheme.$update)
 | |
| 					this._scheme.$update(temp);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		//for all not loaded data
 | |
| 		for (var i=0; i < info._size; i++)
 | |
| 			if (!this.order[i]){
 | |
| 				var id = dhtmlx.uid();
 | |
| 				var temp = {id:id, $template:"loading"};	//create fake records
 | |
| 				this.pull[id]=temp;
 | |
| 				this.order[i]=id;
 | |
| 			}
 | |
| 
 | |
| 		this.callEvent("onStoreLoad",[this.driver, data]);
 | |
| 		//repaint self after data loading
 | |
| 		this.refresh();
 | |
| 	},
 | |
| 	//generate id for data object
 | |
| 	id:function(data){
 | |
| 		return data.id||(data.id=dhtmlx.uid());
 | |
| 	},
 | |
| 	changeId:function(old, newid){
 | |
| 		dhtmlx.assert(this.pull[old],"Can't change id, for non existing item: "+old);
 | |
| 		this.pull[newid] = this.pull[old];
 | |
| 		this.pull[newid].id = newid;
 | |
| 		this.order[this.order.find(old)]=newid;
 | |
| 		if (this._filter_order)
 | |
| 			this._filter_order[this._filter_order.find(old)]=newid;
 | |
| 		this.callEvent("onIdChange", [old, newid]);
 | |
| 		if (this._render_change_id)
 | |
| 			this._render_change_id(old, newid);
 | |
| 	},
 | |
| 	get:function(id){
 | |
| 		return this.item(id);
 | |
| 	},
 | |
| 	set:function(id, data){
 | |
| 		return this.update(id, data);
 | |
| 	},
 | |
| 	//get data from hash by id
 | |
| 	item:function(id){
 | |
| 		return this.pull[id];
 | |
| 	},
 | |
| 	//assigns data by id
 | |
| 	update:function(id,data){
 | |
| 		if (this._scheme && this._scheme.$update)
 | |
| 			this._scheme.$update(data);
 | |
| 		if (this.callEvent("onBeforeUpdate", [id, data]) === false) return false;
 | |
| 		this.pull[id]=data;
 | |
| 		this.refresh(id);
 | |
| 	},
 | |
| 	//sends repainting signal
 | |
| 	refresh:function(id){
 | |
| 		if (this._skip_refresh) return; 
 | |
| 		
 | |
| 		if (id)
 | |
| 			this.callEvent("onStoreUpdated",[id, this.pull[id], "update"]);
 | |
| 		else
 | |
| 			this.callEvent("onStoreUpdated",[null,null,null]);
 | |
| 	},
 | |
| 	silent:function(code){
 | |
| 		this._skip_refresh = true;
 | |
| 		code.call(this);
 | |
| 		this._skip_refresh = false;
 | |
| 	},
 | |
| 	//converts range IDs to array of all IDs between them
 | |
| 	getRange:function(from,to){		
 | |
| 		//if some point is not defined - use first or last id
 | |
| 		//BEWARE - do not use empty or null ID
 | |
| 		if (from)
 | |
| 			from = this.indexById(from);
 | |
| 		else 
 | |
| 			from = this.startOffset||0;
 | |
| 		if (to)
 | |
| 			to = this.indexById(to);
 | |
| 		else {
 | |
| 			to = Math.min((this.endOffset||Infinity),(this.dataCount()-1));
 | |
| 			if (to<0) to = 0; //we have not data in the store
 | |
| 		}
 | |
| 
 | |
| 		if (from>to){ //can be in case of backward shift-selection
 | |
| 			var a=to; to=from; from=a;
 | |
| 		}
 | |
| 				
 | |
| 		return this.getIndexRange(from,to);
 | |
| 	},
 | |
| 	//converts range of indexes to array of all IDs between them
 | |
| 	getIndexRange:function(from,to){
 | |
| 		to=Math.min((to||Infinity),this.dataCount()-1);
 | |
| 		
 | |
| 		var ret=dhtmlx.toArray(); //result of method is rich-array
 | |
| 		for (var i=(from||0); i <= to; i++)
 | |
| 			ret.push(this.item(this.order[i]));
 | |
| 		return ret;
 | |
| 	},
 | |
| 	//returns total count of elements
 | |
| 	dataCount:function(){
 | |
| 		return this.order.length;
 | |
| 	},
 | |
| 	//returns truy if item with such ID exists
 | |
| 	exists:function(id){
 | |
| 		return !!(this.pull[id]);
 | |
| 	},
 | |
| 	//nextmethod is not visible on component level, check DataMove.move
 | |
| 	//moves item from source index to the target index
 | |
| 	move:function(sindex,tindex){
 | |
| 		if (sindex<0 || tindex<0){
 | |
| 			dhtmlx.error("DataStore::move","Incorrect indexes");
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		var id = this.idByIndex(sindex);
 | |
| 		var obj = this.item(id);
 | |
| 		
 | |
| 		this.order.removeAt(sindex);	//remove at old position
 | |
| 		//if (sindex<tindex) tindex--;	//correct shift, caused by element removing
 | |
| 		this.order.insertAt(id,Math.min(this.order.length, tindex));	//insert at new position
 | |
| 		
 | |
| 		//repaint signal
 | |
| 		this.callEvent("onStoreUpdated",[id,obj,"move"]);
 | |
| 	},
 | |
| 	scheme:function(config){
 | |
| 		/*
 | |
| 			some.scheme({
 | |
| 				order:1,
 | |
| 				name:"dummy",
 | |
| 				title:""
 | |
| 			})
 | |
| 		*/
 | |
| 		this._scheme = config;
 | |
| 		
 | |
| 	},
 | |
| 	sync:function(source, filter, silent){
 | |
| 		if (typeof filter != "function"){
 | |
| 			silent = filter;
 | |
| 			filter = null;
 | |
| 		}
 | |
| 		
 | |
| 		if (dhtmlx.debug_bind){
 | |
| 			this.debug_sync_master = source; 
 | |
| 			dhtmlx.log("[sync] "+this.debug_bind_master.name+"@"+this.debug_bind_master._settings.id+" <= "+this.debug_sync_master.name+"@"+this.debug_sync_master._settings.id);
 | |
| 		}
 | |
| 		
 | |
| 		var topsource = source;
 | |
| 		if (source.name != "DataStore")
 | |
| 			source = source.data;
 | |
| 
 | |
| 		var sync_logic = dhtmlx.bind(function(id, data, mode){
 | |
| 			if (mode != "update" || filter) 
 | |
| 				id = null;
 | |
| 
 | |
| 			if (!id){
 | |
| 				this.order = dhtmlx.toArray([].concat(source.order));
 | |
| 				this._filter_order = null;
 | |
| 				this.pull = source.pull;
 | |
| 				
 | |
| 				if (filter)
 | |
| 					this.silent(filter);
 | |
| 				
 | |
| 				if (this._on_sync)
 | |
| 					this._on_sync();
 | |
| 			}
 | |
| 
 | |
| 			if (dhtmlx.debug_bind)
 | |
| 				dhtmlx.log("[sync:request] "+this.debug_sync_master.name+"@"+this.debug_sync_master._settings.id + " <= "+this.debug_bind_master.name+"@"+this.debug_bind_master._settings.id);
 | |
| 			if (!silent) 
 | |
| 				this.refresh(id);
 | |
| 			else
 | |
| 				silent = false;
 | |
| 		}, this);
 | |
| 		
 | |
| 		source.attachEvent("onStoreUpdated", sync_logic);
 | |
| 		this.feed = function(from, count){
 | |
| 			topsource.loadNext(count, from);
 | |
| 		};
 | |
| 		sync_logic();
 | |
| 	},
 | |
| 	//adds item to the store
 | |
| 	add:function(obj,index){
 | |
| 		
 | |
| 		if (this._scheme){
 | |
| 			obj = obj||{};
 | |
| 			for (var key in this._scheme)
 | |
| 				obj[key] = obj[key]||this._scheme[key];
 | |
| 			if (this._scheme){ 
 | |
| 				if (this._scheme.$init)
 | |
| 					this._scheme.$update(obj);
 | |
| 				else if (this._scheme.$update)
 | |
| 					this._scheme.$update(obj);
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		//generate id for the item
 | |
| 		var id = this.id(obj);
 | |
| 		
 | |
| 		//by default item is added to the end of the list
 | |
| 		var data_size = this.dataCount();
 | |
| 		
 | |
| 		if (dhtmlx.isNotDefined(index) || index < 0)
 | |
| 			index = data_size; 
 | |
| 		//check to prevent too big indexes			
 | |
| 		if (index > data_size){
 | |
| 			dhtmlx.log("Warning","DataStore:add","Index of out of bounds");
 | |
| 			index = Math.min(this.order.length,index);
 | |
| 		}
 | |
| 		if (this.callEvent("onBeforeAdd", [id, obj, index]) === false) return false;
 | |
| 
 | |
| 		if (this.exists(id)) return dhtmlx.error("Not unique ID");
 | |
| 		
 | |
| 		this.pull[id]=obj;
 | |
| 		this.order.insertAt(id,index);
 | |
| 		if (this._filter_order){	//adding during filtering
 | |
| 			//we can't know the location of new item in full dataset, making suggestion
 | |
| 			//put at end by default
 | |
| 			var original_index = this._filter_order.length;
 | |
| 			//put at start only if adding to the start and some data exists
 | |
| 			if (!index && this.order.length)
 | |
| 				original_index = 0;
 | |
| 			
 | |
| 			this._filter_order.insertAt(id,original_index);
 | |
| 		}
 | |
| 		this.callEvent("onafterAdd",[id,index]);
 | |
| 		//repaint signal
 | |
| 		this.callEvent("onStoreUpdated",[id,obj,"add"]);
 | |
| 		return id;
 | |
| 	},
 | |
| 	
 | |
| 	//removes element from datastore
 | |
| 	remove:function(id){
 | |
| 		//id can be an array of IDs - result of getSelect, for example
 | |
| 		if (id instanceof Array){
 | |
| 			for (var i=0; i < id.length; i++)
 | |
| 				this.remove(id[i]);
 | |
| 			return;
 | |
| 		}
 | |
| 		if (this.callEvent("onBeforeDelete",[id]) === false) return false;
 | |
| 		if (!this.exists(id)) return dhtmlx.error("Not existing ID",id);
 | |
| 		var obj = this.item(id);	//save for later event
 | |
| 		//clear from collections
 | |
| 		this.order.remove(id);
 | |
| 		if (this._filter_order) 
 | |
| 			this._filter_order.remove(id);
 | |
| 			
 | |
| 		delete this.pull[id];
 | |
| 		this.callEvent("onafterdelete",[id]);
 | |
| 		//repaint signal
 | |
| 		this.callEvent("onStoreUpdated",[id,obj,"delete"]);
 | |
| 	},
 | |
| 	//deletes all records in datastore
 | |
| 	clearAll:function(){
 | |
| 		//instead of deleting one by one - just reset inner collections
 | |
| 		this.pull = {};
 | |
| 		this.order = dhtmlx.toArray();
 | |
| 		this.feed = null;
 | |
| 		this._filter_order = null;
 | |
| 		this.callEvent("onClearAll",[]);
 | |
| 		this.refresh();
 | |
| 	},
 | |
| 	//converts id to index
 | |
| 	idByIndex:function(index){
 | |
| 		if (index>=this.order.length || index<0)
 | |
| 			dhtmlx.log("Warning","DataStore::idByIndex Incorrect index");
 | |
| 			
 | |
| 		return this.order[index];
 | |
| 	},
 | |
| 	//converts index to id
 | |
| 	indexById:function(id){
 | |
| 		var res = this.order.find(id);	//slower than idByIndex
 | |
| 		
 | |
| 		//if (!this.pull[id])
 | |
| 		//	dhtmlx.log("Warning","DataStore::indexById Non-existing ID: "+ id);
 | |
| 			
 | |
| 		return res;
 | |
| 	},
 | |
| 	//returns ID of next element
 | |
| 	next:function(id,step){
 | |
| 		return this.order[this.indexById(id)+(step||1)];
 | |
| 	},
 | |
| 	//returns ID of first element
 | |
| 	first:function(){
 | |
| 		return this.order[0];
 | |
| 	},
 | |
| 	//returns ID of last element
 | |
| 	last:function(){
 | |
| 		return this.order[this.order.length-1];
 | |
| 	},
 | |
| 	//returns ID of previous element
 | |
| 	previous:function(id,step){
 | |
| 		return this.order[this.indexById(id)-(step||1)];
 | |
| 	},
 | |
| 	/*
 | |
| 		sort data in collection
 | |
| 			by - settings of sorting
 | |
| 		
 | |
| 		or
 | |
| 		
 | |
| 			by - sorting function
 | |
| 			dir - "asc" or "desc"
 | |
| 			
 | |
| 		or
 | |
| 		
 | |
| 			by - property
 | |
| 			dir - "asc" or "desc"
 | |
| 			as - type of sortings
 | |
| 		
 | |
| 		Sorting function will accept 2 parameters and must return 1,0,-1, based on desired order
 | |
| 	*/
 | |
| 	sort:function(by, dir, as){
 | |
| 		var sort = by;	
 | |
| 		if (typeof by == "function")
 | |
| 			sort = {as:by, dir:dir};
 | |
| 		else if (typeof by == "string")
 | |
| 			sort = {by:by, dir:dir, as:as};		
 | |
| 		
 | |
| 		
 | |
| 		var parameters = [sort.by, sort.dir, sort.as];
 | |
| 		if (!this.callEvent("onbeforesort",parameters)) return;	
 | |
| 		
 | |
| 		if (this.order.length){
 | |
| 			var sorter = dhtmlx.sort.create(sort);
 | |
| 			//get array of IDs
 | |
| 			var neworder = this.getRange(this.first(), this.last());
 | |
| 			neworder.sort(sorter);
 | |
| 			this.order = neworder.map(function(obj){ return this.id(obj); },this);
 | |
| 		}
 | |
| 		
 | |
| 		//repaint self
 | |
| 		this.refresh();
 | |
| 		
 | |
| 		this.callEvent("onaftersort",parameters);
 | |
| 	},
 | |
| 	/*
 | |
| 		Filter datasource
 | |
| 		
 | |
| 		text - property, by which filter
 | |
| 		value - filter mask
 | |
| 		
 | |
| 		or
 | |
| 		
 | |
| 		text  - filter method
 | |
| 		
 | |
| 		Filter method will receive data object and must return true or false
 | |
| 	*/
 | |
| 	filter:function(text,value){
 | |
| 		if (!this.callEvent("onBeforeFilter", [text, value])) return;
 | |
| 		
 | |
| 		//remove previous filtering , if any
 | |
| 		if (this._filter_order){
 | |
| 			this.order = this._filter_order;
 | |
| 			delete this._filter_order;
 | |
| 		}
 | |
| 		
 | |
| 		if (!this.order.length) return;
 | |
| 		
 | |
| 		//if text not define -just unfilter previous state and exit
 | |
| 		if (text){
 | |
| 			var filter = text;
 | |
| 			value = value||"";
 | |
| 			if (typeof text == "string"){
 | |
| 				text = dhtmlx.Template.fromHTML(text);
 | |
| 				value = value.toString().toLowerCase();
 | |
| 				filter = function(obj,value){	//default filter - string start from, case in-sensitive
 | |
| 					return text(obj).toLowerCase().indexOf(value)!=-1;
 | |
| 				};
 | |
| 			}
 | |
| 			
 | |
| 					
 | |
| 			var neworder = dhtmlx.toArray();
 | |
| 			for (var i=0; i < this.order.length; i++){
 | |
| 				var id = this.order[i];
 | |
| 				if (filter(this.item(id),value))
 | |
| 					neworder.push(id);
 | |
| 			}
 | |
| 			//set new order of items, store original
 | |
| 			this._filter_order = this.order;
 | |
| 			this.order = neworder;
 | |
| 		}
 | |
| 		//repaint self
 | |
| 		this.refresh();
 | |
| 		
 | |
| 		this.callEvent("onAfterFilter", []);
 | |
| 	},
 | |
| 	/*
 | |
| 		Iterate through collection
 | |
| 	*/
 | |
| 	each:function(method,master){
 | |
| 		for (var i=0; i<this.order.length; i++)
 | |
| 			method.call((master||this), this.item(this.order[i]));
 | |
| 	},
 | |
| 	/*
 | |
| 		map inner methods to some distant object
 | |
| 	*/
 | |
| 	provideApi:function(target,eventable){
 | |
| 		this.debug_bind_master = target;
 | |
| 			
 | |
| 		if (eventable){
 | |
| 			this.mapEvent({
 | |
| 				onbeforesort:	target,
 | |
| 				onaftersort:	target,
 | |
| 				onbeforeadd:	target,
 | |
| 				onafteradd:		target,
 | |
| 				onbeforedelete:	target,
 | |
| 				onafterdelete:	target,
 | |
| 				onbeforeupdate: target/*,
 | |
| 				onafterfilter:	target,
 | |
| 				onbeforefilter:	target*/
 | |
| 			});
 | |
| 		}
 | |
| 			
 | |
| 		var list = ["get","set","sort","add","remove","exists","idByIndex","indexById","item","update","refresh","dataCount","filter","next","previous","clearAll","first","last","serialize"];
 | |
| 		for (var i=0; i < list.length; i++)
 | |
| 			target[list[i]]=dhtmlx.methodPush(this,list[i]);
 | |
| 			
 | |
| 		if (dhtmlx.assert_enabled())		
 | |
| 			this.assert_event(target);
 | |
| 	},
 | |
| 	/*
 | |
| 		serializes data to a json object
 | |
| 	*/
 | |
| 	serialize: function(){
 | |
| 		var ids = this.order;
 | |
| 		var result = [];
 | |
| 		for(var i=0; i< ids.length;i++)
 | |
| 			result.push(this.pull[ids[i]]); 
 | |
| 		return result;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.sort = {
 | |
| 	create:function(config){
 | |
| 		return dhtmlx.sort.dir(config.dir, dhtmlx.sort.by(config.by, config.as));
 | |
| 	},
 | |
| 	as:{
 | |
| 		"int":function(a,b){
 | |
| 			a = a*1; b=b*1;
 | |
| 			return a>b?1:(a<b?-1:0);
 | |
| 		},
 | |
| 		"string_strict":function(a,b){
 | |
| 			a = a.toString(); b=b.toString();
 | |
| 			return a>b?1:(a<b?-1:0);
 | |
| 		},
 | |
| 		"string":function(a,b){
 | |
| 			a = a.toString().toLowerCase(); b=b.toString().toLowerCase();
 | |
| 			return a>b?1:(a<b?-1:0);
 | |
| 		}
 | |
| 	},
 | |
| 	by:function(prop, method){
 | |
| 		if (!prop)
 | |
| 			return method;
 | |
| 		if (typeof method != "function")
 | |
| 			method = dhtmlx.sort.as[method||"string"];
 | |
| 		prop = dhtmlx.Template.fromHTML(prop);
 | |
| 		return function(a,b){
 | |
| 			return method(prop(a),prop(b));
 | |
| 		};
 | |
| 	},
 | |
| 	dir:function(prop, method){
 | |
| 		if (prop == "asc")
 | |
| 			return method;
 | |
| 		return function(a,b){
 | |
| 			return method(a,b)*-1;
 | |
| 		};
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'key.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Behavior:KeyEvents - hears keyboard 
 | |
| */
 | |
| dhtmlx.KeyEvents = {
 | |
| 	_init:function(){
 | |
| 		//attach handler to the main container
 | |
| 		dhtmlx.event(this._obj,"keypress",this._onKeyPress,this);
 | |
| 	},
 | |
| 	//called on each key press , when focus is inside of related component
 | |
| 	_onKeyPress:function(e){
 | |
| 		e=e||event;
 | |
| 		var code = e.which||e.keyCode; //FIXME  better solution is required
 | |
| 		this.callEvent((this._edit_id?"onEditKeyPress":"onKeyPress"),[code,e.ctrlKey,e.shiftKey,e]);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'mouse.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Behavior:MouseEvents - provides inner evnets for  mouse actions
 | |
| */
 | |
| dhtmlx.MouseEvents={
 | |
| 	_init: function(){
 | |
| 		//attach dom events if related collection is defined
 | |
| 		if (this.on_click){
 | |
| 			dhtmlx.event(this._obj,"click",this._onClick,this);
 | |
| 			dhtmlx.event(this._obj,"contextmenu",this._onContext,this);
 | |
| 		}
 | |
| 		if (this.on_dblclick)
 | |
| 			dhtmlx.event(this._obj,"dblclick",this._onDblClick,this);
 | |
| 		if (this.on_mouse_move){
 | |
| 			dhtmlx.event(this._obj,"mousemove",this._onMouse,this);
 | |
| 			dhtmlx.event(this._obj,(dhtmlx._isIE?"mouseleave":"mouseout"),this._onMouse,this);
 | |
| 		}
 | |
| 
 | |
| 	},
 | |
| 	//inner onclick object handler
 | |
| 	_onClick: function(e) {
 | |
| 		return this._mouseEvent(e,this.on_click,"ItemClick");
 | |
| 	},
 | |
| 	//inner ondblclick object handler
 | |
| 	_onDblClick: function(e) {
 | |
| 		return this._mouseEvent(e,this.on_dblclick,"ItemDblClick");
 | |
| 	},
 | |
| 	//process oncontextmenu events
 | |
| 	_onContext: function(e) {
 | |
| 		var id = dhtmlx.html.locate(e, this._id);
 | |
| 		if (id && !this.callEvent("onBeforeContextMenu", [id,e]))
 | |
| 			return dhtmlx.html.preventEvent(e);
 | |
| 	},
 | |
| 	/*
 | |
| 		event throttler - ignore events which occurs too fast
 | |
| 		during mouse moving there are a lot of event firing - we need no so much
 | |
| 		also, mouseout can fire when moving inside the same html container - we need to ignore such fake calls
 | |
| 	*/
 | |
| 	_onMouse:function(e){
 | |
| 		if (dhtmlx._isIE)	//make a copy of event, will be used in timed call
 | |
| 			e = document.createEventObject(event);
 | |
| 			
 | |
| 		if (this._mouse_move_timer)	//clear old event timer
 | |
| 			window.clearTimeout(this._mouse_move_timer);
 | |
| 				
 | |
| 		//this event just inform about moving operation, we don't care about details
 | |
| 		this.callEvent("onMouseMoving",[e]);
 | |
| 		//set new event timer
 | |
| 		this._mouse_move_timer = window.setTimeout(dhtmlx.bind(function(){
 | |
| 			//called only when we have at least 100ms after previous event
 | |
| 			if (e.type == "mousemove")
 | |
| 				this._onMouseMove(e);
 | |
| 			else
 | |
| 				this._onMouseOut(e);
 | |
| 		},this),500);
 | |
| 	},
 | |
| 	//inner mousemove object handler
 | |
| 	_onMouseMove: function(e) {
 | |
| 		if (!this._mouseEvent(e,this.on_mouse_move,"MouseMove"))
 | |
| 			this.callEvent("onMouseOut",[e||event]);
 | |
| 	},
 | |
| 	//inner mouseout object handler
 | |
| 	_onMouseOut: function(e) {
 | |
| 		this.callEvent("onMouseOut",[e||event]);
 | |
| 	},
 | |
| 	//common logic for click and dbl-click processing
 | |
| 	_mouseEvent:function(e,hash,name){
 | |
| 		e=e||event;
 | |
| 		var trg=e.target||e.srcElement;
 | |
| 		var css = "";
 | |
| 		var id = null;
 | |
| 		var found = false;
 | |
| 		//loop through all parents
 | |
| 		while (trg && trg.parentNode){
 | |
| 			if (!found && trg.getAttribute){													//if element with ID mark is not detected yet
 | |
| 				id = trg.getAttribute(this._id);							//check id of current one
 | |
| 				if (id){
 | |
| 					if (trg.getAttribute("userdata"))
 | |
| 						this.callEvent("onLocateData",[id,trg]);
 | |
| 					if (!this.callEvent("on"+name,[id,e,trg])) return;		//it will be triggered only for first detected ID, in case of nested elements
 | |
| 					found = true;											//set found flag
 | |
| 				}
 | |
| 			}
 | |
| 			css=trg.className;
 | |
| 			if (css){		//check if pre-defined reaction for element's css name exists
 | |
| 				css = css.split(" ");
 | |
| 				css = css[0]||css[1]; //FIXME:bad solution, workaround css classes which are starting from whitespace
 | |
| 				if (hash[css])
 | |
| 					return  hash[css].call(this,e,id||dhtmlx.html.locate(e, this._id),trg);
 | |
| 			}
 | |
| 			trg=trg.parentNode;
 | |
| 		}		
 | |
| 		return found;	//returns true if item was located and event was triggered
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'config.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Behavior:Settings
 | |
| 	
 | |
| 	@export
 | |
| 		customize
 | |
| 		config
 | |
| */
 | |
| 
 | |
| /*DHX:Depend template.js*/
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| dhtmlx.Settings={
 | |
| 	_init:function(){
 | |
| 		/* 
 | |
| 			property can be accessed as this.config.some
 | |
| 			in same time for inner call it have sense to use _settings
 | |
| 			because it will be minified in final version
 | |
| 		*/
 | |
| 		this._settings = this.config= {}; 
 | |
| 	},
 | |
| 	define:function(property, value){
 | |
| 		if (typeof property == "object")
 | |
| 			return this._parseSeetingColl(property);
 | |
| 		return this._define(property, value);
 | |
| 	},
 | |
| 	_define:function(property,value){
 | |
| 		dhtmlx.assert_settings.call(this,property,value);
 | |
| 		
 | |
| 		//method with name {prop}_setter will be used as property setter
 | |
| 		//setter is optional
 | |
| 		var setter = this[property+"_setter"];
 | |
| 		return this._settings[property]=setter?setter.call(this,value):value;
 | |
| 	},
 | |
| 	//process configuration object
 | |
| 	_parseSeetingColl:function(coll){
 | |
| 		if (coll){
 | |
| 			for (var a in coll)				//for each setting
 | |
| 				this._define(a,coll[a]);		//set value through config
 | |
| 		}
 | |
| 	},
 | |
| 	//helper for object initialization
 | |
| 	_parseSettings:function(obj,initial){
 | |
| 		//initial - set of default values
 | |
| 		var settings = dhtmlx.extend({},initial);
 | |
| 		//code below will copy all properties over default one
 | |
| 		if (typeof obj == "object" && !obj.tagName)
 | |
| 			dhtmlx.extend(settings,obj);	
 | |
| 		//call config for each setting
 | |
| 		this._parseSeetingColl(settings);
 | |
| 	},
 | |
| 	_mergeSettings:function(config, defaults){
 | |
| 		for (var key in defaults)
 | |
| 			switch(typeof config[key]){
 | |
| 				case "object": 
 | |
| 					config[key] = this._mergeSettings((config[key]||{}), defaults[key]);
 | |
| 					break;
 | |
| 				case "undefined":
 | |
| 					config[key] = defaults[key];
 | |
| 					break;
 | |
| 				default:	//do nothing
 | |
| 					break;
 | |
| 			}
 | |
| 		return config;
 | |
| 	},
 | |
| 	//helper for html container init
 | |
| 	_parseContainer:function(obj,name,fallback){
 | |
| 		/*
 | |
| 			parameter can be a config object, in such case real container will be obj.container
 | |
| 			or it can be html object or ID of html object
 | |
| 		*/
 | |
| 		if (typeof obj == "object" && !obj.tagName) 
 | |
| 			obj=obj.container;
 | |
| 		this._obj = this.$view = dhtmlx.toNode(obj);
 | |
| 		if (!this._obj && fallback)
 | |
| 			this._obj = fallback(obj);
 | |
| 			
 | |
| 		dhtmlx.assert(this._obj, "Incorrect html container");
 | |
| 		
 | |
| 		this._obj.className+=" "+name;
 | |
| 		this._obj.onselectstart=function(){return false;};	//block selection by default
 | |
| 		this._dataobj = this._obj;//separate reference for rendering modules
 | |
| 	},
 | |
| 	//apply template-type
 | |
| 	_set_type:function(name){
 | |
| 		//parameter can be a hash of settings
 | |
| 		if (typeof name == "object")
 | |
| 			return this.type_setter(name);
 | |
| 		
 | |
| 		dhtmlx.assert(this.types, "RenderStack :: Types are not defined");
 | |
| 		dhtmlx.assert(this.types[name],"RenderStack :: Inccorect type name",name);
 | |
| 		//or parameter can be a name of existing template-type	
 | |
| 		this.type=dhtmlx.extend({},this.types[name]);
 | |
| 		this.customize();	//init configs
 | |
| 	},
 | |
| 	customize:function(obj){
 | |
| 		//apply new properties
 | |
| 		if (obj) dhtmlx.extend(this.type,obj);
 | |
| 		
 | |
| 		//init tempaltes for item start and item end
 | |
| 		this.type._item_start = dhtmlx.Template.fromHTML(this.template_item_start(this.type));
 | |
| 		this.type._item_end = this.template_item_end(this.type);
 | |
| 		
 | |
| 		//repaint self
 | |
| 		this.render();
 | |
| 	},
 | |
| 	//config.type - creates new template-type, based on configuration object
 | |
| 	type_setter:function(value){
 | |
| 		this._set_type(typeof value == "object"?dhtmlx.Type.add(this,value):value);
 | |
| 		return value;
 | |
| 	},
 | |
| 	//config.template - creates new template-type with defined template string
 | |
| 	template_setter:function(value){
 | |
| 		return this.type_setter({template:value});
 | |
| 	},
 | |
| 	//config.css - css name for top level container
 | |
| 	css_setter:function(value){
 | |
| 		this._obj.className += " "+value;
 | |
| 		return value;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'template.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Template - handles html templates
 | |
| */
 | |
| 
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| dhtmlx.Template={
 | |
| 	_cache:{
 | |
| 	},
 | |
| 	empty:function(){	
 | |
| 		return "";	
 | |
| 	},
 | |
| 	setter:function(value){
 | |
| 		return dhtmlx.Template.fromHTML(value);
 | |
| 	},
 | |
| 	obj_setter:function(value){
 | |
| 		var f = dhtmlx.Template.setter(value);
 | |
| 		var obj = this;
 | |
| 		return function(){
 | |
| 			return f.apply(obj, arguments);
 | |
| 		};
 | |
| 	},
 | |
| 	fromHTML:function(str){
 | |
| 		if (typeof str == "function") return str;
 | |
| 		if (this._cache[str])
 | |
| 			return this._cache[str];
 | |
| 			
 | |
| 	//supported idioms
 | |
| 	// {obj} => value
 | |
| 	// {obj.attr} => named attribute or value of sub-tag in case of xml
 | |
| 	// {obj.attr?some:other} conditional output
 | |
| 	// {-obj => sub-template
 | |
| 		str=(str||"").toString();		
 | |
| 		str=str.replace(/[\r\n]+/g,"\\n");
 | |
| 		str=str.replace(/\{obj\.([^}?]+)\?([^:]*):([^}]*)\}/g,"\"+(obj.$1?\"$2\":\"$3\")+\"");
 | |
| 		str=str.replace(/\{common\.([^}\(]*)\}/g,"\"+common.$1+\"");
 | |
| 		str=str.replace(/\{common\.([^\}\(]*)\(\)\}/g,"\"+(common.$1?common.$1(obj):\"\")+\"");
 | |
| 		str=str.replace(/\{obj\.([^}]*)\}/g,"\"+obj.$1+\"");
 | |
| 		str=str.replace(/#([a-z0-9_]+)#/gi,"\"+obj.$1+\"");
 | |
| 		str=str.replace(/\{obj\}/g,"\"+obj+\"");
 | |
| 		str=str.replace(/\{-obj/g,"{obj");
 | |
| 		str=str.replace(/\{-common/g,"{common");
 | |
| 		str="return \""+str+"\";";
 | |
| 		return this._cache[str]= Function("obj","common",str);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| dhtmlx.Type={
 | |
| 	/*
 | |
| 		adds new template-type
 | |
| 		obj - object to which template will be added
 | |
| 		data - properties of template
 | |
| 	*/
 | |
| 	add:function(obj, data){ 
 | |
| 		//auto switch to prototype, if name of class was provided
 | |
| 		if (!obj.types && obj.prototype.types)
 | |
| 			obj = obj.prototype;
 | |
| 		//if (typeof data == "string")
 | |
| 		//	data = { template:data };
 | |
| 			
 | |
| 		if (dhtmlx.assert_enabled())
 | |
| 			this.assert_event(data);
 | |
| 		
 | |
| 		var name = data.name||"default";
 | |
| 		
 | |
| 		//predefined templates - autoprocessing
 | |
| 		this._template(data);
 | |
| 		this._template(data,"edit");
 | |
| 		this._template(data,"loading");
 | |
| 		
 | |
| 		obj.types[name]=dhtmlx.extend(dhtmlx.extend({},(obj.types[name]||this._default)),data);	
 | |
| 		return name;
 | |
| 	},
 | |
| 	//default template value - basically empty box with 5px margin
 | |
| 	_default:{
 | |
| 		css:"default",
 | |
| 		template:function(){ return ""; },
 | |
| 		template_edit:function(){ return ""; },
 | |
| 		template_loading:function(){ return "..."; },
 | |
| 		width:150,
 | |
| 		height:80,
 | |
| 		margin:5,
 | |
| 		padding:0
 | |
| 	},
 | |
| 	//template creation helper
 | |
| 	_template:function(obj,name){ 
 | |
| 		name = "template"+(name?("_"+name):"");
 | |
| 		var data = obj[name];
 | |
| 		//if template is a string - check is it plain string or reference to external content
 | |
| 		if (data && (typeof data == "string")){
 | |
| 			if (data.indexOf("->")!=-1){
 | |
| 				data = data.split("->");
 | |
| 				switch(data[0]){
 | |
| 					case "html": 	//load from some container on the page
 | |
| 						data = dhtmlx.html.getValue(data[1]).replace(/\"/g,"\\\"");
 | |
| 						break;
 | |
| 					case "http": 	//load from external file
 | |
| 						data = new dhtmlx.ajax().sync().get(data[1],{uid:(new Date()).valueOf()}).responseText;
 | |
| 						break;
 | |
| 					default:
 | |
| 						//do nothing, will use template as is
 | |
| 						break;
 | |
| 				}
 | |
| 			}
 | |
| 			obj[name] = dhtmlx.Template.fromHTML(data);
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'single_render.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	REnders single item. 
 | |
| 	Can be used for elements without datastore, or with complex custom rendering logic
 | |
| 	
 | |
| 	@export
 | |
| 		render
 | |
| */
 | |
| 
 | |
| /*DHX:Depend template.js*/
 | |
| 
 | |
| dhtmlx.SingleRender={
 | |
| 	_init:function(){
 | |
| 	},
 | |
| 	//convert item to the HTML text
 | |
| 	_toHTML:function(obj){
 | |
| 			/*
 | |
| 				this one doesn't support per-item-$template
 | |
| 				it has not sense, because we have only single item per object
 | |
| 			*/
 | |
| 			return this.type._item_start(obj,this.type)+this.type.template(obj,this.type)+this.type._item_end;
 | |
| 	},
 | |
| 	//render self, by templating data object
 | |
| 	render:function(){
 | |
| 		if (!this.callEvent || this.callEvent("onBeforeRender",[this.data])){
 | |
| 			if (this.data)
 | |
| 				this._dataobj.innerHTML = this._toHTML(this.data);
 | |
| 			if (this.callEvent) this.callEvent("onAfterRender",[]);
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'tooltip.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	UI: Tooltip
 | |
| 	
 | |
| 	@export
 | |
| 		show
 | |
| 		hide
 | |
| */
 | |
| 
 | |
| /*DHX:Depend tooltip.css*/
 | |
| /*DHX:Depend template.js*/
 | |
| /*DHX:Depend single_render.js*/
 | |
| 
 | |
| dhtmlx.ui.Tooltip=function(container){
 | |
| 	this.name = "Tooltip";
 | |
| 	this.version = "3.0";
 | |
| 	
 | |
| 	if (dhtmlx.assert_enabled()) this._assert();
 | |
| 
 | |
| 	if (typeof container == "string"){
 | |
| 		container = { template:container };
 | |
| 	}
 | |
| 		
 | |
| 	dhtmlx.extend(this, dhtmlx.Settings);
 | |
| 	dhtmlx.extend(this, dhtmlx.SingleRender);
 | |
| 	this._parseSettings(container,{
 | |
| 		type:"default",
 | |
| 		dy:0,
 | |
| 		dx:20
 | |
| 	});
 | |
| 	
 | |
| 	//create  container for future tooltip
 | |
| 	this._dataobj = this._obj = document.createElement("DIV");
 | |
| 	this._obj.className="dhx_tooltip";
 | |
| 	dhtmlx.html.insertBefore(this._obj,document.body.firstChild);
 | |
| };
 | |
| dhtmlx.ui.Tooltip.prototype = {
 | |
| 	//show tooptip
 | |
| 	//pos - object, pos.x - left, pox.y - top
 | |
| 	show:function(data,pos){
 | |
| 		if (this._disabled) return;
 | |
| 		//render sefl only if new data was provided
 | |
| 		if (this.data!=data){
 | |
| 			this.data=data;
 | |
| 			this.render(data);
 | |
| 		}
 | |
| 		//show at specified position
 | |
| 		this._obj.style.top = pos.y+this._settings.dy+"px";
 | |
| 		this._obj.style.left = pos.x+this._settings.dx+"px";
 | |
| 		this._obj.style.display="block";
 | |
| 	},
 | |
| 	//hide tooltip
 | |
| 	hide:function(){
 | |
| 		this.data=null; //nulify, to be sure that on next show it will be fresh-rendered
 | |
| 		this._obj.style.display="none";
 | |
| 	},
 | |
| 	disable:function(){
 | |
| 		this._disabled = true;	
 | |
| 	},
 | |
| 	enable:function(){
 | |
| 		this._disabled = false;
 | |
| 	},
 | |
| 	types:{
 | |
| 		"default":dhtmlx.Template.fromHTML("{obj.id}")
 | |
| 	},
 | |
| 	template_item_start:dhtmlx.Template.empty,
 | |
| 	template_item_end:dhtmlx.Template.empty
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'autotooltip.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Behavior: AutoTooltip - links tooltip to data driven item
 | |
| */
 | |
| 
 | |
| /*DHX:Depend tooltip.js*/
 | |
| 
 | |
| dhtmlx.AutoTooltip = {
 | |
| 	tooltip_setter:function(value){
 | |
| 		var t = new dhtmlx.ui.Tooltip(value);
 | |
| 		this.attachEvent("onMouseMove",function(id,e){	//show tooltip on mousemove
 | |
| 			t.show(this.get(id),dhtmlx.html.pos(e));
 | |
| 		});
 | |
| 		this.attachEvent("onMouseOut",function(id,e){	//hide tooltip on mouseout
 | |
| 			t.hide();
 | |
| 		});
 | |
| 		this.attachEvent("onMouseMoving",function(id,e){	//hide tooltip just after moving start
 | |
| 			t.hide();
 | |
| 		});
 | |
| 		return t;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'compatibility.js'*/
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Collection of compatibility hacks
 | |
| */
 | |
| 
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| 
 | |
| dhtmlx.compat=function(name, obj){
 | |
| 	//check if name hash present, and applies it when necessary
 | |
| 	if (dhtmlx.compat[name])
 | |
| 		dhtmlx.compat[name](obj);
 | |
| };
 | |
| 
 | |
| 
 | |
| (function(){
 | |
| 	if (!window.dhtmlxError){
 | |
| 		//dhtmlxcommon is not included
 | |
| 		
 | |
| 		//create fake error tracker for connectors
 | |
| 		var dummy = function(){};
 | |
| 		window.dhtmlxError={ catchError:dummy, throwError:dummy };
 | |
| 		//helpers instead of ones from dhtmlxcommon
 | |
| 		window.convertStringToBoolean=function(value){
 | |
| 			return !!value;
 | |
| 		};
 | |
| 		window.dhtmlxEventable = function(node){
 | |
| 			dhtmlx.extend(node,dhtmlx.EventSystem);
 | |
| 		};
 | |
| 		//imitate ajax layer of dhtmlxcommon
 | |
| 		var loader = {
 | |
| 			getXMLTopNode:function(name){
 | |
| 				
 | |
| 			},
 | |
| 			doXPath:function(path){
 | |
| 				return dhtmlx.DataDriver.xml.xpath(this.xml,path);
 | |
| 			},
 | |
| 			xmlDoc:{
 | |
| 				responseXML:true
 | |
| 			}
 | |
| 		};
 | |
| 		//wrap ajax methods of dataprocessor
 | |
| 		dhtmlx.compat.dataProcessor=function(obj){
 | |
| 			//FIXME
 | |
| 			//this is pretty ugly solution - we replace whole method , so changes in dataprocessor need to be reflected here
 | |
| 			
 | |
| 			var sendData = "_sendData";
 | |
| 			var in_progress = "_in_progress";
 | |
| 			var tMode = "_tMode";
 | |
| 			var waitMode = "_waitMode";
 | |
| 			
 | |
| 			obj[sendData]=function(a1,rowId){
 | |
| 		    	if (!a1) return; //nothing to send
 | |
| 		    	if (rowId)
 | |
| 					this[in_progress][rowId]=(new Date()).valueOf();
 | |
| 			    
 | |
| 				if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId)]:[])) return false;				
 | |
| 				
 | |
| 				var a2 = this;
 | |
| 		        var a3=this.serverProcessor;
 | |
| 				if (this[tMode]!="POST")
 | |
| 					//use dhtmlx.ajax instead of old ajax layer
 | |
| 					dhtmlx.ajax().get(a3+((a3.indexOf("?")!=-1)?"&":"?")+this.serialize(a1,rowId),"",function(t,x,xml){
 | |
| 						loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x);
 | |
| 						a2.afterUpdate(a2, null, null, null, loader);
 | |
| 					});
 | |
| 				else
 | |
| 		        	dhtmlx.ajax().post(a3,this.serialize(a1,rowId),function(t,x,xml){
 | |
| 		        		loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x);
 | |
| 		        		a2.afterUpdate(a2, null, null, null, loader);
 | |
| 		    		});
 | |
| 		
 | |
| 				this[waitMode]++;
 | |
| 		    };
 | |
| 		};
 | |
| 	}
 | |
| 	
 | |
| })();
 | |
| 
 | |
| 
 | |
| /* DHX DEPEND FROM FILE 'compatibility_layout.js'*/
 | |
| 
 | |
| 
 | |
| /*DHX:Depend dhtmlx.js*/
 | |
| /*DHX:Depend compatibility.js*/
 | |
| 
 | |
| if (!dhtmlx.attaches)
 | |
| 	dhtmlx.attaches = {};
 | |
| 	
 | |
| dhtmlx.attaches.attachAbstract=function(name, conf){
 | |
| 	var obj = document.createElement("DIV");
 | |
| 	obj.id = "CustomObject_"+dhtmlx.uid();
 | |
| 	obj.style.width = "100%";
 | |
| 	obj.style.height = "100%";
 | |
| 	obj.cmp = "grid";
 | |
| 	document.body.appendChild(obj);
 | |
| 	this.attachObject(obj.id);
 | |
| 	
 | |
| 	conf.container = obj.id;
 | |
| 	
 | |
| 	var that = this.vs[this.av];
 | |
| 	that.grid = new window[name](conf);
 | |
| 	
 | |
| 	that.gridId = obj.id;
 | |
| 	that.gridObj = obj;
 | |
| 	
 | |
| 		
 | |
| 	that.grid.setSizes = function(){
 | |
| 		if (this.resize) this.resize();
 | |
| 		else this.render();
 | |
| 	};
 | |
| 	
 | |
| 	var method_name="_viewRestore";
 | |
| 	return this.vs[this[method_name]()].grid;
 | |
| };
 | |
| dhtmlx.attaches.attachDataView = function(conf){
 | |
| 	return this.attachAbstract("dhtmlXDataView",conf);
 | |
| };
 | |
| dhtmlx.attaches.attachChart = function(conf){
 | |
| 	return this.attachAbstract("dhtmlXChart",conf);
 | |
| };
 | |
| 
 | |
| dhtmlx.compat.layout = function(){};
 | |
| 
 | |
| 
 | |
| 
 |