source: branches/2.2.0.1/calendar/js/dhtmlx/sources/dhtmlxdataprocessor.js @ 4001

Revision 4001, 16.0 KB checked in by rafaelraymundo, 13 years ago (diff)

Ticket #1615 - Componente novo para agenda......................................

Line 
1/**
2        *       @desc: constructor, data processor object
3        *       @param: serverProcessorURL - url used for update
4        *       @type: public
5        */
6function dataProcessor(serverProcessorURL){
7    this.serverProcessor = serverProcessorURL;
8    this.action_param="!nativeeditor_status";
9   
10        this.object = null;
11        this.updatedRows = []; //ids of updated rows
12       
13        this.autoUpdate = true;
14        this.updateMode = "cell";
15        this._tMode="GET";
16        this.post_delim = "_";
17       
18    this._waitMode=0;
19    this._in_progress={};//?
20    this._invalid={};
21    this.mandatoryFields=[];
22    this.messages=[];
23   
24    this.styles={
25        updated:"font-weight:bold;",
26        inserted:"font-weight:bold;",
27        deleted:"text-decoration : line-through;",
28        invalid:"background-color:FFE0E0;",
29        invalid_cell:"border-bottom:2px solid red;",
30        error:"color:red;",
31        clear:"font-weight:normal;text-decoration:none;"
32    };
33   
34    this.enableUTFencoding(true);
35    dhtmlxEventable(this);
36
37    return this;
38    }
39
40dataProcessor.prototype={
41        /**
42        *       @desc: select GET or POST transaction model
43        *       @param: mode - GET/POST
44        *       @param: total - true/false - send records row by row or all at once (for grid only)
45        *       @type: public
46        */
47        setTransactionMode:function(mode,total){
48        this._tMode=mode;
49                this._tSend=total;
50    },
51    escape:function(data){
52        if (this._utf)
53                return encodeURIComponent(data);
54        else
55                return escape(data);
56        },
57    /**
58        *       @desc: allows to set escaping mode
59        *       @param: true - utf based escaping, simple - use current page encoding
60        *       @type: public
61        */     
62        enableUTFencoding:function(mode){
63        this._utf=convertStringToBoolean(mode);
64    },
65    /**
66        *       @desc: allows to define, which column may trigger update
67        *       @param: val - array or list of true/false values
68        *       @type: public
69        */
70        setDataColumns:function(val){
71                this._columns=(typeof val == "string")?val.split(","):val;
72    },
73    /**
74        *       @desc: get state of updating
75        *       @returns:   true - all in sync with server, false - some items not updated yet.
76        *       @type: public
77        */
78        getSyncState:function(){
79                return !this.updatedRows.length;
80        },
81        /**
82        *       @desc: enable/disable named field for data syncing, will use column ids for grid
83        *       @param:   mode - true/false
84        *       @type: public
85        */
86        enableDataNames:function(mode){
87                this._endnm=convertStringToBoolean(mode);
88        },
89        /**
90        *       @desc: enable/disable mode , when only changed fields and row id send to the server side, instead of all fields in default mode
91        *       @param:   mode - true/false
92        *       @type: public
93        */
94        enablePartialDataSend:function(mode){
95                this._changed=convertStringToBoolean(mode);
96        },
97        /**
98        *       @desc: set if rows should be send to server automaticaly
99        *       @param: mode - "row" - based on row selection changed, "cell" - based on cell editing finished, "off" - manual data sending
100        *       @type: public
101        */
102        setUpdateMode:function(mode,dnd){
103                this.autoUpdate = (mode=="cell");
104                this.updateMode = mode;
105                this.dnd=dnd;
106        },
107        ignore:function(code,master){
108                this._silent_mode=true;
109                code.call(master||window);
110                this._silent_mode=false;
111        },
112        /**
113        *       @desc: mark row as updated/normal. check mandatory fields,initiate autoupdate (if turned on)
114        *       @param: rowId - id of row to set update-status for
115        *       @param: state - true for "updated", false for "not updated"
116        *       @param: mode - update mode name
117        *       @type: public
118        */
119        setUpdated:function(rowId,state,mode){
120                if (this._silent_mode) return;
121                var ind=this.findRow(rowId);
122               
123                mode=mode||"updated";
124                var existing = this.obj.getUserData(rowId,this.action_param);
125                if (existing && mode == "updated") mode=existing;
126                if (state){
127                        this.set_invalid(rowId,false); //clear previous error flag
128                        this.updatedRows[ind]=rowId;
129                        this.obj.setUserData(rowId,this.action_param,mode);
130                        if (this._in_progress[rowId])
131                                this._in_progress[rowId]="wait";
132                } else{
133                        if (!this.is_invalid(rowId)){
134                                this.updatedRows.splice(ind,1);
135                                this.obj.setUserData(rowId,this.action_param,"");
136                        }
137                }
138
139                //clear changed flag
140                if (!state)
141                        this._clearUpdateFlag(rowId);
142                       
143                this.markRow(rowId,state,mode);
144                if (state && this.autoUpdate) this.sendData(rowId);
145        },
146        _clearUpdateFlag:function(id){},
147        markRow:function(id,state,mode){
148                var str="";
149                var invalid=this.is_invalid(id);
150                if (invalid){
151                str=this.styles[invalid];
152                state=true;
153        }
154                if (this.callEvent("onRowMark",[id,state,mode,invalid])){
155                        //default logic
156                        str=this.styles[state?mode:"clear"]+str;
157                       
158                this.obj[this._methods[0]](id,str);
159
160                        if (invalid && invalid.details){
161                                str+=this.styles[invalid+"_cell"];
162                                for (var i=0; i < invalid.details.length; i++)
163                                        if (invalid.details[i])
164                                        this.obj[this._methods[1]](id,i,str);
165                        }
166                }
167        },
168        getState:function(id){
169                return this.obj.getUserData(id,this.action_param);
170        },
171        is_invalid:function(id){
172                return this._invalid[id];
173        },
174        set_invalid:function(id,mode,details){
175                if (details) mode={value:mode, details:details, toString:function(){ return this.value.toString(); }};
176                this._invalid[id]=mode;
177        },
178        /**
179        *       @desc: check mandatory fields and varify values of cells, initiate update (if specified)
180        *       @param: rowId - id of row to set update-status for
181        *       @type: public
182        */
183        checkBeforeUpdate:function(rowId){
184                return true;
185        },
186        /**
187        *       @desc: send row(s) values to server
188        *       @param: rowId - id of row which data to send. If not specified, then all "updated" rows will be send
189        *       @type: public
190        */
191        sendData:function(rowId){
192                if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return;
193                if (this.obj.editStop) this.obj.editStop();
194       
195               
196                if(typeof rowId == "undefined" || this._tSend) return this.sendAllData();
197                if (this._in_progress[rowId]) return false;
198               
199                this.messages=[];
200                if (!this.checkBeforeUpdate(rowId) && this.callEvent("onValidatationError",[rowId,this.messages])) return false;
201                this._beforeSendData(this._getRowData(rowId),rowId);
202    },
203    _beforeSendData:function(data,rowId){
204        if (!this.callEvent("onBeforeUpdate",[rowId,this.getState(rowId),data])) return false; 
205                this._sendData(data,rowId);
206    },
207    serialize:function(data, id){
208        if (typeof data == "string")
209                return data;
210        if (typeof id != "undefined")
211                return this.serialize_one(data,"");
212        else{
213                var stack = [];
214                var keys = [];
215                for (var key in data)
216                        if (data.hasOwnProperty(key)){
217                                stack.push(this.serialize_one(data[key],key+this.post_delim));
218                                keys.push(key);
219                                }
220                stack.push("ids="+this.escape(keys.join(",")));
221                return stack.join("&");
222        }
223    },
224    serialize_one:function(data, pref){
225        if (typeof data == "string")
226                return data;
227        var stack = [];
228        for (var key in data)
229                if (data.hasOwnProperty(key))
230                        stack.push(this.escape((pref||"")+key)+"="+this.escape(data[key]));
231                return stack.join("&");
232    },
233    _sendData:function(a1,rowId){
234        if (!a1) return; //nothing to send
235                if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId),a1]:[null, null, a1])) return false;                               
236               
237        if (rowId)
238                        this._in_progress[rowId]=(new Date()).valueOf();
239                var a2=new dtmlXMLLoaderObject(this.afterUpdate,this,true);
240               
241                var a3 = this.serverProcessor+(this._user?(getUrlSymbol(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+this.obj.getUserData(0,"version")].join("&")):"");
242
243                if (this._tMode!="POST")
244                a2.loadXML(a3+((a3.indexOf("?")!=-1)?"&":"?")+this.serialize(a1,rowId));
245                else
246                a2.loadXML(a3,true,this.serialize(a1,rowId));
247
248                this._waitMode++;
249    },
250        sendAllData:function(){
251                if (!this.updatedRows.length) return;                   
252
253                this.messages=[]; var valid=true;
254                for (var i=0; i<this.updatedRows.length; i++)
255                        valid&=this.checkBeforeUpdate(this.updatedRows[i]);
256                if (!valid && !this.callEvent("onValidatationError",["",this.messages])) return false;
257       
258                if (this._tSend)
259                        this._sendData(this._getAllData());
260                else
261                        for (var i=0; i<this.updatedRows.length; i++)
262                                if (!this._in_progress[this.updatedRows[i]]){
263                                        if (this.is_invalid(this.updatedRows[i])) continue;
264                                        this._beforeSendData(this._getRowData(this.updatedRows[i]),this.updatedRows[i]);
265                                        if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return; //block send all for tree
266                                }
267        },
268   
269       
270       
271       
272       
273       
274       
275       
276        _getAllData:function(rowId){
277                var out={};
278                var has_one = false;
279                for(var i=0;i<this.updatedRows.length;i++){
280                        var id=this.updatedRows[i];
281                        if (this._in_progress[id] || this.is_invalid(id)) continue;
282                        if (!this.callEvent("onBeforeUpdate",[id,this.getState(id)])) continue;
283                        out[id]=this._getRowData(id,id+this.post_delim);
284                        has_one = true;
285                        this._in_progress[id]=(new Date()).valueOf();
286                }
287                return has_one?out:null;
288        },
289       
290       
291        /**
292        *       @desc: specify column which value should be varified before sending to server
293        *       @param: ind - column index (0 based)
294        *       @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
295        *       @type: public
296        */
297        setVerificator:function(ind,verifFunction){
298                this.mandatoryFields[ind] = verifFunction||(function(value){return (value!="");});
299        },
300        /**
301        *       @desc: remove column from list of those which should be verified
302        *       @param: ind - column Index (0 based)
303        *       @type: public
304        */
305        clearVerificator:function(ind){
306                this.mandatoryFields[ind] = false;
307        },
308       
309       
310       
311       
312       
313        findRow:function(pattern){
314                var i=0;
315        for(i=0;i<this.updatedRows.length;i++)
316                    if(pattern==this.updatedRows[i]) break;
317            return i;
318    },
319
320   
321       
322
323
324   
325
326
327
328
329
330        /**
331        *       @desc: define custom actions
332        *       @param: name - name of action, same as value of action attribute
333        *       @param: handler - custom function, which receives a XMl response content for action
334        *       @type: private
335        */
336        defineAction:function(name,handler){
337        if (!this._uActions) this._uActions=[];
338            this._uActions[name]=handler;
339        },
340
341
342
343
344        /**
345*     @desc: used in combination with setOnBeforeUpdateHandler to create custom client-server transport system
346*     @param: sid - id of item before update
347*     @param: tid - id of item after up0ate
348*     @param: action - action name
349*     @type: public
350*     @topic: 0
351*/
352        afterUpdateCallback:function(sid, tid, action, btag) {
353                var marker = sid;
354                var correct=(action!="error" && action!="invalid");
355                if (!correct) this.set_invalid(sid,action);
356                if ((this._uActions)&&(this._uActions[action])&&(!this._uActions[action](btag)))
357                        return (delete this._in_progress[marker]);
358                       
359                if (this._in_progress[marker]!="wait")
360                this.setUpdated(sid, false);
361               
362            var soid = sid;
363       
364            switch (action) {
365            case "inserted":
366            case "insert":
367                if (tid != sid) {
368                    this.obj[this._methods[2]](sid, tid);
369                    sid = tid;
370                }
371                break;
372            case "delete":
373            case "deleted":
374                this.obj.setUserData(sid, this.action_param, "true_deleted");
375                this.obj[this._methods[3]](sid);
376                delete this._in_progress[marker];
377                return this.callEvent("onAfterUpdate", [sid, action, tid, btag]);
378                break;
379            }
380           
381            if (this._in_progress[marker]!="wait"){
382                if (correct) this.obj.setUserData(sid, this.action_param,'');
383                delete this._in_progress[marker];
384        } else {
385                delete this._in_progress[marker];
386                this.setUpdated(tid,true,this.obj.getUserData(sid,this.action_param));
387                }
388           
389            this.callEvent("onAfterUpdate", [sid, action, tid, btag]);
390        },
391
392        /**
393        *       @desc: response from server
394        *       @param: xml - XMLLoader object with response XML
395        *       @type: private
396        */
397        afterUpdate:function(that,b,c,d,xml){
398                xml.getXMLTopNode("data"); //fix incorrect content type in IE
399                if (!xml.xmlDoc.responseXML) return;
400                var atag=xml.doXPath("//data/action");
401                for (var i=0; i<atag.length; i++){
402                var btag=atag[i];
403                        var action = btag.getAttribute("type");
404                        var sid = btag.getAttribute("sid");
405                        var tid = btag.getAttribute("tid");
406                       
407                        that.afterUpdateCallback(sid,tid,action,btag);
408                }
409                that.finalizeUpdate();
410        },
411        finalizeUpdate:function(){
412                if (this._waitMode) this._waitMode--;
413               
414                if ((this.obj.mytype=="tree" || this.obj._h2) && this.updatedRows.length)
415                        this.sendData();
416                this.callEvent("onAfterUpdateFinish",[]);
417                if (!this.updatedRows.length)
418                        this.callEvent("onFullSync",[]);
419        },
420
421
422
423
424       
425        /**
426        *       @desc: initializes data-processor
427        *       @param: anObj - dhtmlxGrid object to attach this data-processor to
428        *       @type: public
429        */
430        init:function(anObj){
431                this.obj = anObj;
432                if (this.obj._dp_init)
433                        this.obj._dp_init(this);
434        },
435       
436       
437        setOnAfterUpdate:function(ev){
438                this.attachEvent("onAfterUpdate",ev);
439        },
440        enableDebug:function(mode){
441        },
442        setOnBeforeUpdateHandler:function(func){ 
443                this.attachEvent("onBeforeDataSending",func);
444        },
445
446
447
448        /*! starts autoupdate mode
449                @param interval
450                        time interval for sending update requests
451        */
452        setAutoUpdate: function(interval, user) {
453                interval = interval || 2000;
454               
455                this._user = user || (new Date()).valueOf();
456                this._need_update = false;
457                this._loader = null;
458                this._update_busy = false;
459               
460                this.attachEvent("onAfterUpdate",function(sid,action,tid,xml_node){
461                        this.afterAutoUpdate(sid, action, tid, xml_node);
462                });
463                this.attachEvent("onFullSync",function(){
464                        this.fullSync();
465                });
466               
467                var self = this;
468                window.setInterval(function(){
469                        self.loadUpdate();
470                }, interval);
471        },
472
473
474        /*! process updating request answer
475                if status == collision version is depricated
476                set flag for autoupdating immidiatly
477        */
478        afterAutoUpdate: function(sid, action, tid, xml_node) {
479                if (action == 'collision') {
480                        this._need_update = true;
481                        return false;
482                } else {
483                        return true;
484                }
485        },
486
487
488        /*! callback function for onFillSync event
489                call update function if it's need
490        */
491        fullSync: function() {
492                if (this._need_update == true) {
493                        this._need_update = false;
494                        this.loadUpdate();
495                }
496                return true;
497        },
498
499
500        /*! sends query to the server and call callback function
501        */
502        getUpdates: function(url,callback){
503                if (this._update_busy)
504                        return false;
505                else
506                        this._update_busy = true;
507               
508                this._loader = this._loader || new dtmlXMLLoaderObject(true);
509               
510                this._loader.async=true;
511                this._loader.waitCall=callback;
512                this._loader.loadXML(url);
513        },
514
515
516        /*! returns xml node value
517                @param node
518                        xml node
519        */
520        _v: function(node) {
521                if (node.firstChild) return node.firstChild.nodeValue;
522                return "";
523        },
524
525
526        /*! returns values array of xml nodes array
527                @param arr
528                        array of xml nodes
529        */
530        _a: function(arr) {
531                var res = [];
532                for (var i=0; i < arr.length; i++) {
533                        res[i]=this._v(arr[i]);
534                };
535                return res;
536        },
537
538
539        /*! loads updates and processes them
540        */
541        loadUpdate: function(){
542                var self = this;
543                var version = this.obj.getUserData(0,"version");
544                var url = this.serverProcessor+getUrlSymbol(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+version].join("&");
545                url = url.replace("editing=true&","");
546                this.getUpdates(url, function(){
547                        var vers = self._loader.doXPath("//userdata");
548                        self.obj.setUserData(0,"version",self._v(vers[0]));
549                       
550                        var upds = self._loader.doXPath("//update");
551                        if (upds.length){
552                                self._silent_mode = true;
553                               
554                                for (var i=0; i<upds.length; i++) {
555                                        var status = upds[i].getAttribute('status');
556                                        var id = upds[i].getAttribute('id');
557                                        var parent = upds[i].getAttribute('parent');
558                                        switch (status) {
559                                                case 'inserted':
560                                                        self.callEvent("insertCallback",[upds[i], id, parent]);
561                                                        break;
562                                                case 'updated':
563                                                        self.callEvent("updateCallback",[upds[i], id, parent]);
564                                                        break;
565                                                case 'deleted':
566                                                        self.callEvent("deleteCallback",[upds[i], id, parent]);
567                                                        break;
568                                        }
569                                }
570                               
571                                self._silent_mode = false;
572                        }
573                       
574                        self._update_busy = false;
575                        self = null;
576                });
577        }
578
579};
580
581//(c)dhtmlx ltd. www.dhtmlx.com
Note: See TracBrowser for help on using the repository browser.