source: trunk/prototype/app/datalayer.js @ 5376

Revision 5376, 58.0 KB checked in by acoutinho, 12 years ago (diff)

Ticket #2434 - Estabilizacao da nova api, correcoes de bug e melhorias na nova agenda

  • Property svn:executable set to *
Line 
1internalUrl = /^([A-z0-9-_]+)(:[A-z0-9-_]+)?$/;
2internalUri = /^([a-zA-Z0-9-_]+)\(([a-zA-Z0-9-_]+)\):\/\/(.*)|([a-zA-Z0-9-_]+):\/\/(.*)$/;
3isGeneratedId = /^\d+\(javascript\)$/;
4arrayName = /^([A-z0-9-_]+)\[\]$/;
5startsDoubleDot = /^:/;
6// cached_urls = {};
7
8$.ajaxPrefilter(function( options, originalOptions, jqXHR ){
9
10      if( options.url != 'undefined' && internalUrl.test( options.url ) ){
11
12//        if( !cached_urls[options.url] )
13//            return;
14//        alert( options.url + " dentro" );
15          jqXHR.abort();
16
17          var callback = ( options.success || options.complete || $.noop );
18
19          switch( options.type.toUpperCase() )
20          {
21            case 'GET':
22                  return callback( DataLayer.get( options.url, /*false,*/ options.data ) );
23
24            case 'POST':
25                  return callback( DataLayer.put( options.url, options.data ) );
26          }
27
28          //return( false );
29
30//        options.url = params[1];
31//        options.data = ( options.data || "" ) + "&" + params[2];
32      }
33
34});
35
36// $("a").live("click", function( event ){
37//
38//     event.preventDefault();
39//
40//     $.ajax({
41//
42//     
43//
44//     });
45//
46// });
47
48$("form").live( "submit", function( event ){
49
50    var $this = $(this), action = $this.attr('action'), res = false,
51   
52    method = $this.attr( 'method' ),
53   
54    fileInputs = $this.find('input[type="file"]');
55   
56    if( fileInputs.length && !$this.is('[enctype="multipart/form-data"]') )
57    {
58        event.preventDefault();
59
60        DataLayer.send( action,
61                        [ method, 'iframe json' ], {},
62                        //TODO: check the type for conversion
63                        DataLayer.receive,
64                        false, { 'formData': $this.serializeArray(),  'fileInput': fileInputs } );
65
66        return( false );
67    }
68   
69    if( res = internalUrl.exec( action ) )
70    {
71        event.preventDefault();
72
73        var data = DataLayer.form( this );
74       
75        switch( method.toUpperCase() )
76        {
77          case 'GET':
78                DataLayer.get( res[0], data );
79
80          case 'POST':
81        DataLayer.put( res[1], data );
82        }
83
84        return( false );
85    }
86
87    return( true );
88});
89
90this.storage = new $.store();
91
92DataLayer = {
93
94    links: {},
95    concepts: {},
96    listeners: {},
97    encoders: {},
98    decoders: {},
99    templates: {},
100    criterias: {},
101    tasks: [],
102
103    render: function( templateName, data, filter, formatter, force ){
104
105        if( $.isFunction( filter ) )
106        {
107            force = formatter;
108            formatter = filter;
109            filter = false;
110        }
111
112        if( typeof data === "string" )
113        {
114            data = this.get( data, filter, force ) || {};
115        }
116       
117        var formatting = function( template ){
118
119              if( template === false ) return( false );
120
121              if( template )
122                  DataLayer.templates[ templateName ] = new EJS({ text: template, cache: false });
123
124              var html = DataLayer.templates[ templateName ].render( { data: data } );
125
126              if( !formatter )
127                  return( html );
128
129              return formatter( html );
130        }
131
132        if( this.templates[ templateName ] )
133        {
134            return formatting();
135        }
136
137        return this.send( DataLayer.templatePath + templateName, 'get', false, formatting, !!!formatter );
138    },
139   
140    send: function( url, type, data, callback, sync, extraOptions ){
141     
142          var result = false, fired = false;
143     
144          var envelope = {
145
146              'async': ( typeof sync !== "undefined" ? !sync : !!callback ),
147              'url': url,
148              'success': function( dt, textStatus, jqXHR ){
149
150                    if( callback )
151                    {
152                        fired = true;
153                        result = callback( dt, textStatus, jqXHR );
154                    }
155                    else
156                        result = dt;
157
158                },
159              'complete': function( jqXHR, textStatus ){
160
161                  if( !fired && callback )
162                      result = callback( false, textStatus, jqXHR );
163
164              },
165
166              'type': $.isArray( type ) ? type[0] : type,
167              'data': data
168
169            };
170
171          if( $.isArray( type ) && type[1] )
172              envelope['dataType'] = type[1];
173
174          if( extraOptions )
175              envelope = $.extend( envelope, extraOptions );
176
177          $.ajax( envelope );
178     
179          return( result );
180    },
181   
182    dispatch: function( dispatcher, data, callback, isPost, dataType ){
183     
184      return this.send( this.dispatchPath + dispatcher + ".php",
185                        [ ( isPost ? 'post' : 'get' ), dataType || 'json' ],
186                        data,
187                        callback );
188
189//       $.ajax({
190//            'async': !!callback,
191//            'url': this.dispatchPath + dispatcher + ".php",
192//            'type': ( isPost ? 'post' : 'get' ),
193//            'dataType': 'json',
194//            'data': data,
195//            'success': function( dt, textStatus, jqXHR ){
196//
197//                  if( callback )
198//                  {
199//                      fired = true;
200//                      callback( dt, textStatus, jqXHR );
201//                  }
202//                  else
203//                      result = dt;
204//
205//              },
206//            'complete': function( jqXHR, textStatus ){
207//
208//                if( !fired && callback )
209//                    callback( false, textStatus, jqXHR );
210//
211//            }/*,
212//            'processData': false*/
213//        });
214
215      //return( result );
216    },
217
218    form: function( target ){
219
220        var params = {}, $this = $(target);
221
222        if( !$this.is( "form" ) )
223            $this = $this.parents( "form" );
224
225        $.each( $this.serializeArray(), function( i, el ){
226
227            if( newName = arrayName.exec( el.name ) )
228                el.name = newName[1];
229            else if( !params[ el.name ] )
230                return( params[ el.name ] = el.value );
231
232            params[ el.name ] = params[ el.name ] || [];
233
234            if( $.type(params[ el.name ]) !== "array" )
235                params[ el.name ] = [ params[ el.name ] ];
236
237            params[ el.name ].push( el.value );
238        });
239
240//      alert(dump(params));
241
242        return this.decode( $this.attr( "action" ), params );
243    },
244
245    blend: function( action, data ){
246
247//      if( notArray = (!$.isArray(data)) )
248//          data = [ data ];
249
250        var form = $('form[action="'+action+'"]');
251
252        form.get(0).reset();
253
254        var named = form.find( 'input[name]' );
255
256        for( var name in data )
257        {
258            named.filter( '[name="'+name+'"]' ).val( data[name] );
259        }
260    },
261
262 
263   
264    put: function( concept, filter, data, oneSide ){
265     
266      ///////////////////////////// normalize ////////////////////////////////
267        if( arguments.length == 2 )
268        {
269            data = filter;
270            filter = false;
271        }
272        if( typeof data === "undefined" ||
273            $.type(data) === "boolean" )
274        {
275            oneSide = data;
276            data = filter;
277            filter = false;
278        }
279       
280        if( !concept || !data )
281            return( false );
282
283        var decoder = "", id = false, bothSides = (typeof oneSide === "undefined"), notArray, res;
284       
285        if( $.type(filter) === "string" )
286        {
287            id = filter;
288            filter = false;
289        }
290
291        if( id )
292            data.id = id;
293
294        if( notArray = ( $.type( data ) !== "array" ) )
295            data = [ data ];
296
297        if( res = internalUrl.exec( concept ) )
298        {
299            //TODO: verificar se a decodificaçao deve ser feita em cada item do array
300            data = this.decode( concept, data );
301            concept = res[1];
302            decoder = res[2];
303        }
304
305      ////////////////////////////////////////////////////////////////////////
306
307        if( bothSides || !oneSide )
308        {
309            var result = false, links = this.links( concept ),
310            current = this.check( concept ) || {}, ids = [];
311
312            for( var i = 0; i < data.length; i++ )
313            {
314                var key = ids[ ids.length ] = data[i].id || this.generateId( concept ), updateSet = {};
315
316                ////////////////////////////// linkage /////////////////////////////////   
317                for( var link in links )
318                {
319                    if( data[i][link] )
320                    {
321                        var isConcept = false;
322                     
323                        if( isConcept = this.isConcept( concept, link ) )
324                            data[i][link] = [ data[i][link] ];
325
326                        var _this = this;
327
328                        $.each( data[i][link], function( ii, el ){
329
330                                var isRef = false;
331
332                                if( isRef = ($.type(el) === "string") )
333                                    el = { id: el };
334
335                                var nestedLinks = _this.links( links[link], true );
336                               
337                                if( isConcept )
338                                {
339                                    el[ nestedLinks[concept] ] = el[ nestedLinks[concept] ] || [];
340                                    el[ nestedLinks[concept] ].push( key );
341                                }
342                                else
343                                    el[ nestedLinks[concept] ] = key;
344
345                                if( isRef && ( !current[ key ] || !current[ key ][ link ] ||
346                                               (isConcept ? current[ key ][ link ] !== el.id : !$.inArray( el.id, current[ key ][ link ] )) ) )
347                                {
348                                    updateSet[ links[link] ] = updateSet[ links[link] ] || [];
349                                    updateSet[ links[link] ].push( el );
350                                }
351                                else if( !isRef )
352                                    data[i][link][ii] = _this.put( links[link], el, oneSide );
353                            });
354
355                        if( isConcept )
356                            data[i][link] = data[i][link][0];
357                    }
358                }
359                //////////////////////////////////////////////////////////////////////////
360
361                if( data[i].id )
362                    data[i] = this.merge( current[ data[i].id ], data[i] );
363
364                 current[ key ] = data[i];
365
366                if( bothSides )
367                  this.report( concept, key, data[i] );
368            }
369
370            this.store( concept, current );
371
372            for( var setKey in updateSet )
373            {
374                if( bothSides )
375                    for( var i = 0; i < updateSet[ setKey ].length; i++ )
376                      this.report( setKey, updateSet[ setKey ][i].id, updateSet[ setKey ][i] );
377                   
378                DataLayer.put( setKey, updateSet[ setKey ], false );
379            }
380        }
381
382        if( oneSide )
383            this.commit( concept, ids/*, true */);
384
385        this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', true );
386
387        return( notArray ? ids[0] : ids );
388
389    },
390   
391    remove: function( concept, id, oneSide ){
392     
393        var bothSides = (typeof oneSide === "undefined"),
394
395        links = this.links( concept ), ids = [],
396
397        current = this.check( concept, id );
398
399        if( !current ) return;
400       
401        if( id )
402            current.id = id;
403
404        if( notArray = ( $.type( current ) !== "array" ) )
405            current = [ current ];
406
407        for( var i = 0; i < current.length; i++ )
408        {
409            var currentId = ids[ ids.length ] = current[i].id;
410
411            if( bothSides )
412              this.report( concept, currentId, false );
413
414            if( bothSides || !oneSide )
415              this.del( concept, currentId );
416
417            for( var link in links )
418            {
419                if( !current[i][link] )
420                    continue;
421
422                var nestedLinks = this.links( links[link], true );
423
424                if( isConcept = this.isConcept( concept, link ) )
425                    current[i][link] = [ current[i][link] ];
426
427                $.each( current[i][link], function( ii, el ){
428
429                        el = DataLayer.storage.cache[links[link]][el];
430
431                        if( notArrayNested = ( $.type( el[ nestedLinks[concept] ] ) !== "array" ) )
432                            el[ nestedLinks[concept] ] = [ el[nestedLinks[concept]] ];
433
434                        el[ nestedLinks[concept] ] = $.grep( el[ nestedLinks[concept] ], function( nested, iii ){
435                            return ( currentId !== nested );
436                        });
437
438                        if( notArrayNested )
439                            el[ nestedLinks[concept] ] = el[ nestedLinks[concept] ][0] || false;
440                        if(!el[ nestedLinks[concept] ] || !el[ nestedLinks[concept] ].length)
441                                delete el[ nestedLinks[concept] ];
442                });
443            }
444        }
445
446        if( oneSide )
447            this.commit( concept, ids );
448
449        this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', false );
450    },
451   
452    report: function( concept, id, data )
453    {     
454        var current = this.check( ':current', concept ) || {};
455
456        if( !current[ id ] )
457            current[ id ] = this.check( concept, id ) || {};
458       
459        this.store( ':current', concept, current );
460
461        var diff = this.diff( current[ id ], data );
462
463        var diffs = this.check( ':diff', concept ) || {};
464
465        if( diffs[ id ] )
466            diff = this.merge( diffs[ id ], diff );
467
468        if( !diff || !$.isEmptyObject( diff ) )
469            diffs[ id ] = diff;
470
471        this.store( ':diff', concept, diffs );
472    },
473
474//     enqueue: function( queueName, concept, id, data ){
475//
476//      var queue = this.check( ':' + queueName, concept ) || {};
477//
478//
479//     },
480//     
481//     dequeue: function( queueName, concept, id ){
482//
483//     
484//
485//     },
486   
487   
488   
489    rollback: function( concept, ids ){
490     
491        var queue = this.prepareQ( 'current', concept, ids );
492
493        ids = [];
494
495        for( var id in queue )
496        {
497             this.put( concept, id, queue[id], false );
498
499             ids[ ids.length ] = id;
500        }
501
502        this.clearQ( concept, ( ids.length ? ids : false ) );
503
504        this.broadcast( concept, 'revert' );
505     
506    },
507   
508    prepareQ: function( queueName, concept, ids ){
509     
510      var notArray = false;
511     
512      if( notArray = ($.type(concept) !== "array") )
513          concept = [ concept ];
514     
515      var q = {};
516     
517      for( var i = 0; i < concept.length; i++ )
518      {
519          var queue = this.check( ':' + queueName, concept[i] || false );
520         
521          if( !queue ) continue;
522
523          if( ids )
524          {
525              if( $.type(ids) !== "array" )
526                  ids = [ ids ];
527
528              var filtered = {};
529
530              for( var ii = 0; ii < ids.length; ii++ )
531              {
532                  filtered[ ids[ii] ] = queue[ ids[ii] ];
533              }
534
535              queue = filtered;
536          }
537
538          q[ concept[i] ] = queue;
539      }
540     
541      return( notArray ? q[ concept[0] ] : q );
542    },
543   
544    clearQ: function( concept, ids ){
545     
546//              var current = this.check( ':current', concept || false );
547        var diffs = this.check( ':diff', concept || false );
548
549        if( !ids )
550           /* current =*/ diffs = {};
551        else
552        {
553            if( notArray = ($.type(ids) !== "array") )
554              ids = [ ids ];
555
556            for( var i = 0; i < ids.length; i++ )
557            {
558//              delete current[ ids[i] ];
559                delete diffs[ ids[i] ];
560            }
561        }
562
563//      this.store( ':current', concept, current );
564        this.store( ':diff', concept, diffs );
565    },
566
567    commit: function( concept, ids, callback ){
568     
569        var queue = this.prepareQ( 'diff', concept, ids );
570
571        this.sync( queue, !$.isArray(concept) && concept || false, callback );
572    },
573   
574    sync: function( queue, concept, callback ){
575
576        if( !queue || $.isEmptyObject( queue ) )
577            return;
578
579        if( concept )
580        {
581          var helper = {};
582          helper[concept] = queue;
583          queue = helper;
584        }
585
586        var data = {}, URIs = {};
587
588        for( var concept in queue )
589            for( var id in queue[concept] )
590            {
591                data[ this.URI( concept, id ) ] = queue[concept][id];
592                URIs[ this.URI( concept, id ) ] = { concept: concept, id: id };
593            }
594
595        if( $.isEmptyObject( data ) )
596            return;
597
598        this.dispatch( "Sync", data, function( data, status, jqXHR ){
599
600//          switch( status )
601//          {
602//            case "error":
603//            case "parsererror":
604//              return DataLayer.rollback( concept, URI );
605//            case "success":
606//              return DataLayer.commit();
607//            case "timeout":
608//            case "notmodified":
609//          }
610
611            var received = DataLayer.receive( data );
612
613            for( var URI in URIs )
614                if( typeof received[URI] !== "undefined" )
615                    DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
616
617            if( callback )
618                callback( received );
619
620//          for( var URI in data )
621//          {
622//              var parsed = DataLayer.parseURI( URI ),
623//   
624//              concept = parsed[1], id = parsed[3];
625//
626//              if( $.type(data[URI]) === "string" )
627//              {
628//                //TODO:threat the exception thrown
629//                DataLayer.rollback( concept, id );
630//                delete URIs[ URI ];
631//                continue;
632//              }
633//
634//              if( data[URI] === false ){
635//                DataLayer.remove( concept, id, false );
636//                continue;
637//              }
638//
639//              if( id !== data[URI].id )
640//                DataLayer.move( concept, id, data[URI].id );
641//             
642//              DataLayer.put( concept, id, data[URI], false );
643//          }
644//         
645//          for( var URI in URIs )
646//               DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
647//         
648//          if( callback )
649//              callback();
650
651        }, true );
652
653    },
654   
655    receive: function( data ){
656     
657        var received = {};
658       
659            for( var URI in data )
660            {
661                var parsed = DataLayer.parseURI( URI ),
662   
663            concept = parsed[4], id = parsed[5];
664
665            received[ URI ] = data[ URI ];
666
667                if( $.type(data[URI]) === "string" )
668                {
669                  //TODO:threat the exception thrown
670                  DataLayer.rollback( concept, id );
671                  continue;
672                }
673
674                if( data[URI] === false ){
675                  DataLayer.remove( concept, id, false );
676                  continue;
677                }
678
679                if( id !== data[URI].id )
680                  DataLayer.move( concept, id, data[URI].id );
681               
682                DataLayer.put( concept, id, data[URI], false );
683            }
684           
685        return( received );
686           
687    },
688   
689    unique: function( origArr ){
690
691        var newArr = [];
692     
693        for ( var x = 0; x < origArr.length; x++ )
694        {
695                var found = false;
696            for ( var y = 0; !found && y < newArr.length; y++ )
697                if ( origArr[x] === newArr[y] ) 
698                  found = true;
699
700            if ( !found )
701                newArr[ newArr.length ] = origArr[x];
702        }
703
704        return newArr;
705    },
706
707    merge: function( current, data ){
708     
709        return this.copy(  data, current );
710
711//      return $.extend( current, data );
712
713    },
714   
715    // clone objects, skip other types.
716    clone: function(target) {
717            if ( typeof target == 'object' ) {
718                    Clone.prototype = target;
719                    return new Clone();
720            } else {
721                    return target;
722            }
723    },
724     
725    // Shallow Copy
726    shallowCopy: function(target) {
727            if (typeof target !== 'object' ) {
728                    return target;  // non-object have value sematics, so target is already a copy.
729            } else {
730                    var value = target.valueOf();
731                    if (target != value) {
732                            // the object is a standard object wrapper for a native type, say String.
733                            // we can make a copy by instantiating a new object around the value.
734                            return new target.constructor(value);
735                    } else {
736                            // ok, we have a normal object. If possible, we'll clone the original's prototype
737                            // (not the original) to get an empty object with the same prototype chain as
738                            // the original.  If just copy the instance properties.  Otherwise, we have to
739                            // copy the whole thing, property-by-property.
740                            if ( target instanceof target.constructor && target.constructor !== Object ) {
741                                    var c = clone(target.constructor.prototype);
742     
743                                    // give the copy all the instance properties of target.  It has the same
744                                    // prototype as target, so inherited properties are already there.
745                                    for ( var property in target) {
746                                            if (target.hasOwnProperty(property)) {
747                                                    c[property] = target[property];
748                                            }
749                                    }
750                            } else {
751                                    var c = {};
752                                    for ( var property in target ) c[property] = target[property];
753                            }
754                           
755                            return c;
756                    }
757            }
758    },
759
760    // entry point for deep copy.
761    // source is the object to be deep copied.
762    // depth is an optional recursion limit. Defaults to 256.
763    // deep copy handles the simple cases itself: non-objects and object's we've seen before.
764    // For complex cases, it first identifies an appropriate DeepCopier, then delegate the details of copying the object to him.
765    copy: function(source, result, depth) {
766     
767            // null is a special case: it's the only value of type 'object' without properties.
768            if ( source === null ) return null;
769
770            // All non-objects use value semantics and don't need explict copying.
771            if ( typeof source !== 'object' ) return source;
772
773            if( !depth || !(depth instanceof RecursionHelper) ) depth = new RecursionHelper(depth);
774
775            var cachedResult = depth.getCachedResult(source);
776
777            // we've already seen this object during this deep copy operation
778            // so can immediately return the result.  This preserves the cyclic
779            // reference structure and protects us from infinite recursion.
780            if ( cachedResult ) return cachedResult;
781
782            // objects may need special handling depending on their class.  There is
783            // a class of handlers call "DeepCopiers"  that know how to copy certain
784            // objects.  There is also a final, generic deep copier that can handle any object.
785            for ( var i=0; i<this.comparators.length; i++ ) {
786
787                    var comparator = this.comparators[i];
788
789                    if ( comparator.can(source) ) {
790       
791                            // once we've identified which DeepCopier to use, we need to call it in a very
792                            // particular order: create, cache, populate.  This is the key to detecting cycles.
793                            // We also keep track of recursion depth when calling the potentially recursive
794                            // populate(): this is a fail-fast to prevent an infinite loop from consuming all
795                            // available memory and crashing or slowing down the browser.
796     
797                            if( !result )
798                                // Start by creating a stub object that represents the copy.
799                                result = comparator.create(source);
800                            else if( !comparator.can(result) )
801                                throw new Error("can't compare diferent kind of objects.");
802
803                            // we now know the deep copy of source should always be result, so if we encounter
804                            // source again during this deep copy we can immediately use result instead of
805                            // descending into it recursively. 
806                            depth.cacheResult(source, result);
807
808                            // only DeepCopier.populate() can recursively deep copy.  So, to keep track
809                            // of recursion depth, we increment this shared counter before calling it,
810                            // and decrement it afterwards.
811                            depth.depth++;
812                            if ( depth.depth > depth.maxDepth ) {
813                                    throw new Error("Exceeded max recursion depth in deep copy.");
814                            }
815
816                            // It's now safe to let the comparator recursively deep copy its properties.
817                            var returned = comparator.populate( function(source, result) { return DataLayer.copy(source, result, depth); }, source, result );
818       
819                                if(returned)
820                                        result = returned;
821
822                            depth.depth--;
823
824                            return result;
825                    }
826            }
827            // the generic copier can handle anything, so we should never reach this line.
828            throw new Error("no DeepCopier is able to copy " + source);
829    },
830
831    // publicly expose the list of deepCopiers.
832    comparators: [],
833
834    // make deep copy() extensible by allowing others to
835    // register their own custom Comparators.
836    registerComparator: function(comparatorOptions) {
837
838          // publicly expose the Comparator class.
839          var comparator = {
840
841              // determines if this Comparator can handle the given object.
842              can: function(source) { return false; },
843   
844              // starts the deep copying process by creating the copy object.  You
845              // can initialize any properties you want, but you can't call recursively
846              // into the copy().
847              create: function(source) { },
848
849              // Completes the deep copy of the source object by populating any properties
850              // that need to be recursively deep copied.  You can do this by using the
851              // provided deepCopyAlgorithm instance's copy() method.  This will handle
852              // cyclic references for objects already deepCopied, including the source object
853              // itself.  The "result" passed in is the object returned from create().
854              populate: function(deepCopyAlgorithm, source, result) {}
855          };
856
857          for ( var key in comparatorOptions ) comparator[key] = comparatorOptions[key];
858
859          this.comparators.unshift( comparator );
860    },
861 
862    diff: function( base, toDiff ){
863
864        if( typeof base === 'undefined' || $.isEmptyObject(base) )
865            return( toDiff );
866
867        if( toDiff === false )
868            return( false );
869
870        toDiff = $.extend( {}, toDiff );
871
872        for( var key in toDiff )
873        {
874            switch( $.type(toDiff[key]) )
875            {
876              case 'object':
877                if( $.isEmptyObject(toDiff[key] = this.diff( base[key], toDiff[key] )) )
878                  delete toDiff[key];
879              break;
880              case 'array':
881                if( base[key] && !(toDiff[key] = $.grep( toDiff[key], function( el, i ){ return( $.inArray( el, base[key] ) === -1 ); } )).length )
882                  delete toDiff[key];
883              break;
884              default:
885                if( base[key] == toDiff[key] )
886                  delete toDiff[key];
887            }
888        }
889
890        return( toDiff );
891
892    },
893   
894    links: function( concept, reverse ){
895
896        if( !this.links[ concept ] )
897        {
898            var result = this.dispatch( "links", { concept: concept } ) || false;
899
900            if( !result )
901                return( false );
902
903            this.concepts[ concept ] = $.extend( this.concepts[ concept ] || {},
904                                                 result['concepts'] || {} );
905
906            this.links[ concept ] =  result['links'] || {};
907        }
908
909        if( reverse )
910        {
911            var reverted = {}, llinks = this.links[ concept ];
912   
913            for( var key in llinks )
914                reverted[ llinks[key] ] = key;
915
916            return( reverted );
917        }
918
919        return( this.links[ concept ] );
920
921    },
922   
923    isConcept: function( concept, attr ){
924     
925        if( typeof this.concepts[concept] === "undefined" )
926        {
927            this.links( concept );
928        }
929
930        return !!this.concepts[ concept ][ attr ];
931    },
932   
933    URI: function( concept, URI, context ){
934     
935        if( res = internalUrl.exec( concept ) )
936            concept = res[1];
937       
938        context = context ? "(" + context + ")" : "";
939     
940        if( URI )
941            return( concept + context + "://" + URI );
942        else
943            return( concept );
944     
945    },
946   
947    parseURI: function( URI ){
948
949        return internalUri.exec( URI ) || false;
950
951    },
952   
953   
954   
955   
956    generateId: function( concept ){
957     
958        var newId = this.counter + "(javascript)";
959     
960        this.store( ":counter", (this.counter++) + "" );
961       
962        return( newId );
963    },
964   
965
966   
967
968    get: function( concept, /*URI, */filter, oneSide ){
969
970        ///////////////////////////// normalize ////////////////////////////////
971        if( arguments.length == 2 && $.type(filter) === "boolean" )
972        {
973            oneSide = filter;
974            filter = false;
975        }
976       
977        var encoder = false, id = false, bothSides = (typeof oneSide === 'undefined'), res;
978       
979        if( $.type(filter) === "string" )
980        {
981            id = filter;
982            filter = false;
983        }
984
985        filter = filter || false;
986
987        if( !concept )
988            return( false );
989
990        if( res = internalUrl.exec( concept ) )
991        {
992            encoder = concept;
993            concept = res[1];
994
995            if( filter )
996                filter = this.criteria( encoder, filter );
997        }
998       
999        if ( $.type(filter) === "array" )
1000        {
1001            filter = { filter: filter, criteria: false };
1002        }
1003       
1004        //////////////////////////////////////////////////////////////////////////
1005       
1006        var result = false;
1007
1008        if( bothSides || !oneSide )
1009            result = this.check( concept, id || filter );
1010
1011        if( !result && (bothSides || oneSide) )
1012        {
1013            result = this.request( concept, id || filter.filter, filter.criteria );
1014
1015            if( result && bothSides && !filter.criteria.format )
1016            {
1017              var newResult = [];
1018           
1019              for( var i = 0; i < result.length; i++ )
1020                  newResult[i] = $.extend( {}, result[i] );
1021
1022              this.put( concept, id, newResult, false );
1023            }
1024        }
1025
1026        if( /*result &&*/ encoder )
1027            result = this.encode( encoder, result, filter ); //TODO: retirar o filtro no método encode
1028
1029        return( result );
1030    },
1031   
1032   
1033    filter: function( base, filter ){
1034     
1035        var filtered = [];
1036     
1037//      var operator = filter.shift();
1038     
1039        /*for( var key in base )
1040        {
1041            switch( operator )
1042            {
1043                case 'AND':
1044                  for( var i = 0, current = true; i < filter.length && current; i++ )
1045                    current = this.compare( '&', current, this.compare( base[key], filter[i] ) );
1046                break;
1047                case 'OR':
1048                  for( var i = 0, current = false; i < filter.length && !current; i++ )
1049                    current = this.compare( '|', current, this.compare( base[key], filter[i] ) );
1050                break;
1051                case 'IN':
1052                  for( var i = 0, current = false; i < filter[1].length && !current; i++ )
1053                    current = this.compare( '|', current, this.compare( base[key], [ '=', filter[0], filter[1][i] ] ) );
1054                break;
1055                default : current = this.compare( operator, base[key], );
1056            }
1057        */   
1058//          if( !noGroup )
1059//              for( var i = 0, current = original; i < filter.length && ( current === original ); i++ )
1060//                  current = this.compare( operator, current, this.compare( base[key], filter[i] ) );
1061
1062//          if( current )
1063//              filtered[ filtered.length ] = key;
1064//      }
1065
1066        return( filtered );
1067    },
1068   
1069    compare: function( operator, base, test ){
1070     
1071      switch( operator )
1072      {
1073          case '*':  return  RegExp( "*" + base + "*" ).test( test );
1074          case '^':  return  RegExp( "^" + base +  "" ).test( test );
1075          case '$':  return  RegExp( ""  + base + "$" ).test( test );
1076
1077          case '&': return ( base && test );
1078          case '|': return ( base || test );
1079
1080          case '=':  return ( base == test );
1081          case '<=': return ( base <= test );
1082          case '>=': return ( base >= test );
1083          case '>':  return ( base <  test );
1084          case '<':  return ( base >  test );
1085      }
1086     
1087    },
1088   
1089//     clone: function( object ){
1090//
1091//      new { prototype: object };
1092//
1093//     },
1094
1095    check: function( namespace, keys ){
1096
1097        if( !namespace )
1098            return( false );
1099
1100        var result = this.storage.get( namespace );
1101
1102        if( !keys || !result )
1103          return( result || false );
1104
1105        if( notArray = $.type(keys) === "string" )
1106            keys = [ keys ];
1107        else if( $.type(keys) !== "array" )
1108            keys = this.filter( result, keys.filter );
1109
1110        var res = [];
1111
1112        for( var i = 0; i < keys.length; i++ )
1113            res[ res.length ] = result[keys[i]];
1114
1115        return( notArray ? res[0] || false : res.length ? res : false );
1116    },
1117
1118    storage: {
1119     
1120        cache: {},
1121     
1122        set: function( key, value ){
1123
1124            this.cache[key] = value;
1125
1126        },
1127        get: function( key ){
1128
1129            return DataLayer.copy( this.cache[key] );
1130
1131        },
1132        del: function( key ){
1133
1134            delete this.cache[key];
1135
1136        }
1137    },
1138
1139    flush: function(){
1140
1141    },
1142   
1143    restore: function(){
1144     
1145    },
1146
1147    store: function( namespace, key, data ){
1148
1149        if( !data )
1150          return this.storage.set( namespace, key );
1151
1152        var res = this.check( namespace ) || {};
1153
1154        res[key] = data;
1155
1156        return this.storage.set( namespace, res );
1157    },
1158
1159    del: function( namespace, key ){
1160     
1161        if( !key )
1162          return this.storage.del( namespace );
1163
1164        var res = this.check( namespace ) || {};
1165
1166        delete res[key];
1167
1168        return this.storage.set( namespace, res );
1169     
1170    },
1171   
1172     move: function( concept, oldId, newId ){
1173
1174        this.put( concept, newId, this.check( concept, oldId ), false );
1175
1176        this.remove( concept, oldId, false );
1177    },
1178   
1179
1180   
1181   
1182   
1183    request: function( concept, filter, criteria ){
1184
1185      var id = false, criteria = criteria || {};
1186
1187      if( $.type(filter) === "string" )
1188      {
1189          id = filter;
1190          filter = false;
1191      }
1192
1193      return this.dispatch( "request", {
1194
1195          concept: concept || '',
1196          id: id || '',
1197          filter: filter || '',
1198          criteria: criteria || '',
1199          service: criteria.service || '',
1200          properties: criteria.properties || ''
1201
1202      } );
1203    },
1204
1205   
1206    //         sync: function( data, callback ){
1207//
1208//      if( !data || $.isEmptyObject( data ) )
1209//          return;
1210//       
1211//      this.send( "Sync", data, function( data, status, jqXHR ){
1212//
1213// //       switch( status )
1214// //       {
1215// //         case "error":
1216// //         case "parsererror":
1217// //           return DataLayer.rollback( concept, URI );
1218// //         case "success":
1219// //           return DataLayer.commit();
1220// //         case "timeout":
1221// //         case "notmodified":
1222// //       }
1223//
1224//          if( callback )
1225//          {
1226//              var result = callback( data, status, jqXHR );
1227//
1228//              if( result === false )
1229//                  return;
1230//              else if( typeof result != "undefined" )
1231//                  data = result;
1232//          }
1233//
1234//          for( var URI in data )
1235//          {
1236//              var parsed = DataLayer.parseURI( URI ),
1237//   
1238//              concept = parsed[1], /*URI = parsed[3],*/
1239//
1240//              links = DataLayer.links( concept );
1241//
1242//              for( var linkName in links )
1243//              {
1244//                  var subURI = data[URI][linkName];
1245//
1246//                  if( subURI && data[subURI] )
1247//                  {
1248//                      data[URI][linkName] = DataLayer.put( linkName, subURI, data[subURI], false );
1249//
1250//                      delete( data[subURI] );
1251//                  }
1252//              }
1253//
1254//              DataLayer.put( concept, URI, data[URI], false );
1255//          }
1256//      }, true );
1257//
1258//     },
1259
1260//     report: function( concept, URI, data, sync )
1261//     {
1262//      var current = this.dequeue( 'current', concept, URI );
1263//
1264//      if( !current )
1265//          this.enqueue( 'current', concept, URI, ( current = this.check( concept, URI ) || {} ) );
1266//
1267//      var diff = this.diff( current, data );
1268//
1269//      if( !diff )
1270//          this.dequeue( 'current', concept, URI, true );
1271//      else
1272//          this.enqueue( 'diff', concept, URI, diff );
1273//     
1274//      if( sync )
1275//          this.commit( concept, URI, function(){
1276//
1277//              DataLayer.set( concept, URI, data, false );
1278//
1279//          });
1280//     },
1281   
1282//     enqueue: function( type, concept, URI, obj ){
1283//       
1284//      //var newURI = this.URI( concept, URI );
1285//     
1286//      if( !this.queue[type] )
1287//          this.queue[type] = {};
1288//
1289//      if( !this.queue['all'] )
1290//          this.queue['all'] = {};
1291//     
1292//      if( !this.queue[type][concept] )
1293//          this.queue[type][concept] = {};
1294//     
1295//      if( !this.queue['all'][type] )
1296//          this.queue['all'][type] = {};
1297//     
1298//      if( !this.queue['all'][type][/*new*/URI] )
1299//          this.queue[type][concept][URI] = this.queue['all'][type][/*new*/URI] = obj;
1300//
1301//      this.store( ':queue', this.queue );
1302//     },
1303//     
1304//     dequeue: function( type, concept, URI, remove ){
1305//       
1306//       ///////////////////////////// normalize ////////////////////////////////
1307//      if( arguments.length < 4 && $.type(URI) === 'boolean' )
1308//      {
1309//          remove = URI;
1310//          URI = false;
1311//      }
1312//      if( arguments.length < 3 && $.type(concept) === 'boolean' )
1313//      {
1314//          remove = concept;
1315//          concept = false;
1316//      }
1317//       //////////////////////////////////////////////////////////////////////////
1318//       
1319//      if( !this.queue[type] || !this.queue['all'] )
1320//          return( false );
1321//     
1322//      if( !concept )
1323//      {
1324//          var obj = this.queue['all'][type];
1325//         
1326//          if( remove )
1327//          {
1328//              delete this.queue['all'][type];
1329//              delete this.queue[type];
1330//          }
1331//
1332//          this.store( ':queue', this.queue );
1333//          return( obj );
1334//      }
1335//
1336//      if( !this.queue[type][concept] )
1337//          return( false );
1338//     
1339//      if( !URI )
1340//      {
1341//          var obj = this.queue[type][concept];
1342//
1343//          if( remove )
1344//          {
1345//              var URIs = this.queue[type][concept];
1346//
1347//              for( var subURI in URIs )
1348//                   delete this.queue['all'][type][subURI];
1349//
1350//              delete this.queue[type][concept];
1351//          }
1352//
1353//          this.store( ':queue', this.queue );
1354//          return( obj );
1355//      }
1356//
1357// //   var newURI = URI ? this.URI( concept, URI ) : concept;
1358//     
1359//      var obj = this.queue['all'][type][/*new*/URI];
1360//   
1361//      if( remove )
1362//      {
1363//          delete this.queue['all'][type][/*new*/URI];
1364//          delete this.queue[type][concept][URI];
1365//      }
1366//
1367//      this.store( ':queue', this.queue );
1368//      return( obj );
1369//     },
1370   
1371           //TODO: definir a 'usage' desta função e refatora-la
1372//     set: function( concept, filter, data, oneSide ){
1373//
1374//      ///////////////////////////// normalize ////////////////////////////////
1375//      if( arguments.length == 2 )
1376//      {
1377//          data = filter;
1378//          filter = false;
1379//      }
1380//      if( $.type(data) === "boolean" )
1381//      {
1382//          oneSide = data;
1383//          data = filter;
1384//          filter = false;
1385//      }
1386//     
1387//      if( !concept || !data )
1388//          return( false );
1389//
1390//      var decoder = "", URI = false, bothSides = (typeof oneSide === "undefined");
1391//     
1392//      if( $.type(filter) === "string" )
1393//      {
1394//          URI = filter;
1395//          filter = false;
1396//      }
1397//
1398//      if( res = internalUrl.exec( concept ) )
1399//      {
1400//          //TODO: verificar se a decodificaçao deve ser feita em cada item do array
1401//          data = this.decode( concept, data );
1402//          concept = res[1];
1403//          decoder = res[2];
1404//      }
1405//      ///////////////////////////////////////////////////////////////////////////
1406//
1407//      if( bothSides || oneSide )
1408//          this.report( concept, URI, data, !bothSides );
1409//
1410//      if( bothSides || !oneSide )
1411//      {
1412//          if( URI )
1413//          {
1414//            var helper = {};
1415//            helper[URI] = data;
1416//            data = helper;
1417//          }
1418//
1419//          for( var URI in data )
1420//          {
1421//              var current = this.check( concept, URI ) || {};
1422//
1423//              data[URI] = this.merge( current, data[URI] );
1424//
1425//              this.store( concept, URI, data[URI] );
1426//          }
1427//
1428//      }
1429//
1430//      this.broadcast( concept, oneSide ? 'client' : 'server' );
1431//
1432//      return( true );
1433//     },
1434//     put: function( concept, URI, data, oneSide ){
1435//       
1436//       ///////////////////////////// normalize ////////////////////////////////
1437//      if( $.type(URI) !== "string" && arguments.length < 4 )
1438//      {
1439//          oneSide = data;
1440//          data = URI;
1441//          URI = false;
1442//      }
1443//       ////////////////////////////////////////////////////////////////////////
1444//       
1445//       ////////////////////////////// linkage /////////////////////////////////
1446//      var result = false, links = this.links( concept );
1447//
1448//      for( var link in links )
1449//      {
1450//          if( data[link] )
1451//          {
1452//              if( $.isArray( data[link] ) )
1453//              {
1454//                  data[link] = this.put( links[link], data[link].URI, data[link], oneSide );
1455//              }
1456//              else if( $.isObject( data[link] ) )
1457//              {
1458//                  $.each( data[link], function( i, el ){
1459//
1460//                        data[link][i] = this.put( links[link], el.URI, el, oneSide );
1461//
1462//                  });
1463//              }
1464//          }
1465//      }
1466//       //////////////////////////////////////////////////////////////////////////
1467//     
1468//      if( typeof data.URI === "undefined" )
1469//      {
1470//          URI = this.add( concept, data, oneSide );
1471//      }
1472//      else if( data.URI === false )
1473//      {
1474//          status = this.remove( concept, URI, oneSide );
1475//      }
1476//      else
1477//      {
1478//          status = this.set( concept, URI, data, oneSide );
1479//      }
1480//
1481//      if( URI && data.URI && URI !== data.URI )
1482//          this.move( concept, URI, data.URI );
1483//
1484//      return( data.URI || URI );
1485//
1486//     },
1487   
1488    //     add: function( concept, data, oneSide ){
1489//       
1490//       ///////////////////////////// normalize ////////////////////////////////
1491//      if( !concept || !data )
1492//          return( false );
1493//
1494//      if( res = internalUrl.exec( concept ) )
1495//      {
1496//          //TODO: verificar se a decodificaᅵᅵo deve ser feita em cada item do array
1497//          data = this.decode( concept, data );
1498//          concept = res[1];
1499//          decoder = res[2];
1500//      }
1501//
1502//      var bothSides = (typeof oneSide === "undefined"), uris = [];
1503//
1504//      if( notArray = $.type(data) !== "array" )
1505//          data = [ data ];
1506//       //////////////////////////////////////////////////////////////////////////
1507//
1508//      for( var i = 0; i < data.length; i++ )
1509//      {
1510//          var URI = uris[i] = this.generateURI( concept );
1511//
1512//          this.set( concept, URI, data[i], oneSide );
1513//      }
1514//
1515//      return( notArray ? uris[0] : uris );
1516//     },
1517//      put: function( concept, data ){
1518//
1519//      var decoder = "";
1520//
1521//      if( res = internalUrl.exec( concept ) )
1522//      {
1523//          data = this.decode( concept, data );
1524//          concept = res[1];
1525//          decoder = res[2];
1526//      }
1527//
1528//      var New = [], Update = [], uris = [];
1529//
1530//      if( notArray = $.type(data) !== "array" )
1531//          data = [ data ];
1532//     
1533//      for( var i = 0; i < data.length; i++ )
1534//      {
1535//          if( !data[i].URI )
1536//          {
1537//              uris[ uris.length ] = data[i].URI = this.create( concept, data[i] );
1538//              New[ New.length ] = data[i];
1539//              continue;
1540//          }
1541//
1542//          for( var key in data[i] )
1543//              if( klass = this.isReference( concept, key, data[i][key] ) )
1544//                    data[i][key] = this.put( klass + decoder, data[i][key] );
1545//
1546//          Update[ Update.length ] = this.update( concept, data[i].URI, data[i] );
1547//      }
1548//
1549//      this.report( concept, { "created": New, "updated": Update });
1550//
1551//      return( notArray ? uris[0] : uris );
1552//     },
1553//     merge: function( concept, current, data ){
1554//
1555//      current = current || {};
1556//
1557//      for( var key in data )
1558//          current[key] = (klass = this.isReference( concept, key, data[key] )) ?
1559//                         this.merge( klass, current[key], data[key] ) : data[key];
1560//
1561//      return( current );
1562//     },
1563//
1564//     isReference: function( concept, key, value ){
1565//
1566//       return( ($.type(value) === "object" ||
1567//             $.type(value) === "array" )? this.links[concept][key] : false );
1568//
1569//     },
1570//     
1571//     set: function( concept, data, URI, mergeable ){
1572//     
1573//      if( URI )
1574//      {
1575//          var res = this.get( concept, true ) || {};
1576//         
1577//          if( mergeable )
1578//              data = this.merge( res[URI] || {}, data );
1579//
1580//          res[URI] = data;
1581//
1582//          data = res;
1583//      }
1584//
1585//      return this.store( concept, data );
1586//     },   
1587//
1588//     create: function( concept, data ){
1589//
1590//       if( notArray = ($.type(data) !== "array") )
1591//          data = [ data ];
1592//
1593//       var uris = [];
1594//
1595//       for( var i = 0; i < data.length; i++ )
1596//       {
1597//        uris[ uris.length ] = data[i].URI = "javascript://" + (this.counter + i);
1598//
1599//        this.set( concept, data[i], data[i].URI );
1600//       }
1601// 
1602//       this.set( ":counter", (this.counter += data.length) );
1603//
1604//       return notArray ? uris[0] : uris;
1605//     },
1606//
1607//     update: function( concept, URI, data )
1608//     {
1609//      var target = this.check( concept, URI ) || {};
1610//
1611//      target = this.merge( concept, target, data );
1612//
1613//      if( target.URI !== URI )
1614//          this.remove( concept, URI );
1615//
1616//      this.set( concept, target, target.URI );
1617//
1618//      return( target );
1619//     },
1620//
1621//     remove: function( concept, URI ){
1622//
1623//      if( !URI )
1624//          return this.storage.del( concept );
1625//
1626//      var res = this.check( concept );
1627//
1628//      delete res[URI];
1629//     
1630//      this.set( concept, res );
1631//     },
1632//
1633//     del: function( concept, URI ){
1634//
1635//      this.remove( concept, URI );
1636//
1637//      this.report( concept, { "deleted": { 'URI': URI } });
1638//     },
1639//
1640//     report: function( concept, changes ){
1641//
1642//       this.broadcast( concept, changes.created, changes.updated, changes.deleted );
1643//
1644//      if( changes.created )
1645//          this.sync( concept, changes.created, 'create' );
1646//      if( changes.updated )
1647//          this.sync( concept, changes.updated, 'update' );
1648//      if( changes.deleted )
1649//          this.sync( concept, changes.deleted, 'delete' );
1650//
1651//     },
1652//
1653//
1654//    sync: function( concept, data, type ){
1655//
1656//      if( $.type(data) !== "array" )
1657//          data = [ data ];
1658//
1659//      $.each( data, function( i, el ){
1660//
1661//         DataLayer.send( concept, el, type );
1662//
1663//      });
1664//
1665//     },
1666//     
1667//     
1668//     
1669//     
1670//
1671//     request: function( concept, URI, filter ){
1672//
1673// //       if( startsDoubleDot.test(concept) )
1674// //     return( false );
1675//
1676//       filter = filter || {};
1677//
1678//       if( URI )
1679//      filter.URI = URI;
1680//
1681//       return this.send( concept, filter, "read", true );
1682//
1683//     },
1684//
1685//     send: function( concept, data, type, wait ){
1686//
1687//       switch( type )
1688//       {
1689//      case "create": type = "POST"; break;
1690//      case "update": type = "PUT"; break;
1691//      case "delete": type = "DELETE"; break;
1692//      case "read": type = "GET"; break;
1693//       }
1694//
1695//       var url = this.basePath + concept;
1696//
1697//       var result = [], notArray = false;
1698//
1699// //      alert( data.URI );
1700//
1701//       if( data.URI && data.URI.indexOf("javascript://") !== 0 )
1702//        url += "/" + data.URI;
1703//
1704//       var callback = function( dt, textStatus, jqXHR ){
1705//
1706//          if( notArray = (!$.isArray( dt )) )
1707//              dt = [ dt ];
1708//
1709//          $.each( dt, function( i, el ){
1710//
1711//              if( !el || !el.URI )
1712//                  return;
1713//
1714//              if( data.URI )
1715//                  el = DataLayer.update( concept, data.URI, el );
1716//              else
1717//                  DataLayer.set( concept, el, el.URI );
1718//
1719//              result[ result.length ] = el;
1720//              DataLayer.broadcast( concept );
1721//        });
1722//       };
1723//
1724//       $.ajax({
1725//            'async': ( !wait ),
1726//            'url': url,
1727//            'type': type,
1728//            'success': callback,
1729//            'dataType': 'json',
1730//            'data': data/*,
1731//            'processData': false*/
1732//        });
1733//
1734//       return( notArray ? result[0] || false : result );
1735//     },
1736   
1737   
1738    generateURI: function( concept ){
1739     
1740        return this.URI( concept, this.generateId( concept ), "javascript" );
1741
1742    },
1743   
1744
1745    broadcast: function( concept, status, diff ){
1746
1747        if( this.listeners[ concept ] )
1748            for( var i = 0;
1749                i < this.listeners[ concept ].length;
1750                this.listeners[ concept ][ i++ ]( status, diff ) );
1751    },
1752
1753    listen: function( concept, listener ){
1754
1755        this.register( "listeners", concept, listener );
1756
1757    },
1758
1759    codec: function( concept, namespace, codec ){
1760
1761        if( codec.encoder )
1762            this.encoder( concept, namespace, codec.encoder );
1763        if( codec.decoder )
1764            this.decoder( concept, namespace, codec.decoder );
1765        if( codec.criteria )
1766            this.register( "criterias", concept + ":" + namespace, codec.criteria );
1767
1768    },
1769
1770    encoder: function( concept, namespace, encoder ){
1771
1772        this.register( "encoders", concept + ":" + namespace, encoder );
1773
1774    },
1775
1776    encode: function( encoder, data, filter ){
1777
1778        if( this.encoders[ encoder ] )
1779            for( var i = 0;
1780                i < this.encoders[ encoder ].length;
1781                data = this.encoders[ encoder ][ i++ ]( data, filter ) );
1782
1783        return( data );
1784    },
1785
1786    decoder: function( concept, namespace, decoder ){
1787
1788        this.register( "decoders", concept + ":" + namespace, decoder );
1789
1790    },
1791
1792    decode: function( decoder, data ){
1793
1794        if( this.decoders[ decoder ] )
1795            for( var i = 0;
1796                i < this.decoders[ decoder ].length;
1797                data = this.decoders[ decoder ][ i++ ]( data ) );
1798
1799        return( data );
1800    },
1801
1802    criteria: function( codec, filter ){
1803
1804        if( this.criterias[ codec ] )
1805            for( var i = 0;
1806                i < this.criterias[ codec ].length;
1807                filter = this.criterias[ codec ][ i++ ]( filter ) );
1808
1809        return( filter );
1810
1811    },
1812
1813    register: function( kind, concept, deployable ){
1814
1815      if( arguments.length < 3 )
1816      {
1817          deployable = concept;
1818          concept = kind;
1819          kind = 'global';
1820      }
1821
1822      if( !this[ kind ][ concept ] )
1823            this[ kind ][ concept ] = [];
1824
1825        this[ kind ][ concept ][ this[ kind ][ concept ].length ] = deployable;
1826
1827    },
1828   
1829    start: function(){
1830
1831        var timer = function(){
1832
1833              setTimeout( timer, 1 );
1834
1835              var now = parseInt( $.now() / 1000 );
1836
1837              var tasks = DataLayer.tasks[ now ];
1838
1839              if( !tasks ) return;
1840
1841              for( var i = 0; i < tasks.length; i++ )
1842              {
1843                  var result = tasks[i].task( now );
1844
1845                  if( tasks[i].factor )
1846                  DataLayer.schedule( tasks[i].task, tasks[i].factor );
1847              }
1848     
1849              delete DataLayer.tasks[ now ];
1850        };
1851
1852        setTimeout( timer, 1 );
1853    },
1854   
1855    task: function( timestamp, task, factor )
1856    {
1857        if( !this.tasks[ timestamp ] )
1858            this.tasks[ timestamp ] = [];
1859
1860        this.tasks[ timestamp ][ this.tasks[ timestamp ].length ] = { task: task, factor: factor || false };
1861    },
1862
1863    schedule: function( task, time ){
1864
1865        time = time || 1;
1866       
1867        var index = parseInt( $.now() / 1000 ) + time;
1868
1869        this.task( index, task, time );
1870    },
1871   
1872    poll: function( concept, time ){
1873     
1874      this.schedule( function( now ){
1875 
1876          DataLayer.commit( concept );
1877
1878        }, time || 5 );
1879    },
1880   
1881    init: function(){
1882     
1883        this.counter = parseInt( this.get( ":counter", false ) ) || 0;
1884
1885        if( !this.dispatchPath )
1886            this.dispatchPath = "../../";
1887
1888        if( !this.templatePath )
1889            this.templatePath = "";
1890
1891        if( !this.basePath )
1892            this.basePath = this.dispatchPath + "REST.php?q=";
1893
1894        this.schedule( function( now ){
1895
1896            DataLayer.flush();
1897
1898        });
1899
1900        this.start();
1901    }
1902}
1903
1904// the re-usable constructor function used by clone().
1905function Clone() {}
1906
1907//Recursion Helper
1908function RecursionHelper(){ this.clear(); };
1909
1910RecursionHelper.prototype = {
1911 
1912        constructor: RecursionHelper,
1913
1914        // copiedObjects keeps track of objects already copied by this
1915        // deepCopy operation, so we can correctly handle cyclic references.
1916        copiedObjects: [],
1917
1918        depth: 0,
1919
1920        maxDepth: 256,
1921
1922        //reset the recursion helper cache
1923        clear: function(){
1924                this.copiedObjects = [];
1925                this.depth = 0;
1926        },
1927
1928        // add an object to the cache.  No attempt is made to filter duplicates;
1929        // we always check getCachedResult() before calling it.
1930        cacheResult: function(source, result) {
1931                this.copiedObjects.push([source, result]);
1932        },
1933
1934        // Returns the cached copy of a given object, or undefined if it's an
1935        // object we haven't seen before.
1936        getCachedResult: function(source) {
1937
1938                for ( var i=0; i<this.copiedObjects.length; i++ ) {
1939                        if ( this.copiedObjects[i][0] === source ) {
1940                                return this.copiedObjects[i][1];
1941                        }
1942                }
1943
1944                return undefined;
1945        }
1946};
1947
1948// Generic Object copier
1949// the ultimate fallback DeepCopier, which tries to handle the generic case.  This
1950// should work for base Objects and many user-defined classes.
1951DataLayer.registerComparator({
1952        can: function(source) { return true; },
1953
1954        create: function(source) {
1955                if ( source instanceof source.constructor ) {
1956                        return DataLayer.clone(source.constructor.prototype);
1957                } else {
1958                        return {};
1959                }
1960        },
1961
1962        populate: function(deepCopy, source, result) {
1963                for ( var key in source ) {
1964                        if ( source.hasOwnProperty(key) ) {
1965                                result[key] = deepCopy(source[key], result[key]);
1966                        }
1967                }
1968                return result;
1969        }
1970});
1971
1972// Array copier
1973DataLayer.registerComparator({
1974        can: function(source) {
1975                return ( source instanceof Array );
1976        },
1977
1978        create: function(source) {
1979                return new source.constructor();
1980        },
1981
1982        populate: function(deepCopy, source, result) {
1983                for ( var i=0; i<source.length; i++) {
1984                        result.push( deepCopy(source[i], result[i]) );
1985                }
1986                result =  DataLayer.unique( result )
1987                return result;
1988        }
1989});
1990
1991// Date copier
1992DataLayer.registerComparator({
1993        can: function(source) {
1994                return ( source instanceof Date );
1995        },
1996
1997        create: function(source) {
1998                return new Date(source);
1999        }
2000});
2001
2002// HTML DOM Node copier
2003DataLayer.registerComparator({
2004
2005        // function to detect Nodes.  In particular, we're looking
2006        // for the cloneNode method.  The global document is also defined to
2007        // be a Node, but is a special case in many ways.
2008        can: function(source) {
2009         
2010          if ( window.Node ) {
2011                  return source instanceof Node;
2012          } else {
2013                  // the document is a special Node and doesn't have many of
2014                  // the common properties so we use an identity check instead.
2015                  if ( source === document ) return true;
2016                  return (
2017                          typeof source.nodeType === 'number' &&
2018                          source.attributes &&
2019                          source.childNodes &&
2020                          source.cloneNode
2021                  );
2022          }
2023      },
2024
2025      create: function(source) {
2026              // there can only be one (document).
2027              if ( source === document ) return document;
2028
2029              // start with a shallow copy.  We'll handle the deep copy of
2030              // its children ourselves.
2031              return source.cloneNode(false);
2032      },
2033     
2034      diff: function(base, source){
2035       
2036      },
2037
2038      populate: function(deepCopy, source, result) {
2039              // we're not copying the global document, so don't have to populate it either.
2040              if ( source === document ) return document;
2041
2042              // if this Node has children, deep copy them one-by-one.
2043              if ( source.childNodes && source.childNodes.length ) {
2044                      for ( var i=0; i<source.childNodes.length; i++ ) {
2045                              var childCopy = deepCopy(source.childNodes[i], result.childNodes[i] || false );
2046                              result.appendChild(childCopy);
2047                      }
2048              }
2049                return result;
2050      }
2051});
2052
2053DataLayer.init();
2054
2055// setTimeout(function(){
2056// 
2057//     
2058//
2059// }, 1000 );
2060
2061// var DataLayer = {
2062//
2063//     get: function( concept, filter ){
2064//
2065//      var data = this.storage.get( concept );
2066//
2067//      if( !filter )
2068//          return( data );
2069//
2070//      return this.filter( data, filter );
2071//     },
2072//     
2073//     filter:function( data, filter ){
2074//       
2075//      if( filter.charAt )
2076//          filter = { URI: filter };
2077//     
2078//      var filtered = [];
2079//
2080//      $.each(data, function(i, obj){
2081//       
2082//          for( var attr in filter )
2083//              if( filter[attr] !== obj[attr] )
2084//                  return( true );
2085//
2086//          filtered[i] = obj;
2087//      });
2088//
2089//      return( filtered );
2090//     },
2091//
2092//     find: function( concept, filter, callback ){
2093//
2094//      var data = this.get( concept, filter );
2095//
2096//      if( data )
2097//          return callback( data );
2098//
2099//      //TODO: register callback like a weak listener
2100//
2101// //   $.ajax({
2102// //         type: 'GET',
2103// //         data: $.param( filter ),
2104// //         success: callback, 
2105// //         url: BASE_PATH + filter.URI || concept
2106// //   });
2107//      this.report( concept, filter );
2108//     },
2109//
2110//     put: function( concept, data, filter ){
2111//
2112//      var beforeDiff = this.store( concept, $.extend({},data) );
2113//
2114//      this.report( concept, data, filter, beforeDiff );
2115//     },
2116//     
2117//     
2118//     /*var data = {
2119//                      startTime: $.now(),
2120//                      endTime: $.now() + 1800000,
2121//                      summary: "meu querido evento",
2122//                      description: "desc do evento",
2123//                      location: "prognus software livre",
2124//                      class: 1,
2125//                      calendar: 1,
2126//                      category: 1,
2127//                      participants: [ {
2128//                                         user: { isExternal: true, mail: "user7@prognus.org", name: "user7" }
2129//                                    },{
2130//                                         user: "1003"
2131//                                    } ]
2132//
2133//                };*/
2134//     
2135//
2136//     merge:function( data, target ){
2137//       
2138//      var diff = { New: {}, Update:{}, Delete: {} };
2139//       
2140//      for( var key in data )
2141//      {
2142//          if( !target[ key ] )
2143//              diff.New[ key ] = target[ key ] = data[ key ];
2144//
2145//         
2146//       
2147//      }
2148//       
2149//     }
2150//
2151//     store: function( concept, data, filter ){
2152//
2153//      if( !data.spline )
2154//          data = [ data ];
2155//
2156//      var target = this.storage.get( concept );
2157//     
2158//      var diff = { New: {}, Update:{}, Delete: {} };
2159//
2160//      for( var i = 0; i < data.length; i++ )
2161//      {
2162//          if( data[i].URI && target[ data[i].URI ] )
2163//          {
2164//              diff.Update[ data[i].URI ] = this.merge( target[ data[i].URI ], data[i] );
2165//          }
2166//          else
2167//          {
2168//              diff.New[] = data[i];
2169//          }
2170//      }
2171//
2172//     
2173//
2174//      this.broadcast( concept, data );
2175//
2176//      if( filter )
2177//          target = this.filter( target, filter );
2178//
2179//      if( target )
2180//          data = $.extend( target, data );
2181//
2182//      this.storage.set( concept, data );
2183//
2184// //   return;
2185//     },
2186//     
2187//     set: function( concept, data, filter ){
2188//
2189//       
2190//
2191//     },
2192//
2193//     post: function( concept, data, filter, isNew ){
2194//
2195//      var callback = function(  ){ DataLayer.store( concept, data, filter ) };
2196//
2197//      //TODO: register callback like a weak listener
2198//
2199//      this.report( concept, data, filter, isNew );
2200//     },
2201//     
2202//     report: function( concept, filter, postData, isNew ){
2203//       
2204//      $.ajax({
2205//              type: postData ? isNew ? 'POST' : 'PUT' : 'GET',
2206//              data: postData || $.param( filter ),
2207//              success: function( data ){ DataLayer.broadcast( concept ) },
2208//              url: BASE_PATH + filter.URI || concept
2209//        });
2210//     },
2211//     
2212//     del:function( concept, filter ){
2213//
2214//       
2215//
2216//     }
2217//     
2218//     broadcast: function( concept, data ){
2219//
2220//     
2221//
2222//     },
2223//
2224//     pool: function(){
2225//       
2226//     },
2227//
2228//     refresh: function(){
2229//       
2230//     }
2231// };
2232
2233//
2234// DataLayer = {
2235//   
2236//     get: function( concept, filter ){
2237//
2238//      var data = this.storage.get( concept );
2239//
2240//      if( !filter )
2241//          return( data );
2242//
2243//      if( filter.charAt )
2244//          filter = { URI: filter };
2245//     
2246//      var filtered = [];
2247//
2248//      $.each(data, function(i, obj){
2249//       
2250//          for( var attr in filter )
2251//              if( filter[attr] !== obj[attr] )
2252//                  return( true );
2253//
2254//          filtered[i] = obj;
2255//      });
2256//
2257//      return( filtered );
2258//     },
2259//
2260//     find: function( concept, filter, callback ){
2261//
2262//      var data = this.get( concept, filter );
2263//
2264//      if( data )
2265//          return callback( data );
2266//
2267//       $.ajax({
2268//            type: 'GET',
2269//            data: $.param( filter ),
2270//            success: callback, 
2271//            url: filter.URI || concept
2272//      });
2273//     },
2274//
2275//     put: function( concept, data, filter ){
2276//
2277//      var target = this.get( concept, filter );
2278//
2279//      if( target )
2280//          data = $.extend( target, data );
2281//       
2282//      this.storage.set( concept, data );
2283//     
2284//      //diff
2285//     },
2286//     
2287//     post: function( concept, data, filter ){
2288//
2289//     
2290//
2291//     },
2292//     
2293//     pool: function(){
2294//       
2295//     },
2296//     
2297//     queue: function(){
2298//       
2299//     },
2300//     
2301//     dequeue: function(){
2302//       
2303//     },
2304//     
2305//     refresh: function(){
2306//       
2307//     }
2308// }
2309
2310// var DataLayer = {
2311 
2312//       cache: {},
2313 
2314//       get: function( concept, location ){
2315       
2316           /* if( location )
2317            {*/
2318//              var schema = $.data( this.cache, concept + ':schema' );
2319//              var uri = [];
2320//
2321//              $.each( schema, function( i, addrs ){
2322//                    uri[ uri.length ] = location[addrs];
2323//              });
2324
2325                /*var filter = [], result = false;
2326
2327                while( !(result = $.data( this.cache, uri.join( '.' ) )) || !(uri = uri.join('.')) )
2328                  filter[ filter.length ] = uri.pop();
2329 
2330                if( !filter.length )
2331                {
2332                    var indexes = $.data( this.cache, uri + ':indexes' );
2333
2334                    if( indexes )
2335                        Array.prototype.concat.apply( result, indexes );
2336                   
2337                    return( result );
2338                }
2339
2340                for( var i = 0; i < result.length; i++ )
2341                {
2342                   
2343                }
2344
2345                if( result.length )
2346                    return( result );
2347            }*/
2348
2349//          var data = $.data( this.cache, concept );
2350
2351//          if( !data )
2352//              $.ajax( );
2353
2354//          return( data );
2355//       },
2356     
2357//       data: function(){
2358//
2359//       
2360//
2361//       }
2362//       
2363//       search: function( concept, filter ){
2364//
2365//        var schema = $.data( this.cache, concept + ':schema' );
2366//        var uri = [];
2367//
2368//        $.each( schema, function( i, addrs ){
2369//              uri[ uri.length ] = location[addrs];
2370//        });
2371//       }
2372//       put: function( concept, data, location ){
2373
2374//          if( location )
2375//          {
2376//              var schema = $.data( this.cache, concept + ':schema');
2377//              var uri = [];
2378
2379//              $.each( schema, function( i, addrs ){
2380//                    uri[ uri.length ] = location[addrs];
2381//              });
2382
2383//              var result = false, filter = [];
2384
2385//              while( !(result = $.data( this.cache, uri.join('.')) )
2386//                  filter[ filter.length ] = uri.pop();
2387
2388//              $.data( this.cache, '
2389
2390//          }
2391
2392//              var model = this.storage.get( concept );
2393//
2394//              $.each( model, function( i, o ){
2395//                  $.each( location, function( ii, attr ){
2396//                       if( o[ii] === attr )
2397//                          return( false );
2398//                  });
2399//              });
2400
2401//          return $.data( this.cache, concept, data );
2402
2403//       },
2404//       del: function( concept, location ){
2405//
2406//          if( location )
2407//          {
2408//              var schema = $.data( this.cache, 'concepts', concept );
2409//              var uri = [];
2410//
2411//              $.each( schema, function( i, addrs ){
2412//                    uri[ uri.length ] = location[addrs];
2413//              });
2414//
2415//              concept = uri.join( '.' );
2416
2417//              var model = this.storage.get( concept );
2418//
2419//              $.each( model, function( i, o ){
2420//                  $.each( location, function( ii, attr ){
2421//                       if( o[ii] === attr )
2422//                          return( false );
2423//                  });
2424//              });
2425//          }
2426//         
2427//     
2428//          $.removeData( this.cache, concept );
2429//       }
2430// }
Note: See TracBrowser for help on using the repository browser.