PageSourceSearch

https://web.rosknroll.fi/rosknroll/js/dhtmlxSuite/sources/dhtmlxCommon/codebase/dhtmlxdataprocessor.js

js rosknroll.fi collected 2026-09-26 03:00:20 UTC 20,731 bytes, 737 lines download raw bytes

1/*
2Product Name: dhtmlxSuite 
3Version: 5.1.0 
4Edition: Professional 
5License: content of this file is covered by DHTMLX Commercial or Enterprise license. Usage without proper license is prohibited. To obtain it contact [email protected]
6Copyright UAB Dinamenta http://www.dhtmlx.com
7*/
8
9/**
10	* 	@desc: constructor, data processor object 
11	*	@param: serverProcessorURL - url used for update
12	*	@type: public
13	*/
14function dataProcessor(serverProcessorURL){
15    this.serverProcessor = serverProcessorURL;
16    this.action_param="!nativeeditor_status";
17    
18	this.object = null;
19	this.updatedRows = []; //ids of updated rows
20	
21	this.autoUpdate = true;
22	this.updateMode = "cell";
23	this._tMode="GET"; 
24	this._headers = null;
25	this._payload = null;
26	this.post_delim = "_";
27	
28    this._waitMode=0;
29    this._in_progress={};//?
30    this._invalid={};
31    this.mandatoryFields=[];
32    this.messages=[];
33    
34    this.styles={
35    	updated:"font-weight:bold;",
36    	inserted:"font-weight:bold;",
37    	deleted:"text-decoration : line-through;",
38    	invalid:"background-color:FFE0E0;",
39    	invalid_cell:"border-bottom:2px solid red;",
40    	error:"color:red;",
41    	clear:"font-weight:normal;text-decoration:none;"
42    };
43    
44    this.enableUTFencoding(true);
45    dhx4._eventable(this);
46
47    if (this.connector_init){
48    	this.setTransactionMode("POST",true);
49		this.serverProcessor+=(this.serverProcessor.indexOf("?")!=-1?"&":"?")+"editing=true";
50	};
51
52    return this;
53    }
54
55dataProcessor.prototype={
56	url:function(str){
57		if (str.indexOf("?") != -1)
58			return "&";
59		else
60			return "?";
61	},
62	setTransactionMode:function(mode,total){
63		if (typeof mode == "object"){
64			this._tMode = mode.mode || this._tMode;
65			this._headers = this._headers || mode.headers;
66			this._payload = this._payload || mode.payload;
67		} else {
68     	    this._tMode=mode;
69			this._tSend=total;
70		}
71
72		if (this._tMode == "REST"){
73			this._tSend = false;
74			this._endnm = true;
75		}
76		if (this._tMode == "JSON"){
77			this._tSend = false;
78			this._endnm = true;
79			this._headers = this._headers || {};
80			this._headers["Content-type"] = "application/json";
81		}
82    },
83    escape:function(data){
84    	if (this._utf)
85    		return encodeURIComponent(data);
86    	else
87        	return escape(data);
88	},
89    /**
90	* 	@desc: allows to set escaping mode
91	*	@param: true - utf based escaping, simple - use current page encoding
92	*	@type: public
93	*/	
94	enableUTFencoding:function(mode){
95        this._utf=dhx4.s2b(mode);
96    },
97    /**
98	* 	@desc: allows to define, which column may trigger update
99	*	@param: val - array or list of true/false values
100	*	@type: public
101	*/
102	setDataColumns:function(val){
103		this._columns=(typeof val == "string")?val.split(","):val;
104    },
105    /**
106	* 	@desc: get state of updating
107	*	@returns:   true - all in sync with server, false - some items not updated yet.
108	*	@type: public
109	*/
110	getSyncState:function(){
111		return !this.updatedRows.length;
112	},
113	/**
114	* 	@desc: enable/disable named field for data syncing, will use column ids for grid
115	*	@param:   mode - true/false
116	*	@type: public
117	*/
118	enableDataNames:function(mode){
119		this._endnm=dhx4.s2b(mode);
120	},
121	/**
122	* 	@desc: enable/disable mode , when only changed fields and row id send to the server side, instead of all fields in default mode
123	*	@param:   mode - true/false
124	*	@type: public
125	*/
126	enablePartialDataSend:function(mode){
127		this._changed=dhx4.s2b(mode);
128	},
129	/**
130	* 	@desc: set if rows should be send to server automaticaly
131	*	@param: mode - "row" - based on row selection changed, "cell" - based on cell editing finished, "off" - manual data sending
132	*	@type: public
133	*/
134	setUpdateMode:function(mode,dnd){
135		this.autoUpdate = (mode=="cell");
136		this.updateMode = mode;
137		this.dnd=dnd;
138	},
139	ignore:function(code,master){
140		this._silent_mode=true;
141		code.call(master||window);
142		this._silent_mode=false;
143	},
144	/**
145	* 	@desc: mark row as updated/normal. check mandatory fields,initiate autoupdate (if turned on)
146	*	@param: rowId - id of row to set update-status for
147	*	@param: state - true for "updated", false for "not updated"
148	*	@param: mode - update mode name
149	*	@type: public
150	*/
151	setUpdated:function(rowId,state,mode){
152		this._log("item "+rowId+" "+(state?"marked":"unmarked")+" ["+(mode||"updated")+"]");
153
154		if (this._silent_mode) return;
155		var ind=this.findRow(rowId);
156		
157		mode=mode||"updated";
158		var existing = this.obj.getUserData(rowId,this.action_param);
159		if (existing && mode == "updated") mode=existing;
160		if (state){
161			this.set_invalid(rowId,false); //clear previous error flag
162			this.updatedRows[ind]=rowId;
163			this.obj.setUserData(rowId,this.action_param,mode);
164			if (this._in_progress[rowId]) 
165				this._in_progress[rowId]="wait";
166		} else{
167			if (!this.is_invalid(rowId)){
168				this.updatedRows.splice(ind,1);
169				this.obj.setUserData(rowId,this.action_param,"");
170			}
171		}
172
173		//clear changed flag
174		if (!state)
175			this._clearUpdateFlag(rowId);
176     			
177		this.markRow(rowId,state,mode);
178		if (state && this.autoUpdate) this.sendData(rowId);
179	},
180	_clearUpdateFlag:function(id){},
181	markRow:function(id,state,mode){ 
182		var str="";
183		var invalid=this.is_invalid(id);
184		if (invalid){
185        	str=this.styles[invalid];
186        	state=true;
187    	}
188		if (this.callEvent("onRowMark",[id,state,mode,invalid])){
189			//default logic
190			str=this.styles[state?mode:"clear"]+str;
191			
192        	this.obj[this._methods[0]](id,str);
193
194			if (invalid && invalid.details){
195				str+=this.styles[invalid+"_cell"];
196				for (var i=0; i < invalid.details.length; i++)
197					if (invalid.details[i])
198        				this.obj[this._methods[1]](id,i,str);
199			}
200		}
201	},
202	getState:function(id){
203		return this.obj.getUserData(id,this.action_param);
204	},
205	is_invalid:function(id){
206		return this._invalid[id];
207	},
208	set_invalid:function(id,mode,details){ 
209		if (details) mode={value:mode, details:details, toString:function(){ return this.value.toString(); }};
210		this._invalid[id]=mode;
211	},
212	/**
213	* 	@desc: check mandatory fields and varify values of cells, initiate update (if specified)
214	*	@param: rowId - id of row to set update-status for
215	*	@type: public
216	*/
217	checkBeforeUpdate:function(rowId){ 
218		return true;
219	},
220	/**
221	* 	@desc: send row(s) values to server
222	*	@param: rowId - id of row which data to send. If not specified, then all "updated" rows will be send
223	*	@type: public
224	*/
225	sendData:function(rowId){
226		if (rowId)
227			this._log("Sending: "+rowId);
228
229		if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return;
230		if (this.obj.editStop) this.obj.editStop();
231	
232		
233		if(typeof rowId == "undefined" || this._tSend) return this.sendAllData();
234		if (this._in_progress[rowId]) return false;
235		
236		this.messages=[];
237		if (this.getState(rowId) !== "deleted")
238			if (!this.checkBeforeUpdate(rowId) && this.callEvent("onValidationError",[rowId,this.messages])) return false;
239
240		this._beforeSendData(this._getRowData(rowId),rowId);
241    },
242    _beforeSendData:function(data,rowId){
243    	if (!this.callEvent("onBeforeUpdate",[rowId,this.getState(rowId),data])) return false;	
244		this._sendData(data,rowId);
245    },
246    serialize:function(data, id){
247    	if (typeof data == "string")
248    		return data;
249    	if (typeof id != "undefined")
250    		return this.serialize_one(data,"");
251    	else{
252    		var stack = [];
253    		var keys = [];
254    		for (var key in data)
255    			if (data.hasOwnProperty(key)){
256    				stack.push(this.serialize_one(data[key],key+this.post_delim));
257    				keys.push(key);
258				}
259    		stack.push("ids="+this.escape(keys.join(",")));
260    		if (window.dhtmlx && dhtmlx.security_key)
261				stack.push("dhx_security="+dhtmlx.security_key);
262    		return stack.join("&");
263    	}
264    },
265    serialize_one:function(data, pref){
266    	if (typeof data == "string")
267    		return data;
268    	var stack = [];
269    	for (var key in data)
270    		if (data.hasOwnProperty(key)){
271    			if ((key == "id" || key == this.action_param) && this._tMode == "REST") continue;
272    			stack.push(this.escape((pref||"")+key)+"="+this.escape(data[key]));
273    		}
274		return stack.join("&");
275    },
276    _applyPayload:function(url){
277    	if (this._payload)
278      		for (var key in this._payload)
279       			url = url + (url.indexOf("?") === -1 ? "?" : "&" ) + this.escape(key) + "=" + this.escape(this._payload[key]);
280       	return url;
281    },
282    _sendData:function(a1,rowId){
283    
284    	this._log("url: "+this.serverProcessor);
285    	this._log(a1)
286
287    	if (!a1) return; //nothing to send
288		if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId),a1]:[null, null, a1])) return false;				
289		
290    	if (rowId)
291			this._in_progress[rowId]=(new Date()).valueOf();
292
293		var that = this;
294		var back = function(xml){
295			var ids = [];
296			if (rowId)
297				ids.push(rowId);
298			else if (a1)
299				for (var key in a1)
300					ids.push(key);
301
302			return that.afterUpdate(that,xml,ids);
303		};
304		
305		var a3 = this.serverProcessor+(this._user?(this.url(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+this.obj.getUserData(0,"version")].join("&")):"");
306        var a4 = this._applyPayload(a3);
307
308		if (this._tMode=="GET"){
309	     	dhx4.ajax.query({
310        		url:a4+((a4.indexOf("?")!=-1)?"&":"?")+this.serialize(a1,rowId
310),
311        		method:"GET",
312        		headers:this._headers,
313        		callback:back
314        	});
315		}
316		else if (this._tMode == "POST"){
317			dhx4.ajax.query({
318        		url:a4,
319        		method:"POST",
320        		headers:this._headers,
321        		callback:back,
322        		data:this.serialize(a1,rowId)
323        	});
324		}
325		else if (this._tMode == "JSON"){
326			var action = a1[this.action_param];
327			var data = {};
328			for (var key in a1) data[key] = a1[key];
329			delete data[this.action_param];
330			delete data.id;
331			delete data.gr_id;
332
333			dhx4.ajax.query({
334        		url:a4,
335        		method:"POST",
336        		headers:this._headers,
337        		callback:back,
338        		data:JSON.stringify({
339        			id: rowId,
340        			action: action,
341        			data: data
342        		})
343        	});		
344		}
345        else if (this._tMode == "REST"){
346        	var state = this.getState(rowId);
347        	var url = a3.replace(/(\&|\?)editing\=true/,"");
348        	var parts = url.split("?");
349        	if (parts[1])
350        		parts[1] = "?"+parts[1];
351
352        	var data = "";
353        	var method = "post";
354
355        	if (state == "inserted"){
356        		data = this.serialize(a1, rowId);
357        	} else if (state == "deleted"){
358        		method = "DELETE";
359        		url = parts[0]+rowId+(parts[1]||"");
360        	} else {
361        		method = "PUT";
362        		data = this.serialize(a1, rowId);
363        		url = parts[0]+rowId+(parts[1]||"");
364        	}
365
366        	this._applyPayload(url);
367        	dhx4.ajax.query({
368        		url:url,
369        		method:method,
370        		headers:this._headers,
371        		data:data,
372        		callback:back
373        	});
374        }
375
376		this._waitMode++;
377    },
378	sendAllData:function(){
379		this._log("Sending all updated items");
380		if (!this.updatedRows.length) return;			
381
382		this.messages=[]; var valid=true;
383		for (var i=0; i<this.updatedRows.length; i++)
384			if (this.getState(this.updatedRows[i]) !== "deleted")
385				valid&=this.checkBeforeUpdate(this.updatedRows[i]);
386
387		if (!valid && !this.callEvent("onValidationError",["",this.messages])) return false;
388	
389		if (this._tSend) 
390			this._sendData(this._getAllData());
391		else
392			for (var i=0; i<this.updatedRows.length; i++)
393				if (!this._in_progress[this.updatedRows[i]]){
394					if (this.is_invalid(this.updatedRows[i])) continue;
395					this._beforeSendData(this._getRowData(this.updatedRows[i]),this.updatedRows[i]);
396					if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return; //block send all for tree
397				}
398	},
399    
400	
401	
402	
403	
404	
405	
406	
407	_getAllData:function(rowId){
408		var out={};
409		var has_one = false;
410		for(var i=0;i<this.updatedRows.length;i++){
411			var id=this.updatedRows[i];
412			if (this._in_progress[id] || this.is_invalid(id)) continue;
413			if (!this.callEvent("onBeforeUpdate",[id,this.getState(id), this._getRowData(id)])) continue;
414			out[id]=this._getRowData(id,id+this.post_delim);
415			has_one = true;
416			this._in_progress[id]=(new Date()).valueOf();
417		}
418		return has_one?out:null;
419	},
420	
421	
422	/**
423	* 	@desc: specify column which value should be varified before sending to server
424	*	@param: ind - column index (0 based)
425	*	@param: verifFunction - function (object) which should verify cell value (if not specified, then value will be compared to empty string). Two arguments will be passed into it: value and column name
426	*	@type: public
427	*/
428	setVerificator:function(ind,verifFunction){
429		this.mandatoryFields[ind] = verifFunction||(function(value){return (value!=="");});
430	},
431	/**
432	* 	@desc: remove column from list of those which should be verified
433	*	@param: ind - column Index (0 based)
434	*	@type: public
435	*/
436	clearVerificator:function(ind){
437		this.mandatoryFields[ind] = false;
438	},
439	
440	
441	
442	
443	
444	findRow:function(pattern){
445		var i=0;
446    	for(i=0;i<this.updatedRows.length;i++)
447		    if(pattern==this.updatedRows[i]) break;
448	    return i;
449    },
450
451   
452	
453
454
455    
456
457
458
459
460
461	/**
462	* 	@desc: define custom actions
463	*	@param: name - name of action, same as value of action attribute
464	*	@param: handler - custom function, which receives a XMl response content for action
465	*	@type: private
466	*/
467	defineAction:function(name,handler){
468        if (!this._uActions) this._uActions=[];
469            this._uActions[name]=handler;
470	},
471
472
473
474
475	/**
476*     @desc: used in combination with setOnBeforeUpdateHandler to create custom client-server transport system
477*     @param: sid - id of item before update
478*     @param: tid - id of item after up0ate
479*     @param: action - action name
480*     @type: public
481*     @topic: 0
482*/
483	afterUpdateCallback:function(sid, tid, action, btag) {
484		this._log("Action: "+action+" SID:"+sid+" TID:"+tid, btag);
485		var marker = sid;
486		var correct=(action!="error" && action!="invalid");
487		if (!correct) this.set_invalid(sid,action);
488		if ((this._uActions)&&(this._uActions[action])&&(!this._uActions[action](btag))) 
489			return (delete this._in_progress[marker]);
490			
491		if (this._in_progress[marker]!="wait")
492	    	this.setUpdated(sid, false);
493	    	
494	    var soid = sid;
495	
496	    switch (action) {
497	    case "inserted":
498	    case "insert":
499	        if (tid != sid) {
500	            this.obj[this._methods[2]](sid, tid);
501	            sid = tid;
502	        }
503	        break;
504	    case "delete":
505	    case "deleted":
506	    	this.obj.setUserData(sid, this.action_param, "true_deleted");
507	        this.obj[this._methods[3]](sid);
508	        delete this._in_progress[marker];
509	        return this.callEvent("onAfterUpdate", [sid, action, tid, btag]);
510	        break;
511	    }
512	    
513	    if (this._in_progress[marker]!="wait"){
514	    	if (correct) this.obj.setUserData(sid, this.action_param,'');
515	    	delete this._in_progress[marker];
516    	} else {
517    		delete this._in_progress[marker];
518    		this.setUpdated(tid,true,this.obj.getUserData(sid,this.action_param));
519		}
520	    
521	    this.callEvent("onAfterUpdate", [soid, action, tid, btag]);
522	},
523	enableDebug:function(){
524		this._debug = true;
525	},
526	_log:function(){
527		if (this._debug && window.console && window.console.info)
528			window.console.info.apply(window.console, arguments);
529	},
530	/**
531	* 	@desc: response from server
532	*	@param: xml - XMLLoader object with response XML
533	*	@type: private
534	*/
535	afterUpdate:function(that,xml,id){
536		this._log("Server response received");
537		//try to use json first
538		if (window.JSON){
539			try{
540				var tag = JSON.parse(xml.xmlDoc.responseText);
541				var action = tag.action || this.getState(id) || "updated";
542				var sid = tag.sid || id[0];
543				var tid = tag.tid || id[0];
544				that.afterUpdateCallback(sid, tid, action, tag);
545				that.finalizeUpdate();
546				return;
547			} catch(e){
548			}
549		}
550		//xml response
551		var top = dhx4.ajax.xmltop("data", xml.xmlDoc); //fix incorrect content type in IE
552		if (!top || top.tagName == "DIV") return this.cleanUpdate(id);
553		var atag=dhx4.ajax.xpath("//data/action", top);
554		if (!atag.length) return this.cleanUpdate(id);
555
556		for (var i=0; i<atag.length; i++){
557        	var btag=atag[i];
558			var action = btag.getAttribute("type");
559			var sid = btag.getAttribute("sid");
560			var tid = btag.getAttribute("tid");
561			
562			that.afterUpdateCallback(sid,tid,action,btag);
563		}
564		that.finalizeUpdate();
565	},
566	cleanUpdate:function(id){
567		if (id)
568			for (var i = 0; i < id.length; i++)
569				delete this._in_progress[id[i]];
570	},
571	finalizeUpdate:function(){
572		if (this._waitMode) this._waitMode--;
573		
574		if ((this.obj.mytype=="tree" || this.obj._h2) && this.updatedRows.length) 
575			this.sendData();
576		this.callEvent("onAfterUpdateFinish",[]);
577		if (!this.updatedRows.length)
578			this.callEvent("onFullSync",[]);
579	},
580
581
582
583
584	
585	/**
586	* 	@desc: initializes data-processor
587	*	@param: anObj - dhtmlxGrid object to attach this data-processor to
588	*	@type: public
589	*/
590	init:function(anObj){
591		this.obj = anObj;
592		if (anObj._dp_init) 
593			anObj._dp_init(this);
594		if (this.connector_init)
595			anObj._dataprocessor=this;
596	},
597	
598	
599	setOnAfterUpdate:function(ev){
600		this.attachEvent("onAfterUpdate",ev);
601	},
602	setOnBeforeUpdateHandler:function(func){  
603		this.attachEvent("onBeforeDataSending",func);
604	},
605
606
607
608	/* starts autoupdate mode
609		@param interval
610			time interval for sending update requests
611	*/
612	setAutoUpdate: function(interval, user) {
613		interval = interval || 2000;
614		
615		this._user = user || (new Date()).valueOf();
616		this._need_update = false;
617		this._update_busy = false;
618		
619		this.attachEvent("onAfterUpdate",function(sid,action,tid,xml_node){
620			this.afterAutoUpdate(sid, action, tid, xml_node);
621		});
622		this.attachEvent("onFullSync",function(){
623			this.fullSync();
624		});
625		
626		var self = this;
627		window.setInterval(function(){
628			self.loadUpdate();
629		}, interval);
630	},
631
632
633	/* process updating request answer
634		if status == collision version is depricated
635		set flag for autoupdating immidiatly
636	*/
637	afterAutoUpdate: function(sid, action, tid, xml_node) {
638		if (action == 'collision') {
639			this._need_update = true;
640			return false;
641		} else {
642			return true;
643		}
644	},
645
646
647	/* callback function for onFillSync event
648		call update function if it's need
649	*/
650	fullSync: function() {
651		if (this._need_update == true) {
652			this._need_update = false;
653			this.loadUpdate();
654		}
655		return true;
656	},
657
658
659	/* sends query to the server and call callback function
660	*/
661	getUpdates: function(url,callback){
662		if (this._update_busy) 
663			return false;
664		else
665			this._update_busy = true;
666		
667		dhx4.ajax.get(url, callback);
668	},
669
670
671	/* returns xml node value
672		@param node
673			xml node
674	*/
675	_v: function(node) {
676		if (node.firstChild) return node.firstChild.nodeValue;
677		return "";
678	},
679
680
681	/* returns values array of xml nodes array
682		@param arr
683			array of xml nodes
684	*/
685	_a: function(arr) {
686		var res = [];
687		for (var i=0; i < arr.length; i++) {
688			res[i]=this._v(arr[i]);
689		};
690		return res;
691	},
692
693
694	/* loads updates and processes them
695	*/
696	loadUpdate: function(){
697		var self = this;
698		var version = this.obj.getUserData(0,"version");
699		var url = this.serverProcessor+this.url(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+version].join("&");
700		url = url.replace("editing=true&","");
701		this.getUpdates(url, function(r){
702			var top = dhx4.ajax.xmltop("updates", r.xmlDoc);
703			var vers = dhx4.ajax.xpath("//userdata", top);
704			self.obj.setUserData(0,"version",self._v(vers[0]));
705			
706			var upds = dhx4.ajax.xpath("//update", top);
707			if (upds.length){
708				self._silent_mode = true;
709				
710				for (var i=0; i<upds.length; i++) {
711					var status = upds[i].getAttribute('status');
712					var id = upds[i].getAttribute('id');
713					var parent = upds[i].getAttribute('parent');
714					switch (status) {
715						case 'inserted':
716							self.callEvent("insertCallback",[upds[i], id, parent]);
717							break;
718						case 'updated':
719							self.callEvent("updateCallback",[upds[i], id, parent]);
720							break;
721						case 'deleted':
722							self.callEvent("deleteCallback",[upds[i], id, parent]);
723							break;
724					}
725				}
726				
727				self._silent_mode = false;
728			}
729			
730			self._update_busy = false;
731			self = null;
732		});
733	}
734
735};
736
737//(c)dhtmlx ltd. www.dhtmlx.com

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.